1 /*
2    Directory cache support
3 
4    Copyright (C) 1998-2021
5    Free Software Foundation, Inc.
6 
7    Written by:
8    Pavel Machek <pavel@ucw.cz>, 1998
9    Slava Zanko <slavazanko@gmail.com>, 2013
10 
11    This file is part of the Midnight Commander.
12 
13    The Midnight Commander is free software: you can redistribute it
14    and/or modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation, either version 3 of the License,
16    or (at your option) any later version.
17 
18    The Midnight Commander is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21    GNU General Public License for more details.
22 
23    You should have received a copy of the GNU General Public License
24    along with this program.  If not, see <http://www.gnu.org/licenses/>.
25 
26    \warning Paths here do _not_ begin with '/', so root directory of
27    archive/site is simply "".
28  */
29 
30 /** \file
31  *  \brief Source: directory cache support
32  *
33  *  So that you do not have copy of this in each and every filesystem.
34  *
35  *  Very loosely based on tar.c from midnight and archives.[ch] from
36  *  avfs by Miklos Szeredi (mszeredi@inf.bme.hu)
37  *
38  *  Unfortunately, I was unable to keep all filesystems
39  *  uniform. tar-like filesystems use tree structure where each
40  *  directory has pointers to its subdirectories. We can do this
41  *  because we have full information about our archive.
42  *
43  *  At ftp-like filesystems, situation is a little bit different. When
44  *  you cd /usr/src/linux/drivers/char, you do _not_ want /usr,
45  *  /usr/src, /usr/src/linux and /usr/src/linux/drivers to be
46  *  listed. That means that we do not have complete information, and if
47  *  /usr is symlink to /4, we will not know. Also we have to time out
48  *  entries and things would get messy with tree-like approach. So we
49  *  do different trick: root directory is completely special and
50  *  completely fake, it contains entries such as 'usr', 'usr/src', ...,
51  *  and we'll try to use custom find_entry function.
52  *
53  *  \author Pavel Machek <pavel@ucw.cz>
54  *  \date 1998
55  *
56  */
57 
58 #include <config.h>
59 
60 #include <errno.h>
61 #include <inttypes.h>           /* uintmax_t */
62 #include <stdarg.h>
63 #ifdef HAVE_SYS_SELECT_H
64 #include <sys/select.h>
65 #endif
66 #include <sys/types.h>
67 #include <unistd.h>
68 
69 #include "lib/global.h"
70 
71 #include "lib/tty/tty.h"        /* enable/disable interrupt key */
72 #include "lib/util.h"           /* custom_canonicalize_pathname() */
73 #if 0
74 #include "lib/widget.h"         /* message() */
75 #endif
76 
77 #include "vfs.h"
78 #include "utilvfs.h"
79 #include "xdirentry.h"
80 #include "gc.h"                 /* vfs_rmstamp */
81 
82 /*** global variables ****************************************************************************/
83 
84 /*** file scope macro definitions ****************************************************************/
85 
86 #define CALL(x) \
87         if (VFS_SUBCLASS (me)->x != NULL) \
88             VFS_SUBCLASS (me)->x
89 
90 /*** file scope type declarations ****************************************************************/
91 
92 struct dirhandle
93 {
94     GList *cur;
95     struct vfs_s_inode *dir;
96 };
97 
98 /*** file scope variables ************************************************************************/
99 
100 /* --------------------------------------------------------------------------------------------- */
101 /*** file scope functions ************************************************************************/
102 /* --------------------------------------------------------------------------------------------- */
103 
104 /* We were asked to create entries automagically */
105 
106 static struct vfs_s_entry *
vfs_s_automake(struct vfs_class * me,struct vfs_s_inode * dir,char * path,int flags)107 vfs_s_automake (struct vfs_class *me, struct vfs_s_inode *dir, char *path, int flags)
108 {
109     struct vfs_s_entry *res;
110     char *sep;
111 
112     sep = strchr (path, PATH_SEP);
113     if (sep != NULL)
114         *sep = '\0';
115 
116     res = vfs_s_generate_entry (me, path, dir, (flags & FL_MKDIR) != 0 ? (0777 | S_IFDIR) : 0777);
117     vfs_s_insert_entry (me, dir, res);
118 
119     if (sep != NULL)
120         *sep = PATH_SEP;
121 
122     return res;
123 }
124 
125 /* --------------------------------------------------------------------------------------------- */
126 /* If the entry is a symlink, find the entry for its target */
127 
128 static struct vfs_s_entry *
vfs_s_resolve_symlink(struct vfs_class * me,struct vfs_s_entry * entry,int follow)129 vfs_s_resolve_symlink (struct vfs_class *me, struct vfs_s_entry *entry, int follow)
130 {
131     char *linkname;
132     char *fullname = NULL;
133     struct vfs_s_entry *target;
134 
135     if (follow == LINK_NO_FOLLOW)
136         return entry;
137     if (follow == 0)
138         ERRNOR (ELOOP, NULL);
139     if (entry == NULL)
140         ERRNOR (ENOENT, NULL);
141     if (!S_ISLNK (entry->ino->st.st_mode))
142         return entry;
143 
144     linkname = entry->ino->linkname;
145     if (linkname == NULL)
146         ERRNOR (EFAULT, NULL);
147 
148     /* make full path from relative */
149     if (!IS_PATH_SEP (*linkname))
150     {
151         char *fullpath;
152 
153         fullpath = vfs_s_fullpath (me, entry->dir);
154         if (fullpath != NULL)
155         {
156             fullname = g_strconcat (fullpath, PATH_SEP_STR, linkname, (char *) NULL);
157             linkname = fullname;
158             g_free (fullpath);
159         }
160     }
161 
162     target =
163         VFS_SUBCLASS (me)->find_entry (me, entry->dir->super->root, linkname, follow - 1, FL_NONE);
164     g_free (fullname);
165     return target;
166 }
167 
168 /* --------------------------------------------------------------------------------------------- */
169 /*
170  * Follow > 0: follow links, serves as loop protect,
171  *       == -1: do not follow links
172  */
173 
174 static struct vfs_s_entry *
vfs_s_find_entry_tree(struct vfs_class * me,struct vfs_s_inode * root,const char * a_path,int follow,int flags)175 vfs_s_find_entry_tree (struct vfs_class *me, struct vfs_s_inode *root,
176                        const char *a_path, int follow, int flags)
177 {
178     size_t pseg;
179     struct vfs_s_entry *ent = NULL;
180     char *const pathref = g_strdup (a_path);
181     char *path = pathref;
182 
183     /* canonicalize as well, but don't remove '../' from path */
184     custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
185 
186     while (root != NULL)
187     {
188         GList *iter;
189 
190         while (IS_PATH_SEP (*path))     /* Strip leading '/' */
191             path++;
192 
193         if (path[0] == '\0')
194         {
195             g_free (pathref);
196             return ent;
197         }
198 
199         for (pseg = 0; path[pseg] != '\0' && !IS_PATH_SEP (path[pseg]); pseg++)
200             ;
201 
202         for (iter = g_queue_peek_head_link (root->subdir); iter != NULL; iter = g_list_next (iter))
203         {
204             ent = VFS_ENTRY (iter->data);
205             if (strlen (ent->name) == pseg && strncmp (ent->name, path, pseg) == 0)
206                 /* FOUND! */
207                 break;
208         }
209 
210         ent = iter != NULL ? VFS_ENTRY (iter->data) : NULL;
211 
212         if (ent == NULL && (flags & (FL_MKFILE | FL_MKDIR)) != 0)
213             ent = vfs_s_automake (me, root, path, flags);
214         if (ent == NULL)
215         {
216             me->verrno = ENOENT;
217             goto cleanup;
218         }
219 
220         path += pseg;
221         /* here we must follow leading directories always;
222            only the actual file is optional */
223         ent = vfs_s_resolve_symlink (me, ent,
224                                      strchr (path, PATH_SEP) != NULL ? LINK_FOLLOW : follow);
225         if (ent == NULL)
226             goto cleanup;
227         root = ent->ino;
228     }
229   cleanup:
230     g_free (pathref);
231     return NULL;
232 }
233 
234 /* --------------------------------------------------------------------------------------------- */
235 
236 static struct vfs_s_entry *
vfs_s_find_entry_linear(struct vfs_class * me,struct vfs_s_inode * root,const char * a_path,int follow,int flags)237 vfs_s_find_entry_linear (struct vfs_class *me, struct vfs_s_inode *root,
238                          const char *a_path, int follow, int flags)
239 {
240     struct vfs_s_entry *ent = NULL;
241     char *const path = g_strdup (a_path);
242     GList *iter;
243 
244     if (root->super->root != root)
245         vfs_die ("We have to use _real_ root. Always. Sorry.");
246 
247     /* canonicalize as well, but don't remove '../' from path */
248     custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
249 
250     if ((flags & FL_DIR) == 0)
251     {
252         char *dirname, *name;
253         struct vfs_s_inode *ino;
254 
255         dirname = g_path_get_dirname (path);
256         name = g_path_get_basename (path);
257         ino = vfs_s_find_inode (me, root->super, dirname, follow, flags | FL_DIR);
258         ent = vfs_s_find_entry_tree (me, ino, name, follow, flags);
259         g_free (dirname);
260         g_free (name);
261         g_free (path);
262         return ent;
263     }
264 
265     iter = g_queue_find_custom (root->subdir, path, (GCompareFunc) vfs_s_entry_compare);
266     ent = iter != NULL ? VFS_ENTRY (iter->data) : NULL;
267 
268     if (ent != NULL && !VFS_SUBCLASS (me)->dir_uptodate (me, ent->ino))
269     {
270 #if 1
271         vfs_print_message (_("Directory cache expired for %s"), path);
272 #endif
273         vfs_s_free_entry (me, ent);
274         ent = NULL;
275     }
276 
277     if (ent == NULL)
278     {
279         struct vfs_s_inode *ino;
280 
281         ino = vfs_s_new_inode (me, root->super, vfs_s_default_stat (me, S_IFDIR | 0755));
282         ent = vfs_s_new_entry (me, path, ino);
283         if (VFS_SUBCLASS (me)->dir_load (me, ino, path) == -1)
284         {
285             vfs_s_free_entry (me, ent);
286             g_free (path);
287             return NULL;
288         }
289 
290         vfs_s_insert_entry (me, root, ent);
291 
292         iter = g_queue_find_custom (root->subdir, path, (GCompareFunc) vfs_s_entry_compare);
293         ent = iter != NULL ? VFS_ENTRY (iter->data) : NULL;
294     }
295     if (ent == NULL)
296         vfs_die ("find_linear: success but directory is not there\n");
297 
298 #if 0
299     if (vfs_s_resolve_symlink (me, ent, follow) == NULL)
300     {
301         g_free (path);
302         return NULL;
303     }
304 #endif
305     g_free (path);
306     return ent;
307 }
308 
309 /* --------------------------------------------------------------------------------------------- */
310 /* Ook, these were functions around directory entries / inodes */
311 /* -------------------------------- superblock games -------------------------- */
312 
313 static struct vfs_s_super *
vfs_s_new_super(struct vfs_class * me)314 vfs_s_new_super (struct vfs_class *me)
315 {
316     struct vfs_s_super *super;
317 
318     super = g_new0 (struct vfs_s_super, 1);
319     super->me = me;
320     return super;
321 }
322 
323 /* --------------------------------------------------------------------------------------------- */
324 
325 static inline void
vfs_s_insert_super(struct vfs_class * me,struct vfs_s_super * super)326 vfs_s_insert_super (struct vfs_class *me, struct vfs_s_super *super)
327 {
328     VFS_SUBCLASS (me)->supers = g_list_prepend (VFS_SUBCLASS (me)->supers, super);
329 }
330 
331 /* --------------------------------------------------------------------------------------------- */
332 
333 static void
vfs_s_free_super(struct vfs_class * me,struct vfs_s_super * super)334 vfs_s_free_super (struct vfs_class *me, struct vfs_s_super *super)
335 {
336     if (super->root != NULL)
337     {
338         vfs_s_free_inode (me, super->root);
339         super->root = NULL;
340     }
341 
342 #if 0
343     /* FIXME: We currently leak small ammount of memory, sometimes. Fix it if you can. */
344     if (super->ino_usage != 0)
345         message (D_ERROR, "Direntry warning",
346                  "Super ino_usage is %d, memory leak", super->ino_usage);
347 
348     if (super->want_stale)
349         message (D_ERROR, "Direntry warning", "%s", "Super has want_stale set");
350 #endif
351 
352     VFS_SUBCLASS (me)->supers = g_list_remove (VFS_SUBCLASS (me)->supers, super);
353 
354     CALL (free_archive) (me, super);
355 #ifdef ENABLE_VFS_NET
356     vfs_path_element_free (super->path_element);
357 #endif
358     g_free (super->name);
359     g_free (super);
360 }
361 
362 /* --------------------------------------------------------------------------------------------- */
363 
364 static vfs_file_handler_t *
vfs_s_new_fh(struct vfs_s_inode * ino,gboolean changed)365 vfs_s_new_fh (struct vfs_s_inode *ino, gboolean changed)
366 {
367     vfs_file_handler_t *fh;
368 
369     fh = g_new0 (vfs_file_handler_t, 1);
370     vfs_s_init_fh (fh, ino, changed);
371 
372     return fh;
373 }
374 
375 /* --------------------------------------------------------------------------------------------- */
376 
377 static void
vfs_s_free_fh(struct vfs_s_subclass * s,vfs_file_handler_t * fh)378 vfs_s_free_fh (struct vfs_s_subclass *s, vfs_file_handler_t * fh)
379 {
380     if (s->fh_free != NULL)
381         s->fh_free (fh);
382 
383     g_free (fh);
384 }
385 
386 /* --------------------------------------------------------------------------------------------- */
387 /* Support of archives */
388 /* ------------------------ readdir & friends ----------------------------- */
389 
390 static struct vfs_s_inode *
vfs_s_inode_from_path(const vfs_path_t * vpath,int flags)391 vfs_s_inode_from_path (const vfs_path_t * vpath, int flags)
392 {
393     struct vfs_s_super *super;
394     struct vfs_s_inode *ino;
395     const char *q;
396     const vfs_path_element_t *path_element;
397 
398     q = vfs_s_get_path (vpath, &super, 0);
399     if (q == NULL)
400         return NULL;
401 
402     path_element = vfs_path_get_by_index (vpath, -1);
403 
404     ino =
405         vfs_s_find_inode (path_element->class, super, q,
406                           (flags & FL_FOLLOW) != 0 ? LINK_FOLLOW : LINK_NO_FOLLOW,
407                           flags & ~FL_FOLLOW);
408     if (ino == NULL && *q == '\0')
409         /* We are asking about / directory of ftp server: assume it exists */
410         ino =
411             vfs_s_find_inode (path_element->class, super, q,
412                               (flags & FL_FOLLOW) != 0 ? LINK_FOLLOW : LINK_NO_FOLLOW,
413                               FL_DIR | (flags & ~FL_FOLLOW));
414     return ino;
415 }
416 
417 /* --------------------------------------------------------------------------------------------- */
418 
419 static void *
vfs_s_opendir(const vfs_path_t * vpath)420 vfs_s_opendir (const vfs_path_t * vpath)
421 {
422     struct vfs_s_inode *dir;
423     struct dirhandle *info;
424     const vfs_path_element_t *path_element;
425 
426     dir = vfs_s_inode_from_path (vpath, FL_DIR | FL_FOLLOW);
427     if (dir == NULL)
428         return NULL;
429 
430     path_element = vfs_path_get_by_index (vpath, -1);
431 
432     if (!S_ISDIR (dir->st.st_mode))
433     {
434         path_element->class->verrno = ENOTDIR;
435         return NULL;
436     }
437 
438     dir->st.st_nlink++;
439 #if 0
440     if (dir->subdir == NULL)    /* This can actually happen if we allow empty directories */
441     {
442         path_element->class->verrno = EAGAIN;
443         return NULL;
444     }
445 #endif
446     info = g_new (struct dirhandle, 1);
447     info->cur = g_queue_peek_head_link (dir->subdir);
448     info->dir = dir;
449 
450     return info;
451 }
452 
453 /* --------------------------------------------------------------------------------------------- */
454 
455 static struct vfs_dirent *
vfs_s_readdir(void * data)456 vfs_s_readdir (void *data)
457 {
458     struct vfs_dirent *dir = NULL;
459     struct dirhandle *info = (struct dirhandle *) data;
460     const char *name;
461 
462     if (info->cur == NULL || info->cur->data == NULL)
463         return NULL;
464 
465     name = VFS_ENTRY (info->cur->data)->name;
466     if (name != NULL)
467         dir = vfs_dirent_init (NULL, name, 0);
468     else
469         vfs_die ("Null in structure-cannot happen");
470 
471     info->cur = g_list_next (info->cur);
472 
473     return dir;
474 }
475 
476 /* --------------------------------------------------------------------------------------------- */
477 
478 static int
vfs_s_closedir(void * data)479 vfs_s_closedir (void *data)
480 {
481     struct dirhandle *info = (struct dirhandle *) data;
482     struct vfs_s_inode *dir = info->dir;
483 
484     vfs_s_free_inode (dir->super->me, dir);
485     g_free (data);
486     return 0;
487 }
488 
489 /* --------------------------------------------------------------------------------------------- */
490 
491 static int
vfs_s_chdir(const vfs_path_t * vpath)492 vfs_s_chdir (const vfs_path_t * vpath)
493 {
494     void *data;
495 
496     data = vfs_s_opendir (vpath);
497     if (data == NULL)
498         return (-1);
499     vfs_s_closedir (data);
500     return 0;
501 }
502 
503 /* --------------------------------------------------------------------------------------------- */
504 /* --------------------------- stat and friends ---------------------------- */
505 
506 static int
vfs_s_internal_stat(const vfs_path_t * vpath,struct stat * buf,int flag)507 vfs_s_internal_stat (const vfs_path_t * vpath, struct stat *buf, int flag)
508 {
509     struct vfs_s_inode *ino;
510 
511     ino = vfs_s_inode_from_path (vpath, flag);
512     if (ino == NULL)
513         return (-1);
514     *buf = ino->st;
515     return 0;
516 }
517 
518 /* --------------------------------------------------------------------------------------------- */
519 
520 static int
vfs_s_readlink(const vfs_path_t * vpath,char * buf,size_t size)521 vfs_s_readlink (const vfs_path_t * vpath, char *buf, size_t size)
522 {
523     struct vfs_s_inode *ino;
524     size_t len;
525     const vfs_path_element_t *path_element;
526 
527     ino = vfs_s_inode_from_path (vpath, 0);
528     if (ino == NULL)
529         return (-1);
530 
531     path_element = vfs_path_get_by_index (vpath, -1);
532 
533     if (!S_ISLNK (ino->st.st_mode))
534     {
535         path_element->class->verrno = EINVAL;
536         return (-1);
537     }
538 
539     if (ino->linkname == NULL)
540     {
541         path_element->class->verrno = EFAULT;
542         return (-1);
543     }
544 
545     len = strlen (ino->linkname);
546     if (size < len)
547         len = size;
548     /* readlink() does not append a NUL character to buf */
549     memcpy (buf, ino->linkname, len);
550     return len;
551 }
552 
553 /* --------------------------------------------------------------------------------------------- */
554 
555 static ssize_t
vfs_s_read(void * fh,char * buffer,size_t count)556 vfs_s_read (void *fh, char *buffer, size_t count)
557 {
558     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
559     struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me;
560 
561     if (file->linear == LS_LINEAR_PREOPEN)
562     {
563         if (VFS_SUBCLASS (me)->linear_start (me, file, file->pos) == 0)
564             return (-1);
565     }
566 
567     if (file->linear == LS_LINEAR_CLOSED)
568         vfs_die ("linear_start() did not set linear_state!");
569 
570     if (file->linear == LS_LINEAR_OPEN)
571         return VFS_SUBCLASS (me)->linear_read (me, file, buffer, count);
572 
573     if (file->handle != -1)
574     {
575         ssize_t n;
576 
577         n = read (file->handle, buffer, count);
578         if (n < 0)
579             me->verrno = errno;
580         return n;
581     }
582     vfs_die ("vfs_s_read: This should not happen\n");
583     return (-1);
584 }
585 
586 /* --------------------------------------------------------------------------------------------- */
587 
588 static ssize_t
vfs_s_write(void * fh,const char * buffer,size_t count)589 vfs_s_write (void *fh, const char *buffer, size_t count)
590 {
591     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
592     struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me;
593 
594     if (file->linear != LS_NOT_LINEAR)
595         vfs_die ("no writing to linear files, please");
596 
597     file->changed = TRUE;
598     if (file->handle != -1)
599     {
600         ssize_t n;
601 
602         n = write (file->handle, buffer, count);
603         if (n < 0)
604             me->verrno = errno;
605         return n;
606     }
607     vfs_die ("vfs_s_write: This should not happen\n");
608     return 0;
609 }
610 
611 /* --------------------------------------------------------------------------------------------- */
612 
613 static off_t
vfs_s_lseek(void * fh,off_t offset,int whence)614 vfs_s_lseek (void *fh, off_t offset, int whence)
615 {
616     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
617     off_t size = file->ino->st.st_size;
618 
619     if (file->linear == LS_LINEAR_OPEN)
620         vfs_die ("cannot lseek() after linear_read!");
621 
622     if (file->handle != -1)
623     {                           /* If we have local file opened, we want to work with it */
624         off_t retval;
625 
626         retval = lseek (file->handle, offset, whence);
627         if (retval == -1)
628             VFS_FILE_HANDLER_SUPER (fh)->me->verrno = errno;
629         return retval;
630     }
631 
632     switch (whence)
633     {
634     case SEEK_CUR:
635         offset += file->pos;
636         break;
637     case SEEK_END:
638         offset += size;
639         break;
640     default:
641         break;
642     }
643     if (offset < 0)
644         file->pos = 0;
645     else if (offset < size)
646         file->pos = offset;
647     else
648         file->pos = size;
649     return file->pos;
650 }
651 
652 /* --------------------------------------------------------------------------------------------- */
653 
654 static int
vfs_s_close(void * fh)655 vfs_s_close (void *fh)
656 {
657     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
658     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
659     struct vfs_class *me = super->me;
660     struct vfs_s_subclass *sub = VFS_SUBCLASS (me);
661     int res = 0;
662 
663     if (me == NULL)
664         return (-1);
665 
666     super->fd_usage--;
667     if (super->fd_usage == 0)
668         vfs_stamp_create (me, VFS_FILE_HANDLER_SUPER (fh));
669 
670     if (file->linear == LS_LINEAR_OPEN)
671         sub->linear_close (me, fh);
672     if (sub->fh_close != NULL)
673         res = sub->fh_close (me, fh);
674     if ((me->flags & VFSF_USETMP) != 0 && file->changed && sub->file_store != NULL)
675     {
676         char *s;
677 
678         s = vfs_s_fullpath (me, file->ino);
679 
680         if (s == NULL)
681             res = -1;
682         else
683         {
684             res = sub->file_store (me, fh, s, file->ino->localname);
685             g_free (s);
686         }
687         vfs_s_invalidate (me, super);
688     }
689 
690     if (file->handle != -1)
691     {
692         close (file->handle);
693         file->handle = -1;
694     }
695 
696     vfs_s_free_inode (me, file->ino);
697     vfs_s_free_fh (sub, fh);
698 
699     return res;
700 }
701 
702 /* --------------------------------------------------------------------------------------------- */
703 
704 static void
vfs_s_print_stats(const char * fs_name,const char * action,const char * file_name,off_t have,off_t need)705 vfs_s_print_stats (const char *fs_name, const char *action,
706                    const char *file_name, off_t have, off_t need)
707 {
708     if (need != 0)
709         vfs_print_message (_("%s: %s: %s %3d%% (%lld) bytes transferred"), fs_name, action,
710                            file_name, (int) ((double) have * 100 / need), (long long) have);
711     else
712         vfs_print_message (_("%s: %s: %s %lld bytes transferred"), fs_name, action, file_name,
713                            (long long) have);
714 }
715 
716 /* --------------------------------------------------------------------------------------------- */
717 /* ------------------------------- mc support ---------------------------- */
718 
719 static void
vfs_s_fill_names(struct vfs_class * me,fill_names_f func)720 vfs_s_fill_names (struct vfs_class *me, fill_names_f func)
721 {
722     GList *iter;
723 
724     for (iter = VFS_SUBCLASS (me)->supers; iter != NULL; iter = g_list_next (iter))
725     {
726         const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
727         char *name;
728 
729         name = g_strconcat (super->name, PATH_SEP_STR, me->prefix, VFS_PATH_URL_DELIMITER,
730                             /* super->current_dir->name, */ (char *) NULL);
731         func (name);
732         g_free (name);
733     }
734 }
735 
736 /* --------------------------------------------------------------------------------------------- */
737 
738 static int
vfs_s_ferrno(struct vfs_class * me)739 vfs_s_ferrno (struct vfs_class *me)
740 {
741     return me->verrno;
742 }
743 
744 /* --------------------------------------------------------------------------------------------- */
745 /**
746  * Get local copy of the given file.  We reuse the existing file cache
747  * for remote filesystems.  Archives use standard VFS facilities.
748  */
749 
750 static vfs_path_t *
vfs_s_getlocalcopy(const vfs_path_t * vpath)751 vfs_s_getlocalcopy (const vfs_path_t * vpath)
752 {
753     vfs_file_handler_t *fh;
754     vfs_path_t *local = NULL;
755 
756     if (vpath == NULL)
757         return NULL;
758 
759     fh = vfs_s_open (vpath, O_RDONLY, 0);
760 
761     if (fh != NULL)
762     {
763         const struct vfs_class *me;
764 
765         me = vfs_path_get_by_index (vpath, -1)->class;
766         if ((me->flags & VFSF_USETMP) != 0 && fh->ino != NULL)
767             local = vfs_path_from_str_flags (fh->ino->localname, VPF_NO_CANON);
768 
769         vfs_s_close (fh);
770     }
771 
772     return local;
773 }
774 
775 /* --------------------------------------------------------------------------------------------- */
776 /**
777  * Return the local copy.  Since we are using our cache, we do nothing -
778  * the cache will be removed when the archive is closed.
779  */
780 
781 static int
vfs_s_ungetlocalcopy(const vfs_path_t * vpath,const vfs_path_t * local,gboolean has_changed)782 vfs_s_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
783 {
784     (void) vpath;
785     (void) local;
786     (void) has_changed;
787     return 0;
788 }
789 
790 /* --------------------------------------------------------------------------------------------- */
791 
792 static int
vfs_s_setctl(const vfs_path_t * vpath,int ctlop,void * arg)793 vfs_s_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
794 {
795     const vfs_path_element_t *path_element;
796 
797     path_element = vfs_path_get_by_index (vpath, -1);
798 
799     switch (ctlop)
800     {
801     case VFS_SETCTL_STALE_DATA:
802         {
803             struct vfs_s_inode *ino;
804 
805             ino = vfs_s_inode_from_path (vpath, 0);
806             if (ino == NULL)
807                 return 0;
808             if (arg != NULL)
809                 ino->super->want_stale = TRUE;
810             else
811             {
812                 ino->super->want_stale = FALSE;
813                 vfs_s_invalidate (path_element->class, ino->super);
814             }
815             return 1;
816         }
817     case VFS_SETCTL_LOGFILE:
818         path_element->class->logfile = fopen ((char *) arg, "w");
819         return 1;
820     case VFS_SETCTL_FLUSH:
821         path_element->class->flush = TRUE;
822         return 1;
823     default:
824         return 0;
825     }
826 }
827 
828 /* --------------------------------------------------------------------------------------------- */
829 /* ----------------------------- Stamping support -------------------------- */
830 
831 static vfsid
vfs_s_getid(const vfs_path_t * vpath)832 vfs_s_getid (const vfs_path_t * vpath)
833 {
834     struct vfs_s_super *archive = NULL;
835     const char *p;
836 
837     p = vfs_s_get_path (vpath, &archive, FL_NO_OPEN);
838     if (p == NULL)
839         return NULL;
840 
841     return (vfsid) archive;
842 }
843 
844 /* --------------------------------------------------------------------------------------------- */
845 
846 static gboolean
vfs_s_nothingisopen(vfsid id)847 vfs_s_nothingisopen (vfsid id)
848 {
849     return (VFS_SUPER (id)->fd_usage <= 0);
850 }
851 
852 /* --------------------------------------------------------------------------------------------- */
853 
854 static void
vfs_s_free(vfsid id)855 vfs_s_free (vfsid id)
856 {
857     vfs_s_free_super (VFS_SUPER (id)->me, VFS_SUPER (id));
858 }
859 
860 /* --------------------------------------------------------------------------------------------- */
861 
862 static gboolean
vfs_s_dir_uptodate(struct vfs_class * me,struct vfs_s_inode * ino)863 vfs_s_dir_uptodate (struct vfs_class *me, struct vfs_s_inode *ino)
864 {
865     gint64 tim;
866 
867     if (me->flush)
868     {
869         me->flush = FALSE;
870         return 0;
871     }
872 
873     tim = g_get_real_time ();
874 
875     return (tim < ino->timestamp);
876 }
877 
878 /* --------------------------------------------------------------------------------------------- */
879 /*** public functions ****************************************************************************/
880 /* --------------------------------------------------------------------------------------------- */
881 
882 struct vfs_s_inode *
vfs_s_new_inode(struct vfs_class * me,struct vfs_s_super * super,struct stat * initstat)883 vfs_s_new_inode (struct vfs_class *me, struct vfs_s_super *super, struct stat *initstat)
884 {
885     struct vfs_s_inode *ino;
886 
887     ino = g_try_new0 (struct vfs_s_inode, 1);
888     if (ino == NULL)
889         return NULL;
890 
891     if (initstat != NULL)
892         ino->st = *initstat;
893     ino->super = super;
894     ino->subdir = g_queue_new ();
895     ino->st.st_nlink = 0;
896     ino->st.st_ino = VFS_SUBCLASS (me)->inode_counter++;
897     ino->st.st_dev = VFS_SUBCLASS (me)->rdev;
898 
899     super->ino_usage++;
900 
901     CALL (init_inode) (me, ino);
902 
903     return ino;
904 }
905 
906 /* --------------------------------------------------------------------------------------------- */
907 
908 void
vfs_s_free_inode(struct vfs_class * me,struct vfs_s_inode * ino)909 vfs_s_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
910 {
911     if (ino == NULL)
912         vfs_die ("Don't pass NULL to me");
913 
914     /* ==0 can happen if freshly created entry is deleted */
915     if (ino->st.st_nlink > 1)
916     {
917         ino->st.st_nlink--;
918         return;
919     }
920 
921     while (g_queue_get_length (ino->subdir) != 0)
922     {
923         struct vfs_s_entry *entry;
924 
925         entry = VFS_ENTRY (g_queue_peek_head (ino->subdir));
926         vfs_s_free_entry (me, entry);
927     }
928 
929     g_queue_free (ino->subdir);
930     ino->subdir = NULL;
931 
932     CALL (free_inode) (me, ino);
933     g_free (ino->linkname);
934     if ((me->flags & VFSF_USETMP) != 0 && ino->localname != NULL)
935     {
936         unlink (ino->localname);
937         g_free (ino->localname);
938     }
939     ino->super->ino_usage--;
940     g_free (ino);
941 }
942 
943 /* --------------------------------------------------------------------------------------------- */
944 
945 struct vfs_s_entry *
vfs_s_new_entry(struct vfs_class * me,const char * name,struct vfs_s_inode * inode)946 vfs_s_new_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *inode)
947 {
948     struct vfs_s_entry *entry;
949 
950     entry = g_new0 (struct vfs_s_entry, 1);
951 
952     entry->name = g_strdup (name);
953     entry->ino = inode;
954     entry->ino->ent = entry;
955     CALL (init_entry) (me, entry);
956 
957     return entry;
958 }
959 
960 /* --------------------------------------------------------------------------------------------- */
961 
962 void
vfs_s_free_entry(struct vfs_class * me,struct vfs_s_entry * ent)963 vfs_s_free_entry (struct vfs_class *me, struct vfs_s_entry *ent)
964 {
965     if (ent->dir != NULL)
966         g_queue_remove (ent->dir->subdir, ent);
967 
968     MC_PTR_FREE (ent->name);
969 
970     if (ent->ino != NULL)
971     {
972         ent->ino->ent = NULL;
973         vfs_s_free_inode (me, ent->ino);
974     }
975 
976     g_free (ent);
977 }
978 
979 /* --------------------------------------------------------------------------------------------- */
980 
981 void
vfs_s_insert_entry(struct vfs_class * me,struct vfs_s_inode * dir,struct vfs_s_entry * ent)982 vfs_s_insert_entry (struct vfs_class *me, struct vfs_s_inode *dir, struct vfs_s_entry *ent)
983 {
984     (void) me;
985 
986     ent->dir = dir;
987 
988     ent->ino->st.st_nlink++;
989     g_queue_push_tail (dir->subdir, ent);
990 }
991 
992 /* --------------------------------------------------------------------------------------------- */
993 
994 int
vfs_s_entry_compare(const void * a,const void * b)995 vfs_s_entry_compare (const void *a, const void *b)
996 {
997     const struct vfs_s_entry *e = (const struct vfs_s_entry *) a;
998     const char *name = (const char *) b;
999 
1000     return strcmp (e->name, name);
1001 }
1002 
1003 /* --------------------------------------------------------------------------------------------- */
1004 
1005 struct stat *
vfs_s_default_stat(struct vfs_class * me,mode_t mode)1006 vfs_s_default_stat (struct vfs_class *me, mode_t mode)
1007 {
1008     static struct stat st;
1009     mode_t myumask;
1010 
1011     (void) me;
1012 
1013     myumask = umask (022);
1014     umask (myumask);
1015     mode &= ~myumask;
1016 
1017     st.st_mode = mode;
1018     st.st_ino = 0;
1019     st.st_dev = 0;
1020 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1021     st.st_rdev = 0;
1022 #endif
1023     st.st_uid = getuid ();
1024     st.st_gid = getgid ();
1025 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1026     st.st_blksize = 512;
1027 #endif
1028     st.st_size = 0;
1029 
1030     st.st_mtime = st.st_atime = st.st_ctime = time (NULL);
1031 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1032     st.st_atim.tv_nsec = st.st_mtim.tv_nsec = st.st_ctim.tv_nsec = 0;
1033 #endif
1034 
1035     vfs_adjust_stat (&st);
1036 
1037     return &st;
1038 }
1039 
1040 /* --------------------------------------------------------------------------------------------- */
1041 /**
1042  * Calculate number of st_blocks using st_size and st_blksize.
1043  * In according to stat(2), st_blocks is the size in 512-byte units.
1044  *
1045  * @param s stat info
1046  */
1047 
1048 void
vfs_adjust_stat(struct stat * s)1049 vfs_adjust_stat (struct stat *s)
1050 {
1051 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1052     if (s->st_size == 0)
1053         s->st_blocks = 0;
1054     else
1055     {
1056 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1057         blkcnt_t ioblocks;
1058         blksize_t ioblock_size;
1059 
1060         /* 1. Calculate how many IO blocks are occupied */
1061         ioblocks = 1 + (s->st_size - 1) / s->st_blksize;
1062         /* 2. Calculate size of st_blksize in 512-byte units */
1063         ioblock_size = 1 + (s->st_blksize - 1) / 512;
1064         /* 3. Calculate number of blocks */
1065         s->st_blocks = ioblocks * ioblock_size;
1066 #else
1067         /* Let IO block size is 512 bytes */
1068         s->st_blocks = 1 + (s->st_size - 1) / 512;
1069 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1070     }
1071 #endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
1072 }
1073 
1074 /* --------------------------------------------------------------------------------------------- */
1075 
1076 struct vfs_s_entry *
vfs_s_generate_entry(struct vfs_class * me,const char * name,struct vfs_s_inode * parent,mode_t mode)1077 vfs_s_generate_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *parent,
1078                       mode_t mode)
1079 {
1080     struct vfs_s_inode *inode;
1081     struct stat *st;
1082 
1083     st = vfs_s_default_stat (me, mode);
1084     inode = vfs_s_new_inode (me, parent->super, st);
1085 
1086     return vfs_s_new_entry (me, name, inode);
1087 }
1088 
1089 /* --------------------------------------------------------------------------------------------- */
1090 
1091 struct vfs_s_inode *
vfs_s_find_inode(struct vfs_class * me,const struct vfs_s_super * super,const char * path,int follow,int flags)1092 vfs_s_find_inode (struct vfs_class *me, const struct vfs_s_super *super,
1093                   const char *path, int follow, int flags)
1094 {
1095     struct vfs_s_entry *ent;
1096 
1097     if (((me->flags & VFSF_REMOTE) == 0) && (*path == '\0'))
1098         return super->root;
1099 
1100     ent = VFS_SUBCLASS (me)->find_entry (me, super->root, path, follow, flags);
1101     return (ent != NULL ? ent->ino : NULL);
1102 }
1103 
1104 /* --------------------------------------------------------------------------------------------- */
1105 /* Ook, these were functions around directory entries / inodes */
1106 /* -------------------------------- superblock games -------------------------- */
1107 /**
1108  * get superlock object by vpath
1109  *
1110  * @param vpath path
1111  * @return superlock object or NULL if not found
1112  */
1113 
1114 struct vfs_s_super *
vfs_get_super_by_vpath(const vfs_path_t * vpath)1115 vfs_get_super_by_vpath (const vfs_path_t * vpath)
1116 {
1117     GList *iter;
1118     void *cookie = NULL;
1119     const vfs_path_element_t *path_element;
1120     struct vfs_s_subclass *subclass;
1121     struct vfs_s_super *super = NULL;
1122     vfs_path_t *vpath_archive;
1123 
1124     path_element = vfs_path_get_by_index (vpath, -1);
1125     subclass = VFS_SUBCLASS (path_element->class);
1126 
1127     vpath_archive = vfs_path_clone (vpath);
1128     vfs_path_remove_element_by_index (vpath_archive, -1);
1129 
1130     if (subclass->archive_check != NULL)
1131     {
1132         cookie = subclass->archive_check (vpath_archive);
1133         if (cookie == NULL)
1134             goto ret;
1135     }
1136 
1137     if (subclass->archive_same == NULL)
1138         goto ret;
1139 
1140     for (iter = subclass->supers; iter != NULL; iter = g_list_next (iter))
1141     {
1142         int i;
1143 
1144         super = VFS_SUPER (iter->data);
1145 
1146         /* 0 == other, 1 == same, return it, 2 == other but stop scanning */
1147         i = subclass->archive_same (path_element, super, vpath_archive, cookie);
1148         if (i == 1)
1149             goto ret;
1150         if (i != 0)
1151             break;
1152 
1153         super = NULL;
1154     }
1155 
1156   ret:
1157     vfs_path_free (vpath_archive, TRUE);
1158     return super;
1159 }
1160 
1161 /* --------------------------------------------------------------------------------------------- */
1162 /**
1163  * get path from last VFS-element and create corresponding superblock
1164  *
1165  * @param vpath source path object
1166  * @param archive pointer to object for store newly created superblock
1167  * @param flags flags
1168  *
1169  * @return path from last VFS-element
1170  */
1171 const char *
vfs_s_get_path(const vfs_path_t * vpath,struct vfs_s_super ** archive,int flags)1172 vfs_s_get_path (const vfs_path_t * vpath, struct vfs_s_super **archive, int flags)
1173 {
1174     const char *retval = "";
1175     int result = -1;
1176     struct vfs_s_super *super;
1177     const vfs_path_element_t *path_element;
1178     struct vfs_s_subclass *subclass;
1179 
1180     path_element = vfs_path_get_by_index (vpath, -1);
1181 
1182     if (path_element->path != NULL)
1183         retval = path_element->path;
1184 
1185     super = vfs_get_super_by_vpath (vpath);
1186     if (super != NULL)
1187         goto return_success;
1188 
1189     if ((flags & FL_NO_OPEN) != 0)
1190     {
1191         path_element->class->verrno = EIO;
1192         return NULL;
1193     }
1194 
1195     subclass = VFS_SUBCLASS (path_element->class);
1196 
1197     super = subclass->new_archive != NULL ?
1198         subclass->new_archive (path_element->class) : vfs_s_new_super (path_element->class);
1199 
1200     if (subclass->open_archive != NULL)
1201     {
1202         vfs_path_t *vpath_archive;
1203 
1204         vpath_archive = vfs_path_clone (vpath);
1205         vfs_path_remove_element_by_index (vpath_archive, -1);
1206 
1207         result = subclass->open_archive (super, vpath_archive, path_element);
1208         vfs_path_free (vpath_archive, TRUE);
1209     }
1210     if (result == -1)
1211     {
1212         vfs_s_free_super (path_element->class, super);
1213         path_element->class->verrno = EIO;
1214         return NULL;
1215     }
1216     if (super->name == NULL)
1217         vfs_die ("You have to fill name\n");
1218     if (super->root == NULL)
1219         vfs_die ("You have to fill root inode\n");
1220 
1221     vfs_s_insert_super (path_element->class, super);
1222     vfs_stamp_create (path_element->class, super);
1223 
1224   return_success:
1225     *archive = super;
1226     return retval;
1227 }
1228 
1229 /* --------------------------------------------------------------------------------------------- */
1230 
1231 void
vfs_s_invalidate(struct vfs_class * me,struct vfs_s_super * super)1232 vfs_s_invalidate (struct vfs_class *me, struct vfs_s_super *super)
1233 {
1234     if (!super->want_stale)
1235     {
1236         vfs_s_free_inode (me, super->root);
1237         super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
1238     }
1239 }
1240 
1241 /* --------------------------------------------------------------------------------------------- */
1242 
1243 char *
vfs_s_fullpath(struct vfs_class * me,struct vfs_s_inode * ino)1244 vfs_s_fullpath (struct vfs_class *me, struct vfs_s_inode *ino)
1245 {
1246     if (ino->ent == NULL)
1247         ERRNOR (EAGAIN, NULL);
1248 
1249     if ((me->flags & VFSF_USETMP) == 0)
1250     {
1251         /* archives */
1252         char *path;
1253 
1254         path = g_strdup (ino->ent->name);
1255 
1256         while (TRUE)
1257         {
1258             char *newpath;
1259 
1260             ino = ino->ent->dir;
1261             if (ino == ino->super->root)
1262                 break;
1263 
1264             newpath = g_strconcat (ino->ent->name, PATH_SEP_STR, path, (char *) NULL);
1265             g_free (path);
1266             path = newpath;
1267         }
1268         return path;
1269     }
1270 
1271     /* remote systems */
1272     if (ino->ent->dir == NULL || ino->ent->dir->ent == NULL)
1273         return g_strdup (ino->ent->name);
1274 
1275     return g_strconcat (ino->ent->dir->ent->name, PATH_SEP_STR, ino->ent->name, (char *) NULL);
1276 }
1277 
1278 /* --------------------------------------------------------------------------------------------- */
1279 
1280 void
vfs_s_init_fh(vfs_file_handler_t * fh,struct vfs_s_inode * ino,gboolean changed)1281 vfs_s_init_fh (vfs_file_handler_t * fh, struct vfs_s_inode *ino, gboolean changed)
1282 {
1283     fh->ino = ino;
1284     fh->handle = -1;
1285     fh->changed = changed;
1286     fh->linear = LS_NOT_LINEAR;
1287 }
1288 
1289 /* --------------------------------------------------------------------------------------------- */
1290 /* --------------------------- stat and friends ---------------------------- */
1291 
1292 void *
vfs_s_open(const vfs_path_t * vpath,int flags,mode_t mode)1293 vfs_s_open (const vfs_path_t * vpath, int flags, mode_t mode)
1294 {
1295     gboolean was_changed = FALSE;
1296     vfs_file_handler_t *fh;
1297     struct vfs_s_super *super;
1298     const char *q;
1299     struct vfs_s_inode *ino;
1300     const vfs_path_element_t *path_element;
1301     struct vfs_s_subclass *s;
1302 
1303     q = vfs_s_get_path (vpath, &super, 0);
1304     if (q == NULL)
1305         return NULL;
1306 
1307     path_element = vfs_path_get_by_index (vpath, -1);
1308 
1309     ino = vfs_s_find_inode (path_element->class, super, q, LINK_FOLLOW, FL_NONE);
1310     if (ino != NULL && (flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
1311     {
1312         path_element->class->verrno = EEXIST;
1313         return NULL;
1314     }
1315 
1316     s = VFS_SUBCLASS (path_element->class);
1317 
1318     if (ino == NULL)
1319     {
1320         char *dirname, *name;
1321         struct vfs_s_entry *ent;
1322         struct vfs_s_inode *dir;
1323 
1324         /* If the filesystem is read-only, disable file creation */
1325         if ((flags & O_CREAT) == 0 || path_element->class->write == NULL)
1326             return NULL;
1327 
1328         dirname = g_path_get_dirname (q);
1329         name = g_path_get_basename (q);
1330         dir = vfs_s_find_inode (path_element->class, super, dirname, LINK_FOLLOW, FL_DIR);
1331         if (dir == NULL)
1332         {
1333             g_free (dirname);
1334             g_free (name);
1335             return NULL;
1336         }
1337 
1338         ent = vfs_s_generate_entry (path_element->class, name, dir, 0755);
1339         ino = ent->ino;
1340         vfs_s_insert_entry (path_element->class, dir, ent);
1341         if ((VFS_CLASS (s)->flags & VFSF_USETMP) != 0)
1342         {
1343             int tmp_handle;
1344             vfs_path_t *tmp_vpath;
1345 
1346             tmp_handle = vfs_mkstemps (&tmp_vpath, path_element->class->name, name);
1347             ino->localname = vfs_path_free (tmp_vpath, FALSE);
1348             if (tmp_handle == -1)
1349             {
1350                 g_free (dirname);
1351                 g_free (name);
1352                 return NULL;
1353             }
1354 
1355             close (tmp_handle);
1356         }
1357         g_free (dirname);
1358         g_free (name);
1359         was_changed = TRUE;
1360     }
1361 
1362     if (S_ISDIR (ino->st.st_mode))
1363     {
1364         path_element->class->verrno = EISDIR;
1365         return NULL;
1366     }
1367 
1368     fh = s->fh_new != NULL ? s->fh_new (ino, was_changed) : vfs_s_new_fh (ino, was_changed);
1369 
1370     if (IS_LINEAR (flags))
1371     {
1372         if (s->linear_start != NULL)
1373         {
1374             vfs_print_message ("%s", _("Starting linear transfer..."));
1375             fh->linear = LS_LINEAR_PREOPEN;
1376         }
1377     }
1378     else
1379     {
1380         if (s->fh_open != NULL && s->fh_open (path_element->class, fh, flags, mode) != 0)
1381         {
1382             vfs_s_free_fh (s, fh);
1383             return NULL;
1384         }
1385     }
1386 
1387     if ((VFS_CLASS (s)->flags & VFSF_USETMP) != 0 && fh->ino->localname != NULL)
1388     {
1389         fh->handle = open (fh->ino->localname, NO_LINEAR (flags), mode);
1390         if (fh->handle == -1)
1391         {
1392             vfs_s_free_fh (s, fh);
1393             path_element->class->verrno = errno;
1394             return NULL;
1395         }
1396     }
1397 
1398     /* i.e. we had no open files and now we have one */
1399     vfs_rmstamp (path_element->class, (vfsid) super);
1400     super->fd_usage++;
1401     fh->ino->st.st_nlink++;
1402     return fh;
1403 }
1404 
1405 /* --------------------------------------------------------------------------------------------- */
1406 
1407 int
vfs_s_stat(const vfs_path_t * vpath,struct stat * buf)1408 vfs_s_stat (const vfs_path_t * vpath, struct stat *buf)
1409 {
1410     return vfs_s_internal_stat (vpath, buf, FL_FOLLOW);
1411 }
1412 
1413 /* --------------------------------------------------------------------------------------------- */
1414 
1415 int
vfs_s_lstat(const vfs_path_t * vpath,struct stat * buf)1416 vfs_s_lstat (const vfs_path_t * vpath, struct stat *buf)
1417 {
1418     return vfs_s_internal_stat (vpath, buf, FL_NONE);
1419 }
1420 
1421 /* --------------------------------------------------------------------------------------------- */
1422 
1423 int
vfs_s_fstat(void * fh,struct stat * buf)1424 vfs_s_fstat (void *fh, struct stat *buf)
1425 {
1426     *buf = VFS_FILE_HANDLER (fh)->ino->st;
1427     return 0;
1428 }
1429 
1430 /* --------------------------------------------------------------------------------------------- */
1431 
1432 int
vfs_s_retrieve_file(struct vfs_class * me,struct vfs_s_inode * ino)1433 vfs_s_retrieve_file (struct vfs_class *me, struct vfs_s_inode *ino)
1434 {
1435     /* If you want reget, you'll have to open file with O_LINEAR */
1436     off_t total = 0;
1437     char buffer[8192];
1438     int handle;
1439     ssize_t n;
1440     off_t stat_size = ino->st.st_size;
1441     vfs_file_handler_t *fh = NULL;
1442     vfs_path_t *tmp_vpath;
1443     struct vfs_s_subclass *s = VFS_SUBCLASS (me);
1444 
1445     if ((me->flags & VFSF_USETMP) == 0)
1446         return (-1);
1447 
1448     handle = vfs_mkstemps (&tmp_vpath, me->name, ino->ent->name);
1449     ino->localname = vfs_path_free (tmp_vpath, FALSE);
1450     if (handle == -1)
1451     {
1452         me->verrno = errno;
1453         goto error_4;
1454     }
1455 
1456     fh = s->fh_new != NULL ? s->fh_new (ino, FALSE) : vfs_s_new_fh (ino, FALSE);
1457 
1458     if (s->linear_start (me, fh, 0) == 0)
1459         goto error_3;
1460 
1461     /* Clear the interrupt status */
1462     tty_got_interrupt ();
1463     tty_enable_interrupt_key ();
1464 
1465     while ((n = s->linear_read (me, fh, buffer, sizeof (buffer))) != 0)
1466     {
1467         int t;
1468 
1469         if (n < 0)
1470             goto error_1;
1471 
1472         total += n;
1473         vfs_s_print_stats (me->name, _("Getting file"), ino->ent->name, total, stat_size);
1474 
1475         if (tty_got_interrupt ())
1476             goto error_1;
1477 
1478         t = write (handle, buffer, n);
1479         if (t != n)
1480         {
1481             if (t == -1)
1482                 me->verrno = errno;
1483             goto error_1;
1484         }
1485     }
1486     s->linear_close (me, fh);
1487     close (handle);
1488 
1489     tty_disable_interrupt_key ();
1490     vfs_s_free_fh (s, fh);
1491     return 0;
1492 
1493   error_1:
1494     s->linear_close (me, fh);
1495   error_3:
1496     tty_disable_interrupt_key ();
1497     close (handle);
1498     unlink (ino->localname);
1499   error_4:
1500     MC_PTR_FREE (ino->localname);
1501     if (fh != NULL)
1502         vfs_s_free_fh (s, fh);
1503     return (-1);
1504 }
1505 
1506 /* --------------------------------------------------------------------------------------------- */
1507 /* ----------------------------- Stamping support -------------------------- */
1508 
1509 /* Initialize one of our subclasses - fill common functions */
1510 void
vfs_init_class(struct vfs_class * vclass,const char * name,vfs_flags_t flags,const char * prefix)1511 vfs_init_class (struct vfs_class *vclass, const char *name, vfs_flags_t flags, const char *prefix)
1512 {
1513     memset (vclass, 0, sizeof (struct vfs_class));
1514 
1515     vclass->name = name;
1516     vclass->flags = flags;
1517     vclass->prefix = prefix;
1518 
1519     vclass->fill_names = vfs_s_fill_names;
1520     vclass->open = vfs_s_open;
1521     vclass->close = vfs_s_close;
1522     vclass->read = vfs_s_read;
1523     if ((vclass->flags & VFSF_READONLY) == 0)
1524         vclass->write = vfs_s_write;
1525     vclass->opendir = vfs_s_opendir;
1526     vclass->readdir = vfs_s_readdir;
1527     vclass->closedir = vfs_s_closedir;
1528     vclass->stat = vfs_s_stat;
1529     vclass->lstat = vfs_s_lstat;
1530     vclass->fstat = vfs_s_fstat;
1531     vclass->readlink = vfs_s_readlink;
1532     vclass->chdir = vfs_s_chdir;
1533     vclass->ferrno = vfs_s_ferrno;
1534     vclass->lseek = vfs_s_lseek;
1535     vclass->getid = vfs_s_getid;
1536     vclass->nothingisopen = vfs_s_nothingisopen;
1537     vclass->free = vfs_s_free;
1538     vclass->setctl = vfs_s_setctl;
1539     if ((vclass->flags & VFSF_USETMP) != 0)
1540     {
1541         vclass->getlocalcopy = vfs_s_getlocalcopy;
1542         vclass->ungetlocalcopy = vfs_s_ungetlocalcopy;
1543     }
1544 }
1545 
1546 /* --------------------------------------------------------------------------------------------- */
1547 
1548 void
vfs_init_subclass(struct vfs_s_subclass * sub,const char * name,vfs_flags_t flags,const char * prefix)1549 vfs_init_subclass (struct vfs_s_subclass *sub, const char *name, vfs_flags_t flags,
1550                    const char *prefix)
1551 {
1552     struct vfs_class *vclass = VFS_CLASS (sub);
1553     size_t len;
1554     char *start;
1555 
1556     vfs_init_class (vclass, name, flags, prefix);
1557 
1558     len = sizeof (struct vfs_s_subclass) - sizeof (struct vfs_class);
1559     start = (char *) sub + sizeof (struct vfs_class);
1560     memset (start, 0, len);
1561 
1562     if ((vclass->flags & VFSF_USETMP) != 0)
1563         sub->find_entry = vfs_s_find_entry_linear;
1564     else if ((vclass->flags & VFSF_REMOTE) != 0)
1565         sub->find_entry = vfs_s_find_entry_linear;
1566     else
1567         sub->find_entry = vfs_s_find_entry_tree;
1568     sub->dir_uptodate = vfs_s_dir_uptodate;
1569 }
1570 
1571 /* --------------------------------------------------------------------------------------------- */
1572 /** Find VFS id for given directory name */
1573 
1574 vfsid
vfs_getid(const vfs_path_t * vpath)1575 vfs_getid (const vfs_path_t * vpath)
1576 {
1577     const vfs_path_element_t *path_element;
1578 
1579     path_element = vfs_path_get_by_index (vpath, -1);
1580     if (!vfs_path_element_valid (path_element) || path_element->class->getid == NULL)
1581         return NULL;
1582 
1583     return (*path_element->class->getid) (vpath);
1584 }
1585 
1586 /* --------------------------------------------------------------------------------------------- */
1587 /* ----------- Utility functions for networked filesystems  -------------- */
1588 
1589 #ifdef ENABLE_VFS_NET
1590 int
vfs_s_select_on_two(int fd1,int fd2)1591 vfs_s_select_on_two (int fd1, int fd2)
1592 {
1593     fd_set set;
1594     struct timeval time_out;
1595     int v;
1596     int maxfd = MAX (fd1, fd2) + 1;
1597 
1598     time_out.tv_sec = 1;
1599     time_out.tv_usec = 0;
1600     FD_ZERO (&set);
1601     FD_SET (fd1, &set);
1602     FD_SET (fd2, &set);
1603 
1604     v = select (maxfd, &set, 0, 0, &time_out);
1605     if (v <= 0)
1606         return v;
1607     if (FD_ISSET (fd1, &set))
1608         return 1;
1609     if (FD_ISSET (fd2, &set))
1610         return 2;
1611     return (-1);
1612 }
1613 
1614 /* --------------------------------------------------------------------------------------------- */
1615 
1616 int
vfs_s_get_line(struct vfs_class * me,int sock,char * buf,int buf_len,char term)1617 vfs_s_get_line (struct vfs_class *me, int sock, char *buf, int buf_len, char term)
1618 {
1619     FILE *logfile = me->logfile;
1620     int i;
1621     char c;
1622 
1623     for (i = 0; i < buf_len - 1; i++, buf++)
1624     {
1625         if (read (sock, buf, sizeof (char)) <= 0)
1626             return 0;
1627 
1628         if (logfile != NULL)
1629         {
1630             size_t ret1;
1631             int ret2;
1632 
1633             ret1 = fwrite (buf, 1, 1, logfile);
1634             ret2 = fflush (logfile);
1635             (void) ret1;
1636             (void) ret2;
1637         }
1638 
1639         if (*buf == term)
1640         {
1641             *buf = '\0';
1642             return 1;
1643         }
1644     }
1645 
1646     /* Line is too long - terminate buffer and discard the rest of line */
1647     *buf = '\0';
1648     while (read (sock, &c, sizeof (c)) > 0)
1649     {
1650         if (logfile != NULL)
1651         {
1652             size_t ret1;
1653             int ret2;
1654 
1655             ret1 = fwrite (&c, 1, 1, logfile);
1656             ret2 = fflush (logfile);
1657             (void) ret1;
1658             (void) ret2;
1659         }
1660         if (c == '\n')
1661             return 1;
1662     }
1663     return 0;
1664 }
1665 
1666 /* --------------------------------------------------------------------------------------------- */
1667 
1668 int
vfs_s_get_line_interruptible(struct vfs_class * me,char * buffer,int size,int fd)1669 vfs_s_get_line_interruptible (struct vfs_class *me, char *buffer, int size, int fd)
1670 {
1671     int i;
1672     int res = 0;
1673 
1674     (void) me;
1675 
1676     tty_enable_interrupt_key ();
1677 
1678     for (i = 0; i < size - 1; i++)
1679     {
1680         ssize_t n;
1681 
1682         n = read (fd, &buffer[i], 1);
1683         if (n == -1 && errno == EINTR)
1684         {
1685             buffer[i] = '\0';
1686             res = EINTR;
1687             goto ret;
1688         }
1689         if (n == 0)
1690         {
1691             buffer[i] = '\0';
1692             goto ret;
1693         }
1694         if (buffer[i] == '\n')
1695         {
1696             buffer[i] = '\0';
1697             res = 1;
1698             goto ret;
1699         }
1700     }
1701 
1702     buffer[size - 1] = '\0';
1703 
1704   ret:
1705     tty_disable_interrupt_key ();
1706 
1707     return res;
1708 }
1709 #endif /* ENABLE_VFS_NET */
1710 
1711 /* --------------------------------------------------------------------------------------------- */
1712 /**
1713  * Normalize filenames start position
1714  */
1715 
1716 void
vfs_s_normalize_filename_leading_spaces(struct vfs_s_inode * root_inode,size_t final_num_spaces)1717 vfs_s_normalize_filename_leading_spaces (struct vfs_s_inode *root_inode, size_t final_num_spaces)
1718 {
1719     GList *iter;
1720 
1721     for (iter = g_queue_peek_head_link (root_inode->subdir); iter != NULL;
1722          iter = g_list_next (iter))
1723     {
1724         struct vfs_s_entry *entry = VFS_ENTRY (iter->data);
1725 
1726         if ((size_t) entry->ino->data_offset > final_num_spaces)
1727         {
1728             char *source_name, *spacer;
1729 
1730             source_name = entry->name;
1731             spacer = g_strnfill (entry->ino->data_offset - final_num_spaces, ' ');
1732             entry->name = g_strconcat (spacer, source_name, (char *) NULL);
1733             g_free (spacer);
1734             g_free (source_name);
1735         }
1736 
1737         entry->ino->data_offset = -1;
1738     }
1739 }
1740 
1741 /* --------------------------------------------------------------------------------------------- */
1742