Revert "VFS: make VFS-specific file handler class the derived one from vfs_file_handl...
[midnight-commander.git] / lib / vfs / direntry.c
blob16ae482f4595c20837587bb4ce727de4a98d155e
1 /*
2 Directory cache support
4 Copyright (C) 1998-2018
5 Free Software Foundation, Inc.
7 Written by:
8 Pavel Machek <pavel@ucw.cz>, 1998
9 Slava Zanko <slavazanko@gmail.com>, 2013
11 This file is part of the Midnight Commander.
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.
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.
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/>.
26 \warning Paths here do _not_ begin with '/', so root directory of
27 archive/site is simply "".
30 /** \file
31 * \brief Source: directory cache support
33 * So that you do not have copy of this in each and every filesystem.
35 * Very loosely based on tar.c from midnight and archives.[ch] from
36 * avfs by Miklos Szeredi (mszeredi@inf.bme.hu)
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.
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.
53 * \author Pavel Machek <pavel@ucw.cz>
54 * \date 1998
58 #include <config.h>
60 #include <errno.h>
61 #include <time.h>
62 #include <sys/time.h> /* gettimeofday() */
63 #include <inttypes.h> /* uintmax_t */
64 #include <stdarg.h>
66 #include "lib/global.h"
68 #include "lib/tty/tty.h" /* enable/disable interrupt key */
69 #include "lib/util.h" /* custom_canonicalize_pathname() */
70 #if 0
71 #include "lib/widget.h" /* message() */
72 #endif
74 #include "vfs.h"
75 #include "utilvfs.h"
76 #include "xdirentry.h"
77 #include "gc.h" /* vfs_rmstamp */
79 /*** global variables ****************************************************************************/
81 /*** file scope macro definitions ****************************************************************/
83 #define CALL(x) if (MEDATA->x) MEDATA->x
85 /*** file scope type declarations ****************************************************************/
87 struct dirhandle
89 GList *cur;
90 struct vfs_s_inode *dir;
93 /*** file scope variables ************************************************************************/
95 /*** file scope functions ************************************************************************/
96 /* --------------------------------------------------------------------------------------------- */
98 static int
99 vfs_s_entry_compare (const void *a, const void *b)
101 const struct vfs_s_entry *e = (const struct vfs_s_entry *) a;
102 const char *name = (const char *) b;
104 return strcmp (e->name, name);
107 /* --------------------------------------------------------------------------------------------- */
109 /* We were asked to create entries automagically */
111 static struct vfs_s_entry *
112 vfs_s_automake (struct vfs_class *me, struct vfs_s_inode *dir, char *path, int flags)
114 struct vfs_s_entry *res;
115 char *sep;
117 sep = strchr (path, PATH_SEP);
118 if (sep != NULL)
119 *sep = '\0';
121 res = vfs_s_generate_entry (me, path, dir, (flags & FL_MKDIR) != 0 ? (0777 | S_IFDIR) : 0777);
122 vfs_s_insert_entry (me, dir, res);
124 if (sep != NULL)
125 *sep = PATH_SEP;
127 return res;
130 /* --------------------------------------------------------------------------------------------- */
131 /* If the entry is a symlink, find the entry for its target */
133 static struct vfs_s_entry *
134 vfs_s_resolve_symlink (struct vfs_class *me, struct vfs_s_entry *entry, int follow)
136 char *linkname;
137 char *fullname = NULL;
138 struct vfs_s_entry *target;
140 if (follow == LINK_NO_FOLLOW)
141 return entry;
142 if (follow == 0)
143 ERRNOR (ELOOP, NULL);
144 if (entry == NULL)
145 ERRNOR (ENOENT, NULL);
146 if (!S_ISLNK (entry->ino->st.st_mode))
147 return entry;
149 linkname = entry->ino->linkname;
150 if (linkname == NULL)
151 ERRNOR (EFAULT, NULL);
153 /* make full path from relative */
154 if (!IS_PATH_SEP (*linkname))
156 char *fullpath;
158 fullpath = vfs_s_fullpath (me, entry->dir);
159 if (fullpath != NULL)
161 fullname = g_strconcat (fullpath, PATH_SEP_STR, linkname, (char *) NULL);
162 linkname = fullname;
163 g_free (fullpath);
167 target = MEDATA->find_entry (me, entry->dir->super->root, linkname, follow - 1, FL_NONE);
168 g_free (fullname);
169 return target;
172 /* --------------------------------------------------------------------------------------------- */
174 * Follow > 0: follow links, serves as loop protect,
175 * == -1: do not follow links
178 static struct vfs_s_entry *
179 vfs_s_find_entry_tree (struct vfs_class *me, struct vfs_s_inode *root,
180 const char *a_path, int follow, int flags)
182 size_t pseg;
183 struct vfs_s_entry *ent = NULL;
184 char *const pathref = g_strdup (a_path);
185 char *path = pathref;
187 /* canonicalize as well, but don't remove '../' from path */
188 custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
190 while (root != NULL)
192 GList *iter;
194 while (IS_PATH_SEP (*path)) /* Strip leading '/' */
195 path++;
197 if (path[0] == '\0')
199 g_free (pathref);
200 return ent;
203 for (pseg = 0; path[pseg] != '\0' && !IS_PATH_SEP (path[pseg]); pseg++)
206 for (iter = root->subdir; iter != NULL; iter = g_list_next (iter))
208 ent = (struct vfs_s_entry *) iter->data;
209 if (strlen (ent->name) == pseg && strncmp (ent->name, path, pseg) == 0)
210 /* FOUND! */
211 break;
214 ent = iter != NULL ? (struct vfs_s_entry *) iter->data : NULL;
216 if (ent == NULL && (flags & (FL_MKFILE | FL_MKDIR)) != 0)
217 ent = vfs_s_automake (me, root, path, flags);
218 if (ent == NULL)
220 me->verrno = ENOENT;
221 goto cleanup;
224 path += pseg;
225 /* here we must follow leading directories always;
226 only the actual file is optional */
227 ent = vfs_s_resolve_symlink (me, ent,
228 strchr (path, PATH_SEP) != NULL ? LINK_FOLLOW : follow);
229 if (ent == NULL)
230 goto cleanup;
231 root = ent->ino;
233 cleanup:
234 g_free (pathref);
235 return NULL;
238 /* --------------------------------------------------------------------------------------------- */
240 static struct vfs_s_entry *
241 vfs_s_find_entry_linear (struct vfs_class *me, struct vfs_s_inode *root,
242 const char *a_path, int follow, int flags)
244 struct vfs_s_entry *ent = NULL;
245 char *const path = g_strdup (a_path);
246 GList *iter;
248 if (root->super->root != root)
249 vfs_die ("We have to use _real_ root. Always. Sorry.");
251 /* canonicalize as well, but don't remove '../' from path */
252 custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
254 if ((flags & FL_DIR) == 0)
256 char *dirname, *name;
257 struct vfs_s_inode *ino;
259 dirname = g_path_get_dirname (path);
260 name = g_path_get_basename (path);
261 ino = vfs_s_find_inode (me, root->super, dirname, follow, flags | FL_DIR);
262 ent = vfs_s_find_entry_tree (me, ino, name, follow, flags);
263 g_free (dirname);
264 g_free (name);
265 g_free (path);
266 return ent;
269 iter = g_list_find_custom (root->subdir, path, (GCompareFunc) vfs_s_entry_compare);
270 ent = iter != NULL ? (struct vfs_s_entry *) iter->data : NULL;
272 if (ent != NULL && !MEDATA->dir_uptodate (me, ent->ino))
274 #if 1
275 vfs_print_message (_("Directory cache expired for %s"), path);
276 #endif
277 vfs_s_free_entry (me, ent);
278 ent = NULL;
281 if (ent == NULL)
283 struct vfs_s_inode *ino;
285 ino = vfs_s_new_inode (me, root->super, vfs_s_default_stat (me, S_IFDIR | 0755));
286 ent = vfs_s_new_entry (me, path, ino);
287 if (MEDATA->dir_load (me, ino, path) == -1)
289 vfs_s_free_entry (me, ent);
290 g_free (path);
291 return NULL;
294 vfs_s_insert_entry (me, root, ent);
296 iter = g_list_find_custom (root->subdir, path, (GCompareFunc) vfs_s_entry_compare);
297 ent = iter != NULL ? (struct vfs_s_entry *) iter->data : NULL;
299 if (ent == NULL)
300 vfs_die ("find_linear: success but directory is not there\n");
302 #if 0
303 if (vfs_s_resolve_symlink (me, ent, follow) == NULL)
305 g_free (path);
306 return NULL;
308 #endif
309 g_free (path);
310 return ent;
313 /* --------------------------------------------------------------------------------------------- */
314 /* Ook, these were functions around directory entries / inodes */
315 /* -------------------------------- superblock games -------------------------- */
317 static struct vfs_s_super *
318 vfs_s_new_super (struct vfs_class *me)
320 struct vfs_s_super *super;
322 super = g_new0 (struct vfs_s_super, 1);
323 super->me = me;
324 return super;
327 /* --------------------------------------------------------------------------------------------- */
329 static inline void
330 vfs_s_insert_super (struct vfs_class *me, struct vfs_s_super *super)
332 MEDATA->supers = g_list_prepend (MEDATA->supers, super);
335 /* --------------------------------------------------------------------------------------------- */
337 static void
338 vfs_s_free_super (struct vfs_class *me, struct vfs_s_super *super)
340 if (super->root != NULL)
342 vfs_s_free_inode (me, super->root);
343 super->root = NULL;
346 #if 0
347 /* FIXME: We currently leak small ammount of memory, sometimes. Fix it if you can. */
348 if (super->ino_usage != 0)
349 message (D_ERROR, "Direntry warning",
350 "Super ino_usage is %d, memory leak", super->ino_usage);
352 if (super->want_stale)
353 message (D_ERROR, "Direntry warning", "%s", "Super has want_stale set");
354 #endif
356 MEDATA->supers = g_list_remove (MEDATA->supers, super);
358 CALL (free_archive) (me, super);
359 #ifdef ENABLE_VFS_NET
360 vfs_path_element_free (super->path_element);
361 #endif
362 g_free (super->name);
363 g_free (super);
366 /* --------------------------------------------------------------------------------------------- */
367 /* Support of archives */
368 /* ------------------------ readdir & friends ----------------------------- */
370 static struct vfs_s_inode *
371 vfs_s_inode_from_path (const vfs_path_t * vpath, int flags)
373 struct vfs_s_super *super;
374 struct vfs_s_inode *ino;
375 const char *q;
376 const vfs_path_element_t *path_element;
378 q = vfs_s_get_path (vpath, &super, 0);
379 if (q == NULL)
380 return NULL;
382 path_element = vfs_path_get_by_index (vpath, -1);
384 ino =
385 vfs_s_find_inode (path_element->class, super, q,
386 (flags & FL_FOLLOW) != 0 ? LINK_FOLLOW : LINK_NO_FOLLOW,
387 flags & ~FL_FOLLOW);
388 if (ino == NULL && *q == '\0')
389 /* We are asking about / directory of ftp server: assume it exists */
390 ino =
391 vfs_s_find_inode (path_element->class, super, q,
392 (flags & FL_FOLLOW) != 0 ? LINK_FOLLOW : LINK_NO_FOLLOW,
393 FL_DIR | (flags & ~FL_FOLLOW));
394 return ino;
397 /* --------------------------------------------------------------------------------------------- */
399 static void *
400 vfs_s_opendir (const vfs_path_t * vpath)
402 struct vfs_s_inode *dir;
403 struct dirhandle *info;
404 const vfs_path_element_t *path_element;
406 path_element = vfs_path_get_by_index (vpath, -1);
408 dir = vfs_s_inode_from_path (vpath, FL_DIR | FL_FOLLOW);
409 if (dir == NULL)
410 return NULL;
411 if (!S_ISDIR (dir->st.st_mode))
413 path_element->class->verrno = ENOTDIR;
414 return NULL;
417 dir->st.st_nlink++;
418 #if 0
419 if (dir->subdir == NULL) /* This can actually happen if we allow empty directories */
421 path_element->class->verrno = EAGAIN;
422 return NULL;
424 #endif
425 info = g_new (struct dirhandle, 1);
426 info->cur = dir->subdir;
427 info->dir = dir;
429 return info;
432 /* --------------------------------------------------------------------------------------------- */
434 static void *
435 vfs_s_readdir (void *data)
437 static union vfs_dirent dir;
438 struct dirhandle *info = (struct dirhandle *) data;
439 const char *name;
441 if (info->cur == NULL || info->cur->data == NULL)
442 return NULL;
444 name = ((struct vfs_s_entry *) info->cur->data)->name;
445 if (name != NULL)
446 g_strlcpy (dir.dent.d_name, name, MC_MAXPATHLEN);
447 else
448 vfs_die ("Null in structure-cannot happen");
450 info->cur = g_list_next (info->cur);
452 return (void *) &dir;
455 /* --------------------------------------------------------------------------------------------- */
457 static int
458 vfs_s_closedir (void *data)
460 struct dirhandle *info = (struct dirhandle *) data;
461 struct vfs_s_inode *dir = info->dir;
463 vfs_s_free_inode (dir->super->me, dir);
464 g_free (data);
465 return 0;
468 /* --------------------------------------------------------------------------------------------- */
470 static int
471 vfs_s_chdir (const vfs_path_t * vpath)
473 void *data;
475 data = vfs_s_opendir (vpath);
476 if (data == NULL)
477 return (-1);
478 vfs_s_closedir (data);
479 return 0;
482 /* --------------------------------------------------------------------------------------------- */
483 /* --------------------------- stat and friends ---------------------------- */
485 static int
486 vfs_s_internal_stat (const vfs_path_t * vpath, struct stat *buf, int flag)
488 struct vfs_s_inode *ino;
490 ino = vfs_s_inode_from_path (vpath, flag);
491 if (ino == NULL)
492 return (-1);
493 *buf = ino->st;
494 return 0;
497 /* --------------------------------------------------------------------------------------------- */
499 static int
500 vfs_s_readlink (const vfs_path_t * vpath, char *buf, size_t size)
502 struct vfs_s_inode *ino;
503 size_t len;
504 const vfs_path_element_t *path_element;
506 path_element = vfs_path_get_by_index (vpath, -1);
508 ino = vfs_s_inode_from_path (vpath, 0);
509 if (ino == NULL)
510 return (-1);
512 if (!S_ISLNK (ino->st.st_mode))
514 path_element->class->verrno = EINVAL;
515 return (-1);
518 if (ino->linkname == NULL)
520 path_element->class->verrno = EFAULT;
521 return (-1);
524 len = strlen (ino->linkname);
525 if (size < len)
526 len = size;
527 /* readlink() does not append a NUL character to buf */
528 memcpy (buf, ino->linkname, len);
529 return len;
532 /* --------------------------------------------------------------------------------------------- */
534 static ssize_t
535 vfs_s_read (void *fh, char *buffer, size_t count)
537 struct vfs_class *me = FH_SUPER->me;
539 if (FH->linear == LS_LINEAR_PREOPEN)
541 if (MEDATA->linear_start (me, FH, FH->pos) == 0)
542 return (-1);
545 if (FH->linear == LS_LINEAR_CLOSED)
546 vfs_die ("linear_start() did not set linear_state!");
548 if (FH->linear == LS_LINEAR_OPEN)
549 return MEDATA->linear_read (me, FH, buffer, count);
551 if (FH->handle != -1)
553 ssize_t n;
555 n = read (FH->handle, buffer, count);
556 if (n < 0)
557 me->verrno = errno;
558 return n;
560 vfs_die ("vfs_s_read: This should not happen\n");
561 return (-1);
564 /* --------------------------------------------------------------------------------------------- */
566 static ssize_t
567 vfs_s_write (void *fh, const char *buffer, size_t count)
569 struct vfs_class *me = FH_SUPER->me;
571 if (FH->linear != LS_NOT_LINEAR)
572 vfs_die ("no writing to linear files, please");
574 FH->changed = TRUE;
575 if (FH->handle != -1)
577 ssize_t n;
579 n = write (FH->handle, buffer, count);
580 if (n < 0)
581 me->verrno = errno;
582 return n;
584 vfs_die ("vfs_s_write: This should not happen\n");
585 return 0;
588 /* --------------------------------------------------------------------------------------------- */
590 static off_t
591 vfs_s_lseek (void *fh, off_t offset, int whence)
593 off_t size = FH->ino->st.st_size;
595 if (FH->linear == LS_LINEAR_OPEN)
596 vfs_die ("cannot lseek() after linear_read!");
598 if (FH->handle != -1)
599 { /* If we have local file opened, we want to work with it */
600 off_t retval;
602 retval = lseek (FH->handle, offset, whence);
603 if (retval == -1)
604 FH->ino->super->me->verrno = errno;
605 return retval;
608 switch (whence)
610 case SEEK_CUR:
611 offset += FH->pos;
612 break;
613 case SEEK_END:
614 offset += size;
615 break;
616 default:
617 break;
619 if (offset < 0)
620 FH->pos = 0;
621 else if (offset < size)
622 FH->pos = offset;
623 else
624 FH->pos = size;
625 return FH->pos;
628 /* --------------------------------------------------------------------------------------------- */
630 static int
631 vfs_s_close (void *fh)
633 int res = 0;
634 struct vfs_class *me = FH_SUPER->me;
636 if (me == NULL)
637 return (-1);
639 FH_SUPER->fd_usage--;
640 if (FH_SUPER->fd_usage == 0)
641 vfs_stamp_create (me, FH_SUPER);
643 if (FH->linear == LS_LINEAR_OPEN)
644 MEDATA->linear_close (me, fh);
645 if (MEDATA->fh_close != NULL)
646 res = MEDATA->fh_close (me, fh);
647 if ((MEDATA->flags & VFS_S_USETMP) != 0 && FH->changed && MEDATA->file_store != NULL)
649 char *s;
651 s = vfs_s_fullpath (me, FH->ino);
653 if (s == NULL)
654 res = -1;
655 else
657 res = MEDATA->file_store (me, fh, s, FH->ino->localname);
658 g_free (s);
660 vfs_s_invalidate (me, FH_SUPER);
662 if (FH->handle != -1)
663 close (FH->handle);
665 vfs_s_free_inode (me, FH->ino);
666 if (MEDATA->fh_free_data != NULL)
667 MEDATA->fh_free_data (fh);
668 g_free (fh);
669 return res;
672 /* --------------------------------------------------------------------------------------------- */
674 static void
675 vfs_s_print_stats (const char *fs_name, const char *action,
676 const char *file_name, off_t have, off_t need)
678 if (need != 0)
679 vfs_print_message (_("%s: %s: %s %3d%% (%lld) bytes transferred"), fs_name, action,
680 file_name, (int) ((double) have * 100 / need), (long long) have);
681 else
682 vfs_print_message (_("%s: %s: %s %lld bytes transferred"), fs_name, action, file_name,
683 (long long) have);
686 /* --------------------------------------------------------------------------------------------- */
687 /* ------------------------------- mc support ---------------------------- */
689 static void
690 vfs_s_fill_names (struct vfs_class *me, fill_names_f func)
692 GList *iter;
694 for (iter = MEDATA->supers; iter != NULL; iter = g_list_next (iter))
696 const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
697 char *name;
699 name = g_strconcat (super->name, PATH_SEP_STR, me->prefix, VFS_PATH_URL_DELIMITER,
700 /* super->current_dir->name, */ (char *) NULL);
701 func (name);
702 g_free (name);
706 /* --------------------------------------------------------------------------------------------- */
708 static int
709 vfs_s_ferrno (struct vfs_class *me)
711 return me->verrno;
714 /* --------------------------------------------------------------------------------------------- */
716 * Get local copy of the given file. We reuse the existing file cache
717 * for remote filesystems. Archives use standard VFS facilities.
720 static vfs_path_t *
721 vfs_s_getlocalcopy (const vfs_path_t * vpath)
723 vfs_file_handler_t *fh;
724 vfs_path_t *local = NULL;
726 if (vpath == NULL)
727 return NULL;
729 fh = vfs_s_open (vpath, O_RDONLY, 0);
731 if (fh != NULL)
733 const struct vfs_class *me;
735 me = vfs_path_get_by_index (vpath, -1)->class;
736 if ((MEDATA->flags & VFS_S_USETMP) != 0 && fh->ino != NULL)
737 local = vfs_path_from_str_flags (fh->ino->localname, VPF_NO_CANON);
739 vfs_s_close (fh);
742 return local;
745 /* --------------------------------------------------------------------------------------------- */
747 * Return the local copy. Since we are using our cache, we do nothing -
748 * the cache will be removed when the archive is closed.
751 static int
752 vfs_s_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
754 (void) vpath;
755 (void) local;
756 (void) has_changed;
757 return 0;
760 /* --------------------------------------------------------------------------------------------- */
762 static int
763 vfs_s_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
765 const vfs_path_element_t *path_element;
767 path_element = vfs_path_get_by_index (vpath, -1);
769 switch (ctlop)
771 case VFS_SETCTL_STALE_DATA:
773 struct vfs_s_inode *ino;
775 ino = vfs_s_inode_from_path (vpath, 0);
776 if (ino == NULL)
777 return 0;
778 if (arg != NULL)
779 ino->super->want_stale = TRUE;
780 else
782 ino->super->want_stale = FALSE;
783 vfs_s_invalidate (path_element->class, ino->super);
785 return 1;
787 case VFS_SETCTL_LOGFILE:
788 VFS_SUBCLASS (path_element)->logfile = fopen ((char *) arg, "w");
789 return 1;
790 case VFS_SETCTL_FLUSH:
791 VFS_SUBCLASS (path_element)->flush = 1;
792 return 1;
793 default:
794 return 0;
798 /* --------------------------------------------------------------------------------------------- */
799 /* ----------------------------- Stamping support -------------------------- */
801 static vfsid
802 vfs_s_getid (const vfs_path_t * vpath)
804 struct vfs_s_super *archive = NULL;
805 const char *p;
807 p = vfs_s_get_path (vpath, &archive, FL_NO_OPEN);
808 if (p == NULL)
809 return NULL;
811 return (vfsid) archive;
814 /* --------------------------------------------------------------------------------------------- */
816 static int
817 vfs_s_nothingisopen (vfsid id)
819 (void) id;
820 /* Our data structures should survive free of superblock at any time */
821 return 1;
824 /* --------------------------------------------------------------------------------------------- */
826 static void
827 vfs_s_free (vfsid id)
829 vfs_s_free_super (VFS_SUPER (id)->me, VFS_SUPER (id));
832 /* --------------------------------------------------------------------------------------------- */
834 static int
835 vfs_s_dir_uptodate (struct vfs_class *me, struct vfs_s_inode *ino)
837 struct timeval tim;
839 if (MEDATA->flush != 0)
841 MEDATA->flush = 0;
842 return 0;
845 gettimeofday (&tim, NULL);
847 return (tim.tv_sec < ino->timestamp.tv_sec ? 1 : 0);
851 /* --------------------------------------------------------------------------------------------- */
852 /*** public functions ****************************************************************************/
853 /* --------------------------------------------------------------------------------------------- */
855 struct vfs_s_inode *
856 vfs_s_new_inode (struct vfs_class *me, struct vfs_s_super *super, struct stat *initstat)
858 struct vfs_s_inode *ino;
860 ino = g_try_new0 (struct vfs_s_inode, 1);
861 if (ino == NULL)
862 return NULL;
864 if (initstat != NULL)
865 ino->st = *initstat;
866 ino->super = super;
867 ino->st.st_nlink = 0;
868 ino->st.st_ino = MEDATA->inode_counter++;
869 ino->st.st_dev = MEDATA->rdev;
871 super->ino_usage++;
873 CALL (init_inode) (me, ino);
875 return ino;
878 /* --------------------------------------------------------------------------------------------- */
880 void
881 vfs_s_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
883 if (ino == NULL)
884 vfs_die ("Don't pass NULL to me");
886 /* ==0 can happen if freshly created entry is deleted */
887 if (ino->st.st_nlink > 1)
889 ino->st.st_nlink--;
890 return;
893 while (ino->subdir != NULL)
894 vfs_s_free_entry (me, (struct vfs_s_entry *) ino->subdir->data);
896 CALL (free_inode) (me, ino);
897 g_free (ino->linkname);
898 if ((MEDATA->flags & VFS_S_USETMP) != 0 && ino->localname != NULL)
900 unlink (ino->localname);
901 g_free (ino->localname);
903 ino->super->ino_usage--;
904 g_free (ino);
907 /* --------------------------------------------------------------------------------------------- */
909 struct vfs_s_entry *
910 vfs_s_new_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *inode)
912 struct vfs_s_entry *entry;
914 entry = g_new0 (struct vfs_s_entry, 1);
916 entry->name = g_strdup (name);
917 entry->ino = inode;
918 entry->ino->ent = entry;
919 CALL (init_entry) (me, entry);
921 return entry;
924 /* --------------------------------------------------------------------------------------------- */
926 void
927 vfs_s_free_entry (struct vfs_class *me, struct vfs_s_entry *ent)
929 if (ent->dir != NULL)
930 ent->dir->subdir = g_list_remove (ent->dir->subdir, ent);
932 MC_PTR_FREE (ent->name);
934 if (ent->ino != NULL)
936 ent->ino->ent = NULL;
937 vfs_s_free_inode (me, ent->ino);
940 g_free (ent);
943 /* --------------------------------------------------------------------------------------------- */
945 void
946 vfs_s_insert_entry (struct vfs_class *me, struct vfs_s_inode *dir, struct vfs_s_entry *ent)
948 (void) me;
950 ent->dir = dir;
952 ent->ino->st.st_nlink++;
953 dir->subdir = g_list_append (dir->subdir, ent);
956 /* --------------------------------------------------------------------------------------------- */
958 struct stat *
959 vfs_s_default_stat (struct vfs_class *me, mode_t mode)
961 static struct stat st;
962 mode_t myumask;
964 (void) me;
966 myumask = umask (022);
967 umask (myumask);
968 mode &= ~myumask;
970 st.st_mode = mode;
971 st.st_ino = 0;
972 st.st_dev = 0;
973 #ifdef HAVE_STRUCT_STAT_ST_RDEV
974 st.st_rdev = 0;
975 #endif
976 st.st_uid = getuid ();
977 st.st_gid = getgid ();
978 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
979 st.st_blksize = 512;
980 #endif
981 st.st_size = 0;
983 st.st_mtime = st.st_atime = st.st_ctime = time (NULL);
984 #ifdef HAVE_STRUCT_STAT_ST_MTIM
985 st.st_atim.tv_nsec = st.st_mtim.tv_nsec = st.st_ctim.tv_nsec = 0;
986 #endif
988 vfs_adjust_stat (&st);
990 return &st;
993 /* --------------------------------------------------------------------------------------------- */
995 * Calculate number of st_blocks using st_size and st_blksize.
996 * In according to stat(2), st_blocks is the size in 512-byte units.
998 * @param s stat info
1001 void
1002 vfs_adjust_stat (struct stat *s)
1004 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1005 if (s->st_size == 0)
1006 s->st_blocks = 0;
1007 else
1009 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1010 blkcnt_t ioblocks;
1011 blksize_t ioblock_size;
1013 /* 1. Calculate how many IO blocks are occupied */
1014 ioblocks = 1 + (s->st_size - 1) / s->st_blksize;
1015 /* 2. Calculate size of st_blksize in 512-byte units */
1016 ioblock_size = 1 + (s->st_blksize - 1) / 512;
1017 /* 3. Calculate number of blocks */
1018 s->st_blocks = ioblocks * ioblock_size;
1019 #else
1020 /* Let IO block size is 512 bytes */
1021 s->st_blocks = 1 + (s->st_size - 1) / 512;
1022 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1024 #endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
1027 /* --------------------------------------------------------------------------------------------- */
1029 struct vfs_s_entry *
1030 vfs_s_generate_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *parent,
1031 mode_t mode)
1033 struct vfs_s_inode *inode;
1034 struct stat *st;
1036 st = vfs_s_default_stat (me, mode);
1037 inode = vfs_s_new_inode (me, parent->super, st);
1039 return vfs_s_new_entry (me, name, inode);
1042 /* --------------------------------------------------------------------------------------------- */
1044 struct vfs_s_inode *
1045 vfs_s_find_inode (struct vfs_class *me, const struct vfs_s_super *super,
1046 const char *path, int follow, int flags)
1048 struct vfs_s_entry *ent;
1050 if (((MEDATA->flags & VFS_S_REMOTE) == 0) && (*path == '\0'))
1051 return super->root;
1053 ent = MEDATA->find_entry (me, super->root, path, follow, flags);
1054 return (ent != NULL ? ent->ino : NULL);
1057 /* --------------------------------------------------------------------------------------------- */
1058 /* Ook, these were functions around directory entries / inodes */
1059 /* -------------------------------- superblock games -------------------------- */
1061 * get superlock object by vpath
1063 * @param vpath path
1064 * @return superlock object or NULL if not found
1067 struct vfs_s_super *
1068 vfs_get_super_by_vpath (const vfs_path_t * vpath)
1070 GList *iter;
1071 void *cookie = NULL;
1072 const vfs_path_element_t *path_element;
1073 struct vfs_s_subclass *subclass;
1074 struct vfs_s_super *super = NULL;
1075 vfs_path_t *vpath_archive;
1077 path_element = vfs_path_get_by_index (vpath, -1);
1078 subclass = VFS_SUBCLASS (path_element);
1080 vpath_archive = vfs_path_clone (vpath);
1081 vfs_path_remove_element_by_index (vpath_archive, -1);
1083 if (subclass->archive_check != NULL)
1085 cookie = subclass->archive_check (vpath_archive);
1086 if (cookie == NULL)
1087 goto ret;
1090 if (subclass->archive_same == NULL)
1091 goto ret;
1093 for (iter = subclass->supers; iter != NULL; iter = g_list_next (iter))
1095 int i;
1097 super = VFS_SUPER (iter->data);
1099 /* 0 == other, 1 == same, return it, 2 == other but stop scanning */
1100 i = subclass->archive_same (path_element, super, vpath_archive, cookie);
1101 if (i == 1)
1102 goto ret;
1103 if (i != 0)
1104 break;
1106 super = NULL;
1109 ret:
1110 vfs_path_free (vpath_archive);
1111 return super;
1114 /* --------------------------------------------------------------------------------------------- */
1116 * get path from last VFS-element and create corresponding superblock
1118 * @param vpath source path object
1119 * @param archive pointer to object for store newly created superblock
1120 * @param flags flags
1122 * @return path from last VFS-element
1124 const char *
1125 vfs_s_get_path (const vfs_path_t * vpath, struct vfs_s_super **archive, int flags)
1127 const char *retval = "";
1128 int result = -1;
1129 struct vfs_s_super *super;
1130 const vfs_path_element_t *path_element;
1131 struct vfs_s_subclass *subclass;
1133 path_element = vfs_path_get_by_index (vpath, -1);
1135 if (path_element->path != NULL)
1136 retval = path_element->path;
1138 super = vfs_get_super_by_vpath (vpath);
1139 if (super != NULL)
1140 goto return_success;
1142 if ((flags & FL_NO_OPEN) != 0)
1144 path_element->class->verrno = EIO;
1145 return NULL;
1148 subclass = VFS_SUBCLASS (path_element);
1150 super = subclass->new_archive != NULL ?
1151 subclass->new_archive (path_element->class) : vfs_s_new_super (path_element->class);
1153 if (subclass->open_archive != NULL)
1155 vfs_path_t *vpath_archive;
1157 vpath_archive = vfs_path_clone (vpath);
1158 vfs_path_remove_element_by_index (vpath_archive, -1);
1160 result = subclass->open_archive (super, vpath_archive, path_element);
1161 vfs_path_free (vpath_archive);
1163 if (result == -1)
1165 vfs_s_free_super (path_element->class, super);
1166 path_element->class->verrno = EIO;
1167 return NULL;
1169 if (super->name == NULL)
1170 vfs_die ("You have to fill name\n");
1171 if (super->root == NULL)
1172 vfs_die ("You have to fill root inode\n");
1174 vfs_s_insert_super (path_element->class, super);
1175 vfs_stamp_create (path_element->class, super);
1177 return_success:
1178 *archive = super;
1179 return retval;
1182 /* --------------------------------------------------------------------------------------------- */
1184 void
1185 vfs_s_invalidate (struct vfs_class *me, struct vfs_s_super *super)
1187 if (!super->want_stale)
1189 vfs_s_free_inode (me, super->root);
1190 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
1194 /* --------------------------------------------------------------------------------------------- */
1196 char *
1197 vfs_s_fullpath (struct vfs_class *me, struct vfs_s_inode *ino)
1199 if (ino->ent == NULL)
1200 ERRNOR (EAGAIN, NULL);
1202 if ((MEDATA->flags & VFS_S_USETMP) == 0)
1204 /* archives */
1205 char *path;
1207 path = g_strdup (ino->ent->name);
1209 while (TRUE)
1211 char *newpath;
1213 ino = ino->ent->dir;
1214 if (ino == ino->super->root)
1215 break;
1217 newpath = g_strconcat (ino->ent->name, PATH_SEP_STR, path, (char *) NULL);
1218 g_free (path);
1219 path = newpath;
1221 return path;
1224 /* remote systems */
1225 if (ino->ent->dir == NULL || ino->ent->dir->ent == NULL)
1226 return g_strdup (ino->ent->name);
1228 return g_strconcat (ino->ent->dir->ent->name, PATH_SEP_STR, ino->ent->name, (char *) NULL);
1231 /* --------------------------------------------------------------------------------------------- */
1232 /* --------------------------- stat and friends ---------------------------- */
1234 void *
1235 vfs_s_open (const vfs_path_t * vpath, int flags, mode_t mode)
1237 gboolean was_changed = FALSE;
1238 vfs_file_handler_t *fh;
1239 struct vfs_s_super *super;
1240 const char *q;
1241 struct vfs_s_inode *ino;
1242 const vfs_path_element_t *path_element;
1243 struct vfs_s_subclass *s;
1245 path_element = vfs_path_get_by_index (vpath, -1);
1247 q = vfs_s_get_path (vpath, &super, 0);
1248 if (q == NULL)
1249 return NULL;
1250 ino = vfs_s_find_inode (path_element->class, super, q, LINK_FOLLOW, FL_NONE);
1251 if (ino != NULL && (flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
1253 path_element->class->verrno = EEXIST;
1254 return NULL;
1257 s = VFS_SUBCLASS (path_element);
1259 if (ino == NULL)
1261 char *dirname, *name;
1262 struct vfs_s_entry *ent;
1263 struct vfs_s_inode *dir;
1265 /* If the filesystem is read-only, disable file creation */
1266 if ((flags & O_CREAT) == 0 || path_element->class->write == NULL)
1267 return NULL;
1269 dirname = g_path_get_dirname (q);
1270 name = g_path_get_basename (q);
1271 dir = vfs_s_find_inode (path_element->class, super, dirname, LINK_FOLLOW, FL_DIR);
1272 if (dir == NULL)
1274 g_free (dirname);
1275 g_free (name);
1276 return NULL;
1279 ent = vfs_s_generate_entry (path_element->class, name, dir, 0755);
1280 ino = ent->ino;
1281 vfs_s_insert_entry (path_element->class, dir, ent);
1282 if ((s->flags & VFS_S_USETMP) != 0)
1284 int tmp_handle;
1285 vfs_path_t *tmp_vpath;
1287 tmp_handle = vfs_mkstemps (&tmp_vpath, path_element->class->name, name);
1288 ino->localname = g_strdup (vfs_path_as_str (tmp_vpath));
1289 vfs_path_free (tmp_vpath);
1290 if (tmp_handle == -1)
1292 g_free (dirname);
1293 g_free (name);
1294 return NULL;
1297 close (tmp_handle);
1299 g_free (dirname);
1300 g_free (name);
1301 was_changed = TRUE;
1304 if (S_ISDIR (ino->st.st_mode))
1306 path_element->class->verrno = EISDIR;
1307 return NULL;
1310 fh = g_new (vfs_file_handler_t, 1);
1311 fh->pos = 0;
1312 fh->ino = ino;
1313 fh->handle = -1;
1314 fh->changed = was_changed;
1315 fh->linear = LS_NOT_LINEAR;
1316 fh->data = NULL;
1318 if (IS_LINEAR (flags))
1320 if (s->linear_start != NULL)
1322 vfs_print_message ("%s", _("Starting linear transfer..."));
1323 fh->linear = LS_LINEAR_PREOPEN;
1326 else
1328 if (s->fh_open != NULL && s->fh_open (path_element->class, fh, flags, mode) != 0)
1330 if (s->fh_free_data != NULL)
1331 s->fh_free_data (fh);
1332 g_free (fh);
1333 return NULL;
1337 if ((s->flags & VFS_S_USETMP) != 0 && fh->ino->localname != NULL)
1339 fh->handle = open (fh->ino->localname, NO_LINEAR (flags), mode);
1340 if (fh->handle == -1)
1342 g_free (fh);
1343 path_element->class->verrno = errno;
1344 return NULL;
1348 /* i.e. we had no open files and now we have one */
1349 vfs_rmstamp (path_element->class, (vfsid) super);
1350 super->fd_usage++;
1351 fh->ino->st.st_nlink++;
1352 return fh;
1355 /* --------------------------------------------------------------------------------------------- */
1358 vfs_s_stat (const vfs_path_t * vpath, struct stat *buf)
1360 return vfs_s_internal_stat (vpath, buf, FL_FOLLOW);
1363 /* --------------------------------------------------------------------------------------------- */
1366 vfs_s_lstat (const vfs_path_t * vpath, struct stat *buf)
1368 return vfs_s_internal_stat (vpath, buf, FL_NONE);
1371 /* --------------------------------------------------------------------------------------------- */
1374 vfs_s_fstat (void *fh, struct stat *buf)
1376 *buf = FH->ino->st;
1377 return 0;
1380 /* --------------------------------------------------------------------------------------------- */
1383 vfs_s_retrieve_file (struct vfs_class *me, struct vfs_s_inode *ino)
1385 /* If you want reget, you'll have to open file with O_LINEAR */
1386 off_t total = 0;
1387 char buffer[8192];
1388 int handle;
1389 ssize_t n;
1390 off_t stat_size = ino->st.st_size;
1391 vfs_file_handler_t fh;
1392 vfs_path_t *tmp_vpath;
1394 if ((MEDATA->flags & VFS_S_USETMP) == 0)
1395 return (-1);
1397 memset (&fh, 0, sizeof (fh));
1399 fh.ino = ino;
1400 fh.handle = -1;
1402 handle = vfs_mkstemps (&tmp_vpath, me->name, ino->ent->name);
1403 ino->localname = g_strdup (vfs_path_as_str (tmp_vpath));
1404 vfs_path_free (tmp_vpath);
1405 if (handle == -1)
1407 me->verrno = errno;
1408 goto error_4;
1411 if (MEDATA->linear_start (me, &fh, 0) == 0)
1412 goto error_3;
1414 /* Clear the interrupt status */
1415 tty_got_interrupt ();
1416 tty_enable_interrupt_key ();
1418 while ((n = MEDATA->linear_read (me, &fh, buffer, sizeof (buffer))) != 0)
1420 int t;
1422 if (n < 0)
1423 goto error_1;
1425 total += n;
1426 vfs_s_print_stats (me->name, _("Getting file"), ino->ent->name, total, stat_size);
1428 if (tty_got_interrupt ())
1429 goto error_1;
1431 t = write (handle, buffer, n);
1432 if (t != n)
1434 if (t == -1)
1435 me->verrno = errno;
1436 goto error_1;
1439 MEDATA->linear_close (me, &fh);
1440 close (handle);
1442 tty_disable_interrupt_key ();
1443 g_free (fh.data);
1444 return 0;
1446 error_1:
1447 MEDATA->linear_close (me, &fh);
1448 error_3:
1449 tty_disable_interrupt_key ();
1450 close (handle);
1451 unlink (ino->localname);
1452 error_4:
1453 MC_PTR_FREE (ino->localname);
1454 g_free (fh.data);
1455 return (-1);
1458 /* --------------------------------------------------------------------------------------------- */
1459 /* ----------------------------- Stamping support -------------------------- */
1461 /* Initialize one of our subclasses - fill common functions */
1462 void
1463 vfs_s_init_class (struct vfs_s_subclass *sub)
1465 struct vfs_class *vclass = (struct vfs_class *) sub;
1467 vclass->fill_names = vfs_s_fill_names;
1468 vclass->open = vfs_s_open;
1469 vclass->close = vfs_s_close;
1470 vclass->read = vfs_s_read;
1471 if ((sub->flags & VFS_S_READONLY) == 0)
1472 vclass->write = vfs_s_write;
1473 vclass->opendir = vfs_s_opendir;
1474 vclass->readdir = vfs_s_readdir;
1475 vclass->closedir = vfs_s_closedir;
1476 vclass->stat = vfs_s_stat;
1477 vclass->lstat = vfs_s_lstat;
1478 vclass->fstat = vfs_s_fstat;
1479 vclass->readlink = vfs_s_readlink;
1480 vclass->chdir = vfs_s_chdir;
1481 vclass->ferrno = vfs_s_ferrno;
1482 vclass->lseek = vfs_s_lseek;
1483 vclass->getid = vfs_s_getid;
1484 vclass->nothingisopen = vfs_s_nothingisopen;
1485 vclass->free = vfs_s_free;
1486 if ((sub->flags & VFS_S_USETMP) != 0)
1488 vclass->getlocalcopy = vfs_s_getlocalcopy;
1489 vclass->ungetlocalcopy = vfs_s_ungetlocalcopy;
1490 sub->find_entry = vfs_s_find_entry_linear;
1492 else if ((sub->flags & VFS_S_REMOTE) != 0)
1493 sub->find_entry = vfs_s_find_entry_linear;
1494 else
1495 sub->find_entry = vfs_s_find_entry_tree;
1496 vclass->setctl = vfs_s_setctl;
1497 sub->dir_uptodate = vfs_s_dir_uptodate;
1500 /* --------------------------------------------------------------------------------------------- */
1501 /** Find VFS id for given directory name */
1503 vfsid
1504 vfs_getid (const vfs_path_t * vpath)
1506 const vfs_path_element_t *path_element;
1508 path_element = vfs_path_get_by_index (vpath, -1);
1509 if (!vfs_path_element_valid (path_element) || path_element->class->getid == NULL)
1510 return NULL;
1512 return (*path_element->class->getid) (vpath);
1515 /* --------------------------------------------------------------------------------------------- */
1516 /* ----------- Utility functions for networked filesystems -------------- */
1518 #ifdef ENABLE_VFS_NET
1520 vfs_s_select_on_two (int fd1, int fd2)
1522 fd_set set;
1523 struct timeval time_out;
1524 int v;
1525 int maxfd = MAX (fd1, fd2) + 1;
1527 time_out.tv_sec = 1;
1528 time_out.tv_usec = 0;
1529 FD_ZERO (&set);
1530 FD_SET (fd1, &set);
1531 FD_SET (fd2, &set);
1533 v = select (maxfd, &set, 0, 0, &time_out);
1534 if (v <= 0)
1535 return v;
1536 if (FD_ISSET (fd1, &set))
1537 return 1;
1538 if (FD_ISSET (fd2, &set))
1539 return 2;
1540 return (-1);
1543 /* --------------------------------------------------------------------------------------------- */
1546 vfs_s_get_line (struct vfs_class *me, int sock, char *buf, int buf_len, char term)
1548 FILE *logfile = MEDATA->logfile;
1549 int i;
1550 char c;
1552 for (i = 0; i < buf_len - 1; i++, buf++)
1554 if (read (sock, buf, sizeof (char)) <= 0)
1555 return 0;
1557 if (logfile != NULL)
1559 size_t ret1;
1560 int ret2;
1562 ret1 = fwrite (buf, 1, 1, logfile);
1563 ret2 = fflush (logfile);
1564 (void) ret1;
1565 (void) ret2;
1568 if (*buf == term)
1570 *buf = '\0';
1571 return 1;
1575 /* Line is too long - terminate buffer and discard the rest of line */
1576 *buf = '\0';
1577 while (read (sock, &c, sizeof (c)) > 0)
1579 if (logfile != NULL)
1581 size_t ret1;
1582 int ret2;
1584 ret1 = fwrite (&c, 1, 1, logfile);
1585 ret2 = fflush (logfile);
1586 (void) ret1;
1587 (void) ret2;
1589 if (c == '\n')
1590 return 1;
1592 return 0;
1595 /* --------------------------------------------------------------------------------------------- */
1598 vfs_s_get_line_interruptible (struct vfs_class *me, char *buffer, int size, int fd)
1600 int i;
1601 int res = 0;
1603 (void) me;
1605 tty_enable_interrupt_key ();
1607 for (i = 0; i < size - 1; i++)
1609 ssize_t n;
1611 n = read (fd, &buffer[i], 1);
1612 if (n == -1 && errno == EINTR)
1614 buffer[i] = '\0';
1615 res = EINTR;
1616 goto ret;
1618 if (n == 0)
1620 buffer[i] = '\0';
1621 goto ret;
1623 if (buffer[i] == '\n')
1625 buffer[i] = '\0';
1626 res = 1;
1627 goto ret;
1631 buffer[size - 1] = '\0';
1633 ret:
1634 tty_disable_interrupt_key ();
1636 return res;
1638 #endif /* ENABLE_VFS_NET */
1640 /* --------------------------------------------------------------------------------------------- */
1642 * Normalize filenames start position
1645 void
1646 vfs_s_normalize_filename_leading_spaces (struct vfs_s_inode *root_inode, size_t final_num_spaces)
1648 GList *iter;
1650 for (iter = root_inode->subdir; iter != NULL; iter = g_list_next (iter))
1652 struct vfs_s_entry *entry = (struct vfs_s_entry *) iter->data;
1654 if ((size_t) entry->ino->data_offset > final_num_spaces)
1656 char *source_name = entry->name;
1657 char *spacer;
1659 spacer = g_strnfill (entry->ino->data_offset - final_num_spaces, ' ');
1660 entry->name = g_strdup_printf ("%s%s", spacer, source_name);
1661 g_free (spacer);
1662 g_free (source_name);
1664 entry->ino->data_offset = -1;
1668 /* --------------------------------------------------------------------------------------------- */