1 /*
2    Virtual File System: interface functions
3 
4    Copyright (C) 2011-2021
5    Free Software Foundation, Inc.
6 
7    Written by:
8    Slava Zanko <slavazanko@gmail.com>, 2011, 2013
9 
10    This file is part of the Midnight Commander.
11 
12    The Midnight Commander is free software: you can redistribute it
13    and/or modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation, either version 3 of the License,
15    or (at your option) any later version.
16 
17    The Midnight Commander is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21 
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 /**
27  * \file
28  * \brief Source: Virtual File System: path handlers
29  * \author Slava Zanko
30  * \date 2011
31  */
32 
33 
34 #include <config.h>
35 
36 #include <stdio.h>
37 #include <stdlib.h>             /* For atol() */
38 #include <stdarg.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <sys/types.h>
42 #include <signal.h>
43 #include <ctype.h>              /* is_digit() */
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <dirent.h>
47 #include <pwd.h>
48 #include <grp.h>
49 
50 #include "lib/global.h"
51 
52 #include "lib/widget.h"         /* message() */
53 #include "lib/strutil.h"        /* str_crt_conv_from() */
54 #include "lib/util.h"
55 
56 #include "vfs.h"
57 #include "utilvfs.h"
58 #include "path.h"
59 #include "gc.h"
60 #include "xdirentry.h"
61 
62 /* TODO: move it to separate private .h */
63 extern GString *vfs_str_buffer;
64 extern vfs_class *current_vfs;
65 extern struct vfs_dirent *mc_readdir_result;
66 
67 /*** global variables ****************************************************************************/
68 
69 /*** file scope macro definitions ****************************************************************/
70 
71 /*** file scope type declarations ****************************************************************/
72 
73 /*** file scope variables ************************************************************************/
74 
75 /*** file scope functions ************************************************************************/
76 /* --------------------------------------------------------------------------------------------- */
77 
78 static vfs_path_t *
mc_def_getlocalcopy(const vfs_path_t * filename_vpath)79 mc_def_getlocalcopy (const vfs_path_t * filename_vpath)
80 {
81     vfs_path_t *tmp_vpath = NULL;
82     int fdin, fdout = -1;
83     ssize_t i;
84     char buffer[BUF_1K * 8];
85     struct stat mystat;
86 
87     fdin = mc_open (filename_vpath, O_RDONLY | O_LINEAR);
88     if (fdin == -1)
89         goto fail;
90 
91     fdout = vfs_mkstemps (&tmp_vpath, "vfs", vfs_path_get_last_path_str (filename_vpath));
92     if (fdout == -1)
93         goto fail;
94 
95     while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0)
96     {
97         if (write (fdout, buffer, i) != i)
98             goto fail;
99     }
100     if (i == -1)
101         goto fail;
102     i = mc_close (fdin);
103     fdin = -1;
104     if (i == -1)
105         goto fail;
106 
107     i = close (fdout);
108     fdout = -1;
109     if (i == -1)
110         goto fail;
111 
112     if (mc_stat (filename_vpath, &mystat) != -1)
113         mc_chmod (tmp_vpath, mystat.st_mode);
114 
115     return tmp_vpath;
116 
117   fail:
118     vfs_path_free (tmp_vpath, TRUE);
119     if (fdout != -1)
120         close (fdout);
121     if (fdin != -1)
122         mc_close (fdin);
123     return NULL;
124 }
125 
126 /* --------------------------------------------------------------------------------------------- */
127 
128 static int
mc_def_ungetlocalcopy(const vfs_path_t * filename_vpath,const vfs_path_t * local_vpath,gboolean has_changed)129 mc_def_ungetlocalcopy (const vfs_path_t * filename_vpath,
130                        const vfs_path_t * local_vpath, gboolean has_changed)
131 {
132     int fdin = -1, fdout = -1;
133     const char *local;
134 
135     local = vfs_path_get_last_path_str (local_vpath);
136 
137     if (has_changed)
138     {
139         char buffer[BUF_1K * 8];
140         ssize_t i;
141 
142         if (vfs_path_get_last_path_vfs (filename_vpath)->write == NULL)
143             goto failed;
144 
145         fdin = open (local, O_RDONLY);
146         if (fdin == -1)
147             goto failed;
148         fdout = mc_open (filename_vpath, O_WRONLY | O_TRUNC);
149         if (fdout == -1)
150             goto failed;
151         while ((i = read (fdin, buffer, sizeof (buffer))) > 0)
152             if (mc_write (fdout, buffer, (size_t) i) != i)
153                 goto failed;
154         if (i == -1)
155             goto failed;
156 
157         if (close (fdin) == -1)
158         {
159             fdin = -1;
160             goto failed;
161         }
162         fdin = -1;
163         if (mc_close (fdout) == -1)
164         {
165             fdout = -1;
166             goto failed;
167         }
168     }
169     unlink (local);
170     return 0;
171 
172   failed:
173     message (D_ERROR, _("Changes to file lost"), "%s", vfs_path_get_last_path_str (filename_vpath));
174     if (fdout != -1)
175         mc_close (fdout);
176     if (fdin != -1)
177         close (fdin);
178     unlink (local);
179     return (-1);
180 }
181 
182 /* --------------------------------------------------------------------------------------------- */
183 /*** public functions ****************************************************************************/
184 /* --------------------------------------------------------------------------------------------- */
185 
186 int
mc_open(const vfs_path_t * vpath,int flags,...)187 mc_open (const vfs_path_t * vpath, int flags, ...)
188 {
189     int result = -1;
190     mode_t mode = 0;
191     const vfs_path_element_t *path_element;
192 
193     if (vpath == NULL)
194         return (-1);
195 
196     /* Get the mode flag */
197     if ((flags & O_CREAT) != 0)
198     {
199         va_list ap;
200 
201         va_start (ap, flags);
202         /* We have to use PROMOTED_MODE_T instead of mode_t. Doing 'va_arg (ap, mode_t)'
203          * fails on systems where 'mode_t' is smaller than 'int' because of C's "default
204          * argument promotions". */
205         mode = va_arg (ap, PROMOTED_MODE_T);
206         va_end (ap);
207     }
208 
209     path_element = vfs_path_get_by_index (vpath, -1);
210     if (vfs_path_element_valid (path_element) && path_element->class->open != NULL)
211     {
212         void *info;
213 
214         /* open must be supported */
215         info = path_element->class->open (vpath, flags, mode);
216         if (info == NULL)
217             errno = vfs_ferrno (path_element->class);
218         else
219             result = vfs_new_handle (path_element->class, info);
220     }
221     else
222         errno = -EOPNOTSUPP;
223 
224     return result;
225 }
226 
227 /* --------------------------------------------------------------------------------------------- */
228 
229 /* *INDENT-OFF* */
230 
231 #define MC_NAMEOP(name, inarg, callarg) \
232 int mc_##name inarg \
233 { \
234     int result; \
235     const vfs_path_element_t *path_element; \
236 \
237     if (vpath == NULL) \
238         return (-1); \
239 \
240     path_element = vfs_path_get_by_index (vpath, -1); \
241     if (!vfs_path_element_valid (path_element)) \
242         return (-1); \
243 \
244     result = path_element->class->name != NULL ? path_element->class->name callarg : -1; \
245     if (result == -1) \
246         errno = path_element->class->name != NULL ? vfs_ferrno (path_element->class) : E_NOTSUPP; \
247     return result; \
248 }
249 
250 MC_NAMEOP (chmod, (const vfs_path_t *vpath, mode_t mode), (vpath, mode))
251 MC_NAMEOP (chown, (const vfs_path_t *vpath, uid_t owner, gid_t group), (vpath, owner, group))
252 MC_NAMEOP (utime, (const vfs_path_t *vpath, mc_timesbuf_t * times), (vpath, times))
253 MC_NAMEOP (readlink, (const vfs_path_t *vpath, char *buf, size_t bufsiz), (vpath, buf, bufsiz))
254 MC_NAMEOP (unlink, (const vfs_path_t *vpath), (vpath))
255 MC_NAMEOP (mkdir, (const vfs_path_t *vpath, mode_t mode), (vpath, mode))
256 MC_NAMEOP (rmdir, (const vfs_path_t *vpath), (vpath))
257 MC_NAMEOP (mknod, (const vfs_path_t *vpath, mode_t mode, dev_t dev), (vpath, mode, dev))
258 
259 /* *INDENT-ON* */
260 
261 /* --------------------------------------------------------------------------------------------- */
262 
263 int
mc_symlink(const vfs_path_t * vpath1,const vfs_path_t * vpath2)264 mc_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
265 {
266     int result = -1;
267 
268     if (vpath1 != NULL && vpath2 != NULL)
269     {
270         const vfs_path_element_t *path_element;
271 
272         path_element = vfs_path_get_by_index (vpath2, -1);
273         if (vfs_path_element_valid (path_element))
274         {
275             result =
276                 path_element->class->symlink != NULL ?
277                 path_element->class->symlink (vpath1, vpath2) : -1;
278 
279             if (result == -1)
280                 errno =
281                     path_element->class->symlink != NULL ?
282                     vfs_ferrno (path_element->class) : E_NOTSUPP;
283         }
284     }
285     return result;
286 }
287 
288 /* --------------------------------------------------------------------------------------------- */
289 
290 /* *INDENT-OFF* */
291 
292 #define MC_HANDLEOP(name) \
293 ssize_t mc_##name (int handle, C void *buf, size_t count) \
294 { \
295     struct vfs_class *vfs; \
296     void *fsinfo = NULL; \
297     int result; \
298 \
299     if (handle == -1) \
300         return (-1); \
301 \
302     vfs = vfs_class_find_by_handle (handle, &fsinfo); \
303     if (vfs == NULL) \
304         return (-1); \
305 \
306     result = vfs->name != NULL ? vfs->name (fsinfo, buf, count) : -1; \
307     if (result == -1) \
308         errno = vfs->name != NULL ? vfs_ferrno (vfs) : E_NOTSUPP; \
309     return result; \
310 }
311 
312 #define C
313 MC_HANDLEOP (read)
314 #undef C
315 #define C const
MC_HANDLEOP(write)316 MC_HANDLEOP (write)
317 #undef C
318 
319 /* --------------------------------------------------------------------------------------------- */
320 
321 #define MC_RENAMEOP(name) \
322 int mc_##name (const vfs_path_t *vpath1, const vfs_path_t *vpath2) \
323 { \
324     int result; \
325     const vfs_path_element_t *path_element1; \
326     const vfs_path_element_t *path_element2; \
327 \
328     if (vpath1 == NULL || vpath2 == NULL) \
329         return (-1); \
330 \
331     path_element1 = vfs_path_get_by_index (vpath1, (-1)); \
332     path_element2 = vfs_path_get_by_index (vpath2, (-1)); \
333 \
334     if (!vfs_path_element_valid (path_element1) || !vfs_path_element_valid (path_element2) || \
335         path_element1->class != path_element2->class) \
336     { \
337         errno = EXDEV; \
338         return (-1); \
339     } \
340 \
341     result = path_element1->class->name != NULL \
342         ? path_element1->class->name (vpath1, vpath2) : -1; \
343     if (result == -1) \
344         errno = path_element1->class->name != NULL ? vfs_ferrno (path_element1->class) : E_NOTSUPP; \
345     return result; \
346 }
347 
348 MC_RENAMEOP (link)
349 MC_RENAMEOP (rename)
350 
351 /* *INDENT-ON* */
352 
353 /* --------------------------------------------------------------------------------------------- */
354 
355 int
356 mc_ctl (int handle, int ctlop, void *arg)
357 {
358     struct vfs_class *vfs;
359     void *fsinfo = NULL;
360 
361     vfs = vfs_class_find_by_handle (handle, &fsinfo);
362 
363     return (vfs == NULL || vfs->ctl == NULL) ? 0 : vfs->ctl (fsinfo, ctlop, arg);
364 }
365 
366 /* --------------------------------------------------------------------------------------------- */
367 
368 int
mc_setctl(const vfs_path_t * vpath,int ctlop,void * arg)369 mc_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
370 {
371     int result = -1;
372     const vfs_path_element_t *path_element;
373 
374     if (vpath == NULL)
375         vfs_die ("You don't want to pass NULL to mc_setctl.");
376 
377     path_element = vfs_path_get_by_index (vpath, -1);
378     if (vfs_path_element_valid (path_element))
379         result =
380             path_element->class->setctl != NULL ? path_element->class->setctl (vpath,
381                                                                                ctlop, arg) : 0;
382 
383     return result;
384 }
385 
386 /* --------------------------------------------------------------------------------------------- */
387 
388 int
mc_close(int handle)389 mc_close (int handle)
390 {
391     struct vfs_class *vfs;
392     void *fsinfo = NULL;
393     int result;
394 
395     if (handle == -1)
396         return (-1);
397 
398     vfs = vfs_class_find_by_handle (handle, &fsinfo);
399     if (vfs == NULL || fsinfo == NULL)
400         return (-1);
401 
402     if (handle < 3)
403         return close (handle);
404 
405     if (vfs->close == NULL)
406         vfs_die ("VFS must support close.\n");
407     result = vfs->close (fsinfo);
408     vfs_free_handle (handle);
409     if (result == -1)
410         errno = vfs_ferrno (vfs);
411 
412     return result;
413 }
414 
415 /* --------------------------------------------------------------------------------------------- */
416 
417 DIR *
mc_opendir(const vfs_path_t * vpath)418 mc_opendir (const vfs_path_t * vpath)
419 {
420     int handle, *handlep;
421     void *info;
422     vfs_path_element_t *path_element;
423 
424     if (vpath == NULL)
425         return NULL;
426 
427     path_element = (vfs_path_element_t *) vfs_path_get_by_index (vpath, -1);
428     if (!vfs_path_element_valid (path_element))
429     {
430         errno = E_NOTSUPP;
431         return NULL;
432     }
433 
434     info = path_element->class->opendir ? path_element->class->opendir (vpath) : NULL;
435     if (info == NULL)
436     {
437         errno = path_element->class->opendir ? vfs_ferrno (path_element->class) : E_NOTSUPP;
438         return NULL;
439     }
440 
441     path_element->dir.info = info;
442 
443 #ifdef HAVE_CHARSET
444     path_element->dir.converter = (path_element->encoding != NULL) ?
445         str_crt_conv_from (path_element->encoding) : str_cnv_from_term;
446     if (path_element->dir.converter == INVALID_CONV)
447         path_element->dir.converter = str_cnv_from_term;
448 #endif
449 
450     handle = vfs_new_handle (path_element->class, vfs_path_element_clone (path_element));
451 
452     handlep = g_new (int, 1);
453     *handlep = handle;
454     return (DIR *) handlep;
455 }
456 
457 /* --------------------------------------------------------------------------------------------- */
458 
459 struct vfs_dirent *
mc_readdir(DIR * dirp)460 mc_readdir (DIR * dirp)
461 {
462     int handle;
463     struct vfs_class *vfs;
464     void *fsinfo = NULL;
465     struct vfs_dirent *entry = NULL;
466     vfs_path_element_t *vfs_path_element;
467 
468     if (dirp == NULL)
469     {
470         errno = EFAULT;
471         return NULL;
472     }
473 
474     handle = *(int *) dirp;
475 
476     vfs = vfs_class_find_by_handle (handle, &fsinfo);
477     if (vfs == NULL || fsinfo == NULL)
478         return NULL;
479 
480     vfs_path_element = (vfs_path_element_t *) fsinfo;
481     if (vfs->readdir != NULL)
482     {
483         entry = vfs->readdir (vfs_path_element->dir.info);
484         if (entry == NULL)
485             return NULL;
486 
487         g_string_set_size (vfs_str_buffer, 0);
488 #ifdef HAVE_CHARSET
489         str_vfs_convert_from (vfs_path_element->dir.converter, entry->d_name, vfs_str_buffer);
490 #else
491         g_string_assign (vfs_str_buffer, entry->d_name);
492 #endif
493         vfs_dirent_assign (mc_readdir_result, vfs_str_buffer->str, entry->d_ino);
494         vfs_dirent_free (entry);
495     }
496     if (entry == NULL)
497         errno = vfs->readdir ? vfs_ferrno (vfs) : E_NOTSUPP;
498     return (entry != NULL) ? mc_readdir_result : NULL;
499 }
500 
501 /* --------------------------------------------------------------------------------------------- */
502 
503 int
mc_closedir(DIR * dirp)504 mc_closedir (DIR * dirp)
505 {
506     int handle;
507     struct vfs_class *vfs;
508     void *fsinfo = NULL;
509     int result = -1;
510 
511     if (dirp == NULL)
512         return result;
513 
514     handle = *(int *) dirp;
515 
516     vfs = vfs_class_find_by_handle (handle, &fsinfo);
517     if (vfs != NULL && fsinfo != NULL)
518     {
519         vfs_path_element_t *vfs_path_element = (vfs_path_element_t *) fsinfo;
520 
521 #ifdef HAVE_CHARSET
522         if (vfs_path_element->dir.converter != str_cnv_from_term)
523         {
524             str_close_conv (vfs_path_element->dir.converter);
525             vfs_path_element->dir.converter = INVALID_CONV;
526         }
527 #endif
528 
529         result = vfs->closedir ? (*vfs->closedir) (vfs_path_element->dir.info) : -1;
530         vfs_free_handle (handle);
531         vfs_path_element_free (vfs_path_element);
532     }
533     g_free (dirp);
534     return result;
535 }
536 
537 /* --------------------------------------------------------------------------------------------- */
538 
539 int
mc_stat(const vfs_path_t * vpath,struct stat * buf)540 mc_stat (const vfs_path_t * vpath, struct stat *buf)
541 {
542     int result = -1;
543     const vfs_path_element_t *path_element;
544 
545     if (vpath == NULL)
546         return (-1);
547 
548     path_element = vfs_path_get_by_index (vpath, -1);
549     if (vfs_path_element_valid (path_element))
550     {
551         result = path_element->class->stat ? path_element->class->stat (vpath, buf) : -1;
552         if (result == -1)
553             errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
554     }
555 
556     return result;
557 }
558 
559 /* --------------------------------------------------------------------------------------------- */
560 
561 int
mc_lstat(const vfs_path_t * vpath,struct stat * buf)562 mc_lstat (const vfs_path_t * vpath, struct stat *buf)
563 {
564     int result = -1;
565     const vfs_path_element_t *path_element;
566 
567     if (vpath == NULL)
568         return (-1);
569 
570     path_element = vfs_path_get_by_index (vpath, -1);
571     if (vfs_path_element_valid (path_element))
572     {
573         result = path_element->class->lstat ? path_element->class->lstat (vpath, buf) : -1;
574         if (result == -1)
575             errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
576     }
577 
578     return result;
579 }
580 
581 /* --------------------------------------------------------------------------------------------- */
582 
583 int
mc_fstat(int handle,struct stat * buf)584 mc_fstat (int handle, struct stat *buf)
585 {
586     struct vfs_class *vfs;
587     void *fsinfo = NULL;
588     int result;
589 
590     if (handle == -1)
591         return (-1);
592 
593     vfs = vfs_class_find_by_handle (handle, &fsinfo);
594     if (vfs == NULL)
595         return (-1);
596 
597     result = vfs->fstat ? vfs->fstat (fsinfo, buf) : -1;
598     if (result == -1)
599         errno = vfs->fstat ? vfs_ferrno (vfs) : E_NOTSUPP;
600     return result;
601 }
602 
603 /* --------------------------------------------------------------------------------------------- */
604 
605 vfs_path_t *
mc_getlocalcopy(const vfs_path_t * pathname_vpath)606 mc_getlocalcopy (const vfs_path_t * pathname_vpath)
607 {
608     vfs_path_t *result = NULL;
609     const vfs_path_element_t *path_element;
610 
611     if (pathname_vpath == NULL)
612         return NULL;
613 
614     path_element = vfs_path_get_by_index (pathname_vpath, -1);
615     if (vfs_path_element_valid (path_element))
616     {
617         result = path_element->class->getlocalcopy != NULL ?
618             path_element->class->getlocalcopy (pathname_vpath) :
619             mc_def_getlocalcopy (pathname_vpath);
620         if (result == NULL)
621             errno = vfs_ferrno (path_element->class);
622     }
623     return result;
624 }
625 
626 /* --------------------------------------------------------------------------------------------- */
627 
628 int
mc_ungetlocalcopy(const vfs_path_t * pathname_vpath,const vfs_path_t * local_vpath,gboolean has_changed)629 mc_ungetlocalcopy (const vfs_path_t * pathname_vpath, const vfs_path_t * local_vpath,
630                    gboolean has_changed)
631 {
632     int result = -1;
633     const vfs_path_element_t *path_element;
634 
635     if (pathname_vpath == NULL)
636         return (-1);
637 
638     path_element = vfs_path_get_by_index (pathname_vpath, -1);
639     if (vfs_path_element_valid (path_element))
640         result = path_element->class->ungetlocalcopy != NULL ?
641             path_element->class->ungetlocalcopy (pathname_vpath, local_vpath, has_changed) :
642             mc_def_ungetlocalcopy (pathname_vpath, local_vpath, has_changed);
643 
644     return result;
645 }
646 
647 /* --------------------------------------------------------------------------------------------- */
648 /**
649  * VFS chdir.
650  *
651  * @param vpath VFS-path
652  *
653  * @return 0 on success, -1 on failure.
654  */
655 
656 int
mc_chdir(const vfs_path_t * vpath)657 mc_chdir (const vfs_path_t * vpath)
658 {
659     struct vfs_class *old_vfs;
660     vfsid old_vfsid;
661     int result;
662     const vfs_path_element_t *path_element;
663     vfs_path_t *cd_vpath;
664 
665     if (vpath == NULL)
666         return (-1);
667 
668     if (vpath->relative)
669         cd_vpath = vfs_path_to_absolute (vpath);
670     else
671         cd_vpath = vfs_path_clone (vpath);
672 
673     path_element = vfs_path_get_by_index (cd_vpath, -1);
674     if (!vfs_path_element_valid (path_element) || path_element->class->chdir == NULL)
675         goto error_end;
676 
677 
678     result = path_element->class->chdir (cd_vpath);
679     if (result == -1)
680     {
681         errno = vfs_ferrno (path_element->class);
682         goto error_end;
683     }
684 
685     old_vfsid = vfs_getid (vfs_get_raw_current_dir ());
686     old_vfs = current_vfs;
687 
688     /* Actually change directory */
689     vfs_set_raw_current_dir (cd_vpath);
690 
691     current_vfs = path_element->class;
692 
693     /* This function uses the new current_dir implicitly */
694     vfs_stamp_create (old_vfs, old_vfsid);
695 
696     /* Sometimes we assume no trailing slash on cwd */
697     path_element = vfs_path_get_by_index (vfs_get_raw_current_dir (), -1);
698     if (vfs_path_element_valid (path_element))
699     {
700         if (*path_element->path != '\0')
701         {
702             char *p;
703 
704             p = strchr (path_element->path, 0) - 1;
705             if (IS_PATH_SEP (*p) && p > path_element->path)
706                 *p = '\0';
707         }
708 
709 #ifdef ENABLE_VFS_NET
710         {
711             struct vfs_s_super *super;
712 
713             super = vfs_get_super_by_vpath (vpath);
714             if (super != NULL && super->path_element != NULL)
715             {
716                 g_free (super->path_element->path);
717                 super->path_element->path = g_strdup (path_element->path);
718             }
719         }
720 #endif /* ENABLE_VFS_NET */
721     }
722 
723     return 0;
724 
725   error_end:
726     vfs_path_free (cd_vpath, TRUE);
727     return (-1);
728 }
729 
730 /* --------------------------------------------------------------------------------------------- */
731 
732 off_t
mc_lseek(int fd,off_t offset,int whence)733 mc_lseek (int fd, off_t offset, int whence)
734 {
735     struct vfs_class *vfs;
736     void *fsinfo = NULL;
737     off_t result;
738 
739     if (fd == -1)
740         return (-1);
741 
742     vfs = vfs_class_find_by_handle (fd, &fsinfo);
743     if (vfs == NULL)
744         return (-1);
745 
746     result = vfs->lseek ? vfs->lseek (fsinfo, offset, whence) : -1;
747     if (result == -1)
748         errno = vfs->lseek ? vfs_ferrno (vfs) : E_NOTSUPP;
749     return result;
750 }
751 
752 /* --------------------------------------------------------------------------------------------- */
753 /* Following code heavily borrows from libiberty, mkstemps.c */
754 /*
755  * Arguments:
756  * pname (output) - pointer to the name of the temp file (needs g_free).
757  *                  NULL if the function fails.
758  * prefix - part of the filename before the random part.
759  *          Prepend $TMPDIR or /tmp if there are no path separators.
760  * suffix - if not NULL, part of the filename after the random part.
761  *
762  * Result:
763  * handle of the open file or -1 if couldn't open any.
764  */
765 
766 int
mc_mkstemps(vfs_path_t ** pname_vpath,const char * prefix,const char * suffix)767 mc_mkstemps (vfs_path_t ** pname_vpath, const char *prefix, const char *suffix)
768 {
769     char *p1, *p2;
770     int fd;
771 
772     if (strchr (prefix, PATH_SEP) != NULL)
773         p1 = g_strdup (prefix);
774     else
775     {
776         /* Add prefix first to find the position of XXXXXX */
777         p1 = g_build_filename (mc_tmpdir (), prefix, (char *) NULL);
778     }
779 
780     p2 = g_strconcat (p1, "XXXXXX", suffix, (char *) NULL);
781     g_free (p1);
782 
783     fd = g_mkstemp (p2);
784     if (fd >= 0)
785         *pname_vpath = vfs_path_from_str (p2);
786     else
787     {
788         *pname_vpath = NULL;
789         fd = -1;
790     }
791 
792     g_free (p2);
793 
794     return fd;
795 }
796 
797 /* --------------------------------------------------------------------------------------------- */
798 /**
799  * Return the directory where mc should keep its temporary files.
800  * This directory is (in Bourne shell terms) "${TMPDIR=/tmp}/mc-$USER"
801  * When called the first time, the directory is created if needed.
802  * The first call should be done early, since we are using fprintf()
803  * and not message() to report possible problems.
804  */
805 
806 const char *
mc_tmpdir(void)807 mc_tmpdir (void)
808 {
809     static char buffer[PATH_MAX];
810     static const char *tmpdir = NULL;
811     const char *sys_tmp;
812     struct passwd *pwd;
813     struct stat st;
814     const char *error = NULL;
815 
816     /* Check if already correctly initialized */
817     if (tmpdir != NULL && lstat (tmpdir, &st) == 0 && S_ISDIR (st.st_mode) &&
818         st.st_uid == getuid () && (st.st_mode & 0777) == 0700)
819         return tmpdir;
820 
821     sys_tmp = getenv ("MC_TMPDIR");
822     if (sys_tmp == NULL || !IS_PATH_SEP (sys_tmp[0]))
823     {
824         sys_tmp = getenv ("TMPDIR");
825         if (sys_tmp == NULL || !IS_PATH_SEP (sys_tmp[0]))
826             sys_tmp = TMPDIR_DEFAULT;
827     }
828 
829     pwd = getpwuid (getuid ());
830     if (pwd != NULL)
831         g_snprintf (buffer, sizeof (buffer), "%s/mc-%s", sys_tmp, pwd->pw_name);
832     else
833         g_snprintf (buffer, sizeof (buffer), "%s/mc-%lu", sys_tmp, (unsigned long) getuid ());
834 
835     canonicalize_pathname (buffer);
836 
837     /* Try to create directory */
838     if (mkdir (buffer, S_IRWXU) != 0)
839     {
840         if (errno == EEXIST && lstat (buffer, &st) == 0)
841         {
842             /* Sanity check for existing directory */
843             if (!S_ISDIR (st.st_mode))
844                 error = _("%s is not a directory\n");
845             else if (st.st_uid != getuid ())
846                 error = _("Directory %s is not owned by you\n");
847             else if (((st.st_mode & 0777) != 0700) && (chmod (buffer, 0700) != 0))
848                 error = _("Cannot set correct permissions for directory %s\n");
849         }
850         else
851         {
852             fprintf (stderr,
853                      _("Cannot create temporary directory %s: %s\n"),
854                      buffer, unix_error_string (errno));
855             error = "";
856         }
857     }
858 
859     if (error != NULL)
860     {
861         int test_fd;
862         char *fallback_prefix;
863         gboolean fallback_ok = FALSE;
864         vfs_path_t *test_vpath;
865 
866         if (*error != '\0')
867             fprintf (stderr, error, buffer);
868 
869         /* Test if sys_tmp is suitable for temporary files */
870         fallback_prefix = g_strdup_printf ("%s/mctest", sys_tmp);
871         test_fd = mc_mkstemps (&test_vpath, fallback_prefix, NULL);
872         g_free (fallback_prefix);
873         if (test_fd != -1)
874         {
875             close (test_fd);
876             test_fd = open (vfs_path_as_str (test_vpath), O_RDONLY);
877             if (test_fd != -1)
878             {
879                 close (test_fd);
880                 unlink (vfs_path_as_str (test_vpath));
881                 fallback_ok = TRUE;
882             }
883         }
884 
885         if (fallback_ok)
886         {
887             fprintf (stderr, _("Temporary files will be created in %s\n"), sys_tmp);
888             g_snprintf (buffer, sizeof (buffer), "%s", sys_tmp);
889             error = NULL;
890         }
891         else
892         {
893             fprintf (stderr, _("Temporary files will not be created\n"));
894             g_snprintf (buffer, sizeof (buffer), "%s", "/dev/null/");
895         }
896 
897         vfs_path_free (test_vpath, TRUE);
898         fprintf (stderr, "%s\n", _("Press any key to continue..."));
899         getc (stdin);
900     }
901 
902     tmpdir = buffer;
903 
904     if (error == NULL)
905         g_setenv ("MC_TMPDIR", tmpdir, TRUE);
906 
907     return tmpdir;
908 }
909 
910 /* --------------------------------------------------------------------------------------------- */
911