Fix of file panel encoding change w/o VFS support.
[pantumic.git] / lib / vfs / mc-vfs / direntry.c
blob61542292c0803caf86d5efebeb6db91156cc77ab
1 /** \file
2 * \brief Source: directory cache support
4 * So that you do not have copy of this in each and every filesystem.
6 * Very loosely based on tar.c from midnight and archives.[ch] from
7 * avfs by Miklos Szeredi (mszeredi@inf.bme.hu)
9 * Unfortunately, I was unable to keep all filesystems
10 * uniform. tar-like filesystems use tree structure where each
11 * directory has pointers to its subdirectories. We can do this
12 * because we have full information about our archive.
14 * At ftp-like filesystems, situation is a little bit different. When
15 * you cd /usr/src/linux/drivers/char, you do _not_ want /usr,
16 * /usr/src, /usr/src/linux and /usr/src/linux/drivers to be
17 * listed. That means that we do not have complete information, and if
18 * /usr is symlink to /4, we will not know. Also we have to time out
19 * entries and things would get messy with tree-like approach. So we
20 * do different trick: root directory is completely special and
21 * completely fake, it contains entries such as 'usr', 'usr/src', ...,
22 * and we'll try to use custom find_entry function.
24 * \author Pavel Machek <pavel@ucw.cz>, distribute under LGPL.
25 * \date 1998
27 * \warning Paths here do _not_ begin with '/', so root directory of
28 * archive/site is simply "".
31 #include <config.h>
33 #include <errno.h>
34 #include <fcntl.h> /* include fcntl.h -> sys/fcntl.h only */
35 /* includes fcntl.h see IEEE Std 1003.1-2008 */
36 #include <time.h>
37 #include <sys/time.h> /* gettimeofday() */
39 #include "lib/global.h"
41 #include "lib/tty/tty.h" /* enable/disable interrupt key */
42 #include "lib/util.h" /* concat_dir_and_file */
44 #include "src/wtools.h" /* message() */
45 #include "src/main.h" /* print_vfs_message */
47 #include "vfs-impl.h"
48 #include "utilvfs.h"
49 #include "xdirentry.h"
50 #include "gc.h" /* vfs_rmstamp */
52 #define CALL(x) if (MEDATA->x) MEDATA->x
54 static volatile int total_inodes = 0, total_entries = 0;
56 struct vfs_s_inode *
57 vfs_s_new_inode (struct vfs_class *me, struct vfs_s_super *super, struct stat *initstat)
59 struct vfs_s_inode *ino;
61 ino = g_try_new0 (struct vfs_s_inode, 1);
62 if (ino == NULL)
63 return NULL;
65 if (initstat)
66 ino->st = *initstat;
67 ino->super = super;
68 ino->st.st_nlink = 0;
69 ino->st.st_ino = MEDATA->inode_counter++;
70 ino->st.st_dev = MEDATA->rdev;
72 super->ino_usage++;
73 total_inodes++;
75 CALL (init_inode) (me, ino);
77 return ino;
80 struct vfs_s_entry *
81 vfs_s_new_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *inode)
83 struct vfs_s_entry *entry;
85 entry = g_new0 (struct vfs_s_entry, 1);
86 total_entries++;
88 if (name)
89 entry->name = g_strdup (name);
91 entry->ino = inode;
92 entry->ino->ent = entry;
93 CALL (init_entry) (me, entry);
95 return entry;
98 static void
99 vfs_s_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
101 if (!ino)
102 vfs_die ("Don't pass NULL to me");
104 /* ==0 can happen if freshly created entry is deleted */
105 if (ino->st.st_nlink <= 1)
107 while (ino->subdir)
109 vfs_s_free_entry (me, ino->subdir);
112 CALL (free_inode) (me, ino);
113 g_free (ino->linkname);
114 if (ino->localname)
116 unlink (ino->localname);
117 g_free (ino->localname);
119 total_inodes--;
120 ino->super->ino_usage--;
121 g_free (ino);
123 else
124 ino->st.st_nlink--;
127 void
128 vfs_s_free_entry (struct vfs_class *me, struct vfs_s_entry *ent)
130 if (ent->prevp)
132 /* It is possible that we are deleting freshly created entry */
133 *ent->prevp = ent->next;
134 if (ent->next)
135 ent->next->prevp = ent->prevp;
138 g_free (ent->name);
139 ent->name = NULL;
141 if (ent->ino)
143 ent->ino->ent = NULL;
144 vfs_s_free_inode (me, ent->ino);
145 ent->ino = NULL;
148 total_entries--;
149 g_free (ent);
152 void
153 vfs_s_insert_entry (struct vfs_class *me, struct vfs_s_inode *dir, struct vfs_s_entry *ent)
155 struct vfs_s_entry **ep;
157 (void) me;
159 for (ep = &dir->subdir; *ep != NULL; ep = &((*ep)->next))
161 ent->prevp = ep;
162 ent->next = NULL;
163 ent->dir = dir;
164 *ep = ent;
166 ent->ino->st.st_nlink++;
169 struct stat *
170 vfs_s_default_stat (struct vfs_class *me, mode_t mode)
172 static struct stat st;
173 int myumask;
175 (void) me;
177 myumask = umask (022);
178 umask (myumask);
179 mode &= ~myumask;
181 st.st_mode = mode;
182 st.st_ino = 0;
183 st.st_dev = 0;
184 st.st_rdev = 0;
185 st.st_uid = getuid ();
186 st.st_gid = getgid ();
187 st.st_size = 0;
188 st.st_mtime = st.st_atime = st.st_ctime = time (NULL);
190 return &st;
193 struct vfs_s_entry *
194 vfs_s_generate_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *parent,
195 mode_t mode)
197 struct vfs_s_inode *inode;
198 struct stat *st;
200 st = vfs_s_default_stat (me, mode);
201 inode = vfs_s_new_inode (me, parent->super, st);
203 return vfs_s_new_entry (me, name, inode);
206 /* We were asked to create entries automagically */
207 static struct vfs_s_entry *
208 vfs_s_automake (struct vfs_class *me, struct vfs_s_inode *dir, char *path, int flags)
210 struct vfs_s_entry *res;
211 char *sep = strchr (path, PATH_SEP);
213 if (sep)
214 *sep = 0;
215 res = vfs_s_generate_entry (me, path, dir, flags & FL_MKDIR ? (0777 | S_IFDIR) : 0777);
216 vfs_s_insert_entry (me, dir, res);
218 if (sep)
219 *sep = PATH_SEP;
221 return res;
224 /* If the entry is a symlink, find the entry for its target */
225 static struct vfs_s_entry *
226 vfs_s_resolve_symlink (struct vfs_class *me, struct vfs_s_entry *entry, int follow)
228 char *linkname;
229 char *fullname = NULL;
230 struct vfs_s_entry *target;
232 if (follow == LINK_NO_FOLLOW)
233 return entry;
234 if (follow == 0)
235 ERRNOR (ELOOP, NULL);
236 if (!entry)
237 ERRNOR (ENOENT, NULL);
238 if (!S_ISLNK (entry->ino->st.st_mode))
239 return entry;
241 linkname = entry->ino->linkname;
242 if (linkname == NULL)
243 ERRNOR (EFAULT, NULL);
245 /* make full path from relative */
246 if (*linkname != PATH_SEP)
248 char *fullpath = vfs_s_fullpath (me, entry->dir);
249 if (fullpath)
251 fullname = g_strconcat (fullpath, "/", linkname, (char *) NULL);
252 linkname = fullname;
253 g_free (fullpath);
257 target = (MEDATA->find_entry) (me, entry->dir->super->root, linkname, follow - 1, 0);
258 g_free (fullname);
259 return target;
263 * Follow > 0: follow links, serves as loop protect,
264 * == -1: do not follow links
266 static struct vfs_s_entry *
267 vfs_s_find_entry_tree (struct vfs_class *me, struct vfs_s_inode *root,
268 const char *a_path, int follow, int flags)
270 size_t pseg;
271 struct vfs_s_entry *ent = NULL;
272 char *const pathref = g_strdup (a_path);
273 char *path = pathref;
275 /* canonicalize as well, but don't remove '../' from path */
276 custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
278 while (root)
280 while (*path == PATH_SEP) /* Strip leading '/' */
281 path++;
283 if (!path[0])
285 g_free (pathref);
286 return ent;
289 for (pseg = 0; path[pseg] && path[pseg] != PATH_SEP; pseg++);
291 for (ent = root->subdir; ent != NULL; ent = ent->next)
292 if (strlen (ent->name) == pseg && (!strncmp (ent->name, path, pseg)))
293 /* FOUND! */
294 break;
296 if (!ent && (flags & (FL_MKFILE | FL_MKDIR)))
297 ent = vfs_s_automake (me, root, path, flags);
298 if (!ent)
300 me->verrno = ENOENT;
301 goto cleanup;
303 path += pseg;
304 /* here we must follow leading directories always;
305 only the actual file is optional */
306 ent = vfs_s_resolve_symlink (me, ent, strchr (path, PATH_SEP) ? LINK_FOLLOW : follow);
307 if (!ent)
308 goto cleanup;
309 root = ent->ino;
311 cleanup:
312 g_free (pathref);
313 return NULL;
316 static void
317 split_dir_name (struct vfs_class *me, char *path, char **dir, char **name, char **save)
319 char *s;
321 (void) me;
323 s = strrchr (path, PATH_SEP);
324 if (s == NULL)
326 *save = NULL;
327 *name = path;
328 *dir = path + strlen (path); /* an empty string */
330 else
332 *save = s;
333 *dir = path;
334 *s++ = '\0';
335 *name = s;
339 static struct vfs_s_entry *
340 vfs_s_find_entry_linear (struct vfs_class *me, struct vfs_s_inode *root,
341 const char *a_path, int follow, int flags)
343 struct vfs_s_entry *ent = NULL;
344 char *const path = g_strdup (a_path);
345 struct vfs_s_entry *retval = NULL;
347 if (root->super->root != root)
348 vfs_die ("We have to use _real_ root. Always. Sorry.");
350 /* canonicalize as well, but don't remove '../' from path */
351 custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
353 if (!(flags & FL_DIR))
355 char *dirname, *name, *save;
356 struct vfs_s_inode *ino;
357 split_dir_name (me, path, &dirname, &name, &save);
358 ino = vfs_s_find_inode (me, root->super, dirname, follow, flags | FL_DIR);
359 if (save)
360 *save = PATH_SEP;
361 retval = vfs_s_find_entry_tree (me, ino, name, follow, flags);
362 g_free (path);
363 return retval;
366 for (ent = root->subdir; ent != NULL; ent = ent->next)
367 if (!strcmp (ent->name, path))
368 break;
370 if (ent && (!(MEDATA->dir_uptodate) (me, ent->ino)))
372 #if 1
373 print_vfs_message (_("Directory cache expired for %s"), path);
374 #endif
375 vfs_s_free_entry (me, ent);
376 ent = NULL;
379 if (!ent)
381 struct vfs_s_inode *ino;
383 ino = vfs_s_new_inode (me, root->super, vfs_s_default_stat (me, S_IFDIR | 0755));
384 ent = vfs_s_new_entry (me, path, ino);
385 if ((MEDATA->dir_load) (me, ino, path) == -1)
387 vfs_s_free_entry (me, ent);
388 g_free (path);
389 return NULL;
391 vfs_s_insert_entry (me, root, ent);
393 for (ent = root->subdir; ent != NULL; ent = ent->next)
394 if (!strcmp (ent->name, path))
395 break;
397 if (!ent)
398 vfs_die ("find_linear: success but directory is not there\n");
400 #if 0
401 if (!vfs_s_resolve_symlink (me, ent, follow))
403 g_free (path);
404 return NULL;
406 #endif
407 g_free (path);
408 return ent;
411 struct vfs_s_inode *
412 vfs_s_find_inode (struct vfs_class *me, const struct vfs_s_super *super,
413 const char *path, int follow, int flags)
415 struct vfs_s_entry *ent;
417 if (((MEDATA->flags & VFS_S_REMOTE) == 0) && (*path == '\0'))
418 return super->root;
420 ent = (MEDATA->find_entry) (me, super->root, path, follow, flags);
421 return (ent != NULL) ? ent->ino : NULL;
424 /* Ook, these were functions around directory entries / inodes */
425 /* -------------------------------- superblock games -------------------------- */
427 static struct vfs_s_super *
428 vfs_s_new_super (struct vfs_class *me)
430 struct vfs_s_super *super;
432 super = g_new0 (struct vfs_s_super, 1);
433 super->me = me;
434 return super;
437 static void
438 vfs_s_insert_super (struct vfs_class *me, struct vfs_s_super *super)
440 super->next = MEDATA->supers;
441 super->prevp = &MEDATA->supers;
443 if (MEDATA->supers != NULL)
444 MEDATA->supers->prevp = &super->next;
445 MEDATA->supers = super;
448 static void
449 vfs_s_free_super (struct vfs_class *me, struct vfs_s_super *super)
451 if (super->root)
453 vfs_s_free_inode (me, super->root);
454 super->root = NULL;
457 #if 0
458 /* FIXME: We currently leak small ammount of memory, sometimes. Fix it if you can. */
459 if (super->ino_usage)
460 message (D_ERROR, "Direntry warning",
461 "Super ino_usage is %d, memory leak", super->ino_usage);
463 if (super->want_stale)
464 message (D_ERROR, "Direntry warning", "%s", "Super has want_stale set");
465 #endif
467 if (super->prevp)
469 *super->prevp = super->next;
470 if (super->next)
471 super->next->prevp = super->prevp;
474 CALL (free_archive) (me, super);
475 g_free (super->name);
476 g_free (super);
481 * Dissect the path and create corresponding superblock. Note that inname
482 * can be changed and the result may point inside the original string.
484 const char *
485 vfs_s_get_path_mangle (struct vfs_class *me, char *inname, struct vfs_s_super **archive, int flags)
487 const char *retval;
488 char *local, *op;
489 const char *archive_name;
490 int result = -1;
491 struct vfs_s_super *super;
492 void *cookie = NULL;
494 archive_name = inname;
495 vfs_split (inname, &local, &op);
496 retval = (local != NULL) ? local : "";
498 if (MEDATA->archive_check != NULL)
500 cookie = MEDATA->archive_check (me, archive_name, op);
501 if (cookie == NULL)
502 return NULL;
505 for (super = MEDATA->supers; super != NULL; super = super->next)
507 /* 0 == other, 1 == same, return it, 2 == other but stop scanning */
508 int i = MEDATA->archive_same (me, super, archive_name, op, cookie);
509 if (i != 0)
511 if (i == 1)
512 goto return_success;
513 else
514 break;
518 if (flags & FL_NO_OPEN)
519 ERRNOR (EIO, NULL);
521 super = vfs_s_new_super (me);
522 result = MEDATA->open_archive (me, super, archive_name, op);
523 if (result == -1)
525 vfs_s_free_super (me, super);
526 ERRNOR (EIO, NULL);
528 if (!super->name)
529 vfs_die ("You have to fill name\n");
530 if (!super->root)
531 vfs_die ("You have to fill root inode\n");
533 vfs_s_insert_super (me, super);
534 vfs_stamp_create (me, super);
536 return_success:
537 *archive = super;
538 return retval;
543 * Dissect the path and create corresponding superblock.
544 * The result should be freed.
546 static char *
547 vfs_s_get_path (struct vfs_class *me, const char *inname, struct vfs_s_super **archive, int flags)
549 char *buf, *retval;
551 buf = g_strdup (inname);
552 retval = g_strdup (vfs_s_get_path_mangle (me, buf, archive, flags));
553 g_free (buf);
554 return retval;
557 void
558 vfs_s_invalidate (struct vfs_class *me, struct vfs_s_super *super)
560 if (!super->want_stale)
562 vfs_s_free_inode (me, super->root);
563 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
567 char *
568 vfs_s_fullpath (struct vfs_class *me, struct vfs_s_inode *ino)
570 if (!ino->ent)
571 ERRNOR (EAGAIN, NULL);
573 if (!(MEDATA->flags & VFS_S_REMOTE))
575 /* archives */
576 char *newpath;
577 char *path = g_strdup (ino->ent->name);
578 while (1)
580 ino = ino->ent->dir;
581 if (ino == ino->super->root)
582 break;
583 newpath = g_strconcat (ino->ent->name, "/", path, (char *) NULL);
584 g_free (path);
585 path = newpath;
587 return path;
590 /* remote systems */
591 if ((!ino->ent->dir) || (!ino->ent->dir->ent))
592 return g_strdup (ino->ent->name);
594 return g_strconcat (ino->ent->dir->ent->name, PATH_SEP_STR, ino->ent->name, (char *) NULL);
597 /* Support of archives */
598 /* ------------------------ readdir & friends ----------------------------- */
600 static struct vfs_s_inode *
601 vfs_s_inode_from_path (struct vfs_class *me, const char *name, int flags)
603 struct vfs_s_super *super;
604 struct vfs_s_inode *ino;
605 char *q;
607 if (!(q = vfs_s_get_path (me, name, &super, 0)))
608 return NULL;
610 ino =
611 vfs_s_find_inode (me, super, q,
612 flags & FL_FOLLOW ? LINK_FOLLOW : LINK_NO_FOLLOW, flags & ~FL_FOLLOW);
613 if ((!ino) && (!*q))
614 /* We are asking about / directory of ftp server: assume it exists */
615 ino =
616 vfs_s_find_inode (me, super, q,
617 flags & FL_FOLLOW ? LINK_FOLLOW :
618 LINK_NO_FOLLOW, FL_DIR | (flags & ~FL_FOLLOW));
619 g_free (q);
620 return ino;
623 struct dirhandle
625 struct vfs_s_entry *cur;
626 struct vfs_s_inode *dir;
629 static void *
630 vfs_s_opendir (struct vfs_class *me, const char *dirname)
632 struct vfs_s_inode *dir;
633 struct dirhandle *info;
635 dir = vfs_s_inode_from_path (me, dirname, FL_DIR | FL_FOLLOW);
636 if (!dir)
637 return NULL;
638 if (!S_ISDIR (dir->st.st_mode))
639 ERRNOR (ENOTDIR, NULL);
641 dir->st.st_nlink++;
642 #if 0
643 if (!dir->subdir) /* This can actually happen if we allow empty directories */
644 ERRNOR (EAGAIN, NULL);
645 #endif
646 info = g_new (struct dirhandle, 1);
647 info->cur = dir->subdir;
648 info->dir = dir;
650 return info;
653 static void *
654 vfs_s_readdir (void *data)
656 static union vfs_dirent dir;
657 struct dirhandle *info = (struct dirhandle *) data;
659 if (!(info->cur))
660 return NULL;
662 if (info->cur->name)
664 g_strlcpy (dir.dent.d_name, info->cur->name, MC_MAXPATHLEN);
666 else
668 vfs_die ("Null in structure-cannot happen");
671 compute_namelen (&dir.dent);
672 info->cur = info->cur->next;
674 return (void *) &dir;
677 static int
678 vfs_s_closedir (void *data)
680 struct dirhandle *info = (struct dirhandle *) data;
681 struct vfs_s_inode *dir = info->dir;
683 vfs_s_free_inode (dir->super->me, dir);
684 g_free (data);
685 return 0;
688 static int
689 vfs_s_chdir (struct vfs_class *me, const char *path)
691 void *data;
693 data = vfs_s_opendir (me, path);
694 if (data == NULL)
695 return -1;
696 vfs_s_closedir (data);
697 return 0;
700 /* --------------------------- stat and friends ---------------------------- */
702 static int
703 vfs_s_internal_stat (struct vfs_class *me, const char *path, struct stat *buf, int flag)
705 struct vfs_s_inode *ino;
707 ino = vfs_s_inode_from_path (me, path, flag);
708 if (ino == NULL)
709 return -1;
710 *buf = ino->st;
711 return 0;
714 static int
715 vfs_s_stat (struct vfs_class *me, const char *path, struct stat *buf)
717 return vfs_s_internal_stat (me, path, buf, FL_FOLLOW);
720 static int
721 vfs_s_lstat (struct vfs_class *me, const char *path, struct stat *buf)
723 return vfs_s_internal_stat (me, path, buf, FL_NONE);
726 static int
727 vfs_s_fstat (void *fh, struct stat *buf)
729 *buf = FH->ino->st;
730 return 0;
733 static int
734 vfs_s_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
736 struct vfs_s_inode *ino;
737 size_t len;
739 ino = vfs_s_inode_from_path (me, path, 0);
740 if (!ino)
741 return -1;
743 if (!S_ISLNK (ino->st.st_mode))
744 ERRNOR (EINVAL, -1);
746 if (ino->linkname == NULL)
747 ERRNOR (EFAULT, -1);
749 len = strlen (ino->linkname);
750 if (size < len)
751 len = size;
752 /* readlink() does not append a NUL character to buf */
753 memcpy (buf, ino->linkname, len);
754 return len;
757 void *
758 vfs_s_open (struct vfs_class *me, const char *file, int flags, mode_t mode)
760 int was_changed = 0;
761 struct vfs_s_fh *fh;
762 struct vfs_s_super *super;
763 char *q;
764 struct vfs_s_inode *ino;
766 q = vfs_s_get_path (me, file, &super, 0);
767 if (q == NULL)
768 return NULL;
769 ino = vfs_s_find_inode (me, super, q, LINK_FOLLOW, FL_NONE);
770 if (ino && ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)))
772 g_free (q);
773 ERRNOR (EEXIST, NULL);
775 if (!ino)
777 char *dirname, *name, *save;
778 struct vfs_s_entry *ent;
779 struct vfs_s_inode *dir;
780 int tmp_handle;
782 /* If the filesystem is read-only, disable file creation */
783 if (!(flags & O_CREAT) || !(me->write))
785 g_free (q);
786 return NULL;
789 split_dir_name (me, q, &dirname, &name, &save);
790 /* FIXME: check if vfs_s_find_inode returns NULL */
791 dir = vfs_s_find_inode (me, super, dirname, LINK_FOLLOW, FL_DIR);
792 if (save)
793 *save = PATH_SEP;
794 ent = vfs_s_generate_entry (me, name, dir, 0755);
795 ino = ent->ino;
796 vfs_s_insert_entry (me, dir, ent);
797 tmp_handle = vfs_mkstemps (&ino->localname, me->name, name);
798 if (tmp_handle == -1)
800 g_free (q);
801 return NULL;
803 close (tmp_handle);
804 was_changed = 1;
807 g_free (q);
809 if (S_ISDIR (ino->st.st_mode))
810 ERRNOR (EISDIR, NULL);
812 fh = g_new (struct vfs_s_fh, 1);
813 fh->pos = 0;
814 fh->ino = ino;
815 fh->handle = -1;
816 fh->changed = was_changed;
817 fh->linear = 0;
819 if (IS_LINEAR (flags))
821 if (MEDATA->linear_start)
823 print_vfs_message (_("Starting linear transfer..."));
824 fh->linear = LS_LINEAR_PREOPEN;
827 else if ((MEDATA->fh_open) && (MEDATA->fh_open (me, fh, flags, mode)))
829 g_free (fh);
830 return NULL;
833 if (fh->ino->localname)
835 fh->handle = open (fh->ino->localname, NO_LINEAR (flags), mode);
836 if (fh->handle == -1)
838 g_free (fh);
839 ERRNOR (errno, NULL);
843 /* i.e. we had no open files and now we have one */
844 vfs_rmstamp (me, (vfsid) super);
845 super->fd_usage++;
846 fh->ino->st.st_nlink++;
847 return fh;
850 static ssize_t
851 vfs_s_read (void *fh, char *buffer, size_t count)
853 int n;
854 struct vfs_class *me = FH_SUPER->me;
856 if (FH->linear == LS_LINEAR_PREOPEN)
858 if (!MEDATA->linear_start (me, FH, FH->pos))
859 return -1;
862 if (FH->linear == LS_LINEAR_CLOSED)
863 vfs_die ("linear_start() did not set linear_state!");
865 if (FH->linear == LS_LINEAR_OPEN)
866 return MEDATA->linear_read (me, FH, buffer, count);
868 if (FH->handle != -1)
870 n = read (FH->handle, buffer, count);
871 if (n < 0)
872 me->verrno = errno;
873 return n;
875 vfs_die ("vfs_s_read: This should not happen\n");
876 return -1;
879 static ssize_t
880 vfs_s_write (void *fh, const char *buffer, size_t count)
882 int n;
883 struct vfs_class *me = FH_SUPER->me;
885 if (FH->linear)
886 vfs_die ("no writing to linear files, please");
888 FH->changed = 1;
889 if (FH->handle != -1)
891 n = write (FH->handle, buffer, count);
892 if (n < 0)
893 me->verrno = errno;
894 return n;
896 vfs_die ("vfs_s_write: This should not happen\n");
897 return 0;
900 static off_t
901 vfs_s_lseek (void *fh, off_t offset, int whence)
903 off_t size = FH->ino->st.st_size;
905 if (FH->linear == LS_LINEAR_OPEN)
906 vfs_die ("cannot lseek() after linear_read!");
908 if (FH->handle != -1)
909 { /* If we have local file opened, we want to work with it */
910 off_t retval = lseek (FH->handle, offset, whence);
911 if (retval == -1)
912 FH->ino->super->me->verrno = errno;
913 return retval;
916 switch (whence)
918 case SEEK_CUR:
919 offset += FH->pos;
920 break;
921 case SEEK_END:
922 offset += size;
923 break;
925 if (offset < 0)
926 FH->pos = 0;
927 else if (offset < size)
928 FH->pos = offset;
929 else
930 FH->pos = size;
931 return FH->pos;
934 static int
935 vfs_s_close (void *fh)
937 int res = 0;
938 struct vfs_class *me = FH_SUPER->me;
940 FH_SUPER->fd_usage--;
941 if (!FH_SUPER->fd_usage)
942 vfs_stamp_create (me, FH_SUPER);
944 if (FH->linear == LS_LINEAR_OPEN)
945 MEDATA->linear_close (me, fh);
946 if (MEDATA->fh_close)
947 res = MEDATA->fh_close (me, fh);
948 if (FH->changed && MEDATA->file_store)
950 char *s = vfs_s_fullpath (me, FH->ino);
951 if (!s)
952 res = -1;
953 else
955 res = MEDATA->file_store (me, fh, s, FH->ino->localname);
956 g_free (s);
958 vfs_s_invalidate (me, FH_SUPER);
960 if (FH->handle != -1)
961 close (FH->handle);
963 vfs_s_free_inode (me, FH->ino);
964 g_free (fh);
965 return res;
968 static void
969 vfs_s_print_stats (const char *fs_name, const char *action,
970 const char *file_name, off_t have, off_t need)
972 static const char *i18n_percent_transf_format = NULL;
973 static const char *i18n_transf_format = NULL;
975 if (i18n_percent_transf_format == NULL)
977 i18n_percent_transf_format = _("%s: %s: %s %3d%% (%ju bytes transferred)");
978 i18n_transf_format = _("%s: %s: %s %ju bytes transferred");
981 if (need)
982 print_vfs_message (i18n_percent_transf_format, fs_name, action,
983 file_name, (int) ((double) have * 100 / need), have);
984 else
985 print_vfs_message (i18n_transf_format, fs_name, action, file_name, have);
989 vfs_s_retrieve_file (struct vfs_class *me, struct vfs_s_inode *ino)
991 /* If you want reget, you'll have to open file with O_LINEAR */
992 off_t total = 0;
993 char buffer[8192];
994 int handle, n;
995 off_t stat_size = ino->st.st_size;
996 struct vfs_s_fh fh;
998 memset (&fh, 0, sizeof (fh));
1000 fh.ino = ino;
1001 fh.handle = -1;
1003 handle = vfs_mkstemps (&ino->localname, me->name, ino->ent->name);
1004 if (handle == -1)
1006 me->verrno = errno;
1007 goto error_4;
1010 if (!MEDATA->linear_start (me, &fh, 0))
1011 goto error_3;
1013 /* Clear the interrupt status */
1014 tty_got_interrupt ();
1015 tty_enable_interrupt_key ();
1017 while ((n = MEDATA->linear_read (me, &fh, buffer, sizeof (buffer))))
1019 int t;
1020 if (n < 0)
1021 goto error_1;
1023 total += n;
1024 vfs_s_print_stats (me->name, _("Getting file"), ino->ent->name, total, stat_size);
1026 if (tty_got_interrupt ())
1027 goto error_1;
1029 t = write (handle, buffer, n);
1030 if (t != n)
1032 if (t == -1)
1033 me->verrno = errno;
1034 goto error_1;
1037 MEDATA->linear_close (me, &fh);
1038 close (handle);
1040 tty_disable_interrupt_key ();
1041 return 0;
1043 error_1:
1044 MEDATA->linear_close (me, &fh);
1045 error_3:
1046 tty_disable_interrupt_key ();
1047 close (handle);
1048 unlink (ino->localname);
1049 error_4:
1050 g_free (ino->localname);
1051 ino->localname = NULL;
1052 return -1;
1055 /* ------------------------------- mc support ---------------------------- */
1057 static void
1058 vfs_s_fill_names (struct vfs_class *me, fill_names_f func)
1060 struct vfs_s_super *a = MEDATA->supers;
1061 char *name;
1063 while (a)
1065 name = g_strconcat (a->name, "#", me->prefix, "/",
1066 /* a->current_dir->name, */ (char *) NULL);
1067 (*func) (name);
1068 g_free (name);
1069 a = a->next;
1073 static int
1074 vfs_s_ferrno (struct vfs_class *me)
1076 return me->verrno;
1080 * Get local copy of the given file. We reuse the existing file cache
1081 * for remote filesystems. Archives use standard VFS facilities.
1083 static char *
1084 vfs_s_getlocalcopy (struct vfs_class *me, const char *path)
1086 struct vfs_s_fh *fh;
1087 char *local = NULL;
1089 fh = vfs_s_open (me, path, O_RDONLY, 0);
1091 if (fh != NULL)
1093 if ((fh->ino != NULL) && (fh->ino->localname != NULL))
1094 local = g_strdup (fh->ino->localname);
1096 vfs_s_close (fh);
1099 return local;
1103 * Return the local copy. Since we are using our cache, we do nothing -
1104 * the cache will be removed when the archive is closed.
1106 static int
1107 vfs_s_ungetlocalcopy (struct vfs_class *me, const char *path, const char *local, int has_changed)
1109 (void) me;
1110 (void) path;
1111 (void) local;
1112 (void) has_changed;
1113 return 0;
1116 static int
1117 vfs_s_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1119 switch (ctlop)
1121 case VFS_SETCTL_STALE_DATA:
1123 struct vfs_s_inode *ino = vfs_s_inode_from_path (me, path, 0);
1125 if (ino == NULL)
1126 return 0;
1127 if (arg)
1128 ino->super->want_stale = 1;
1129 else
1131 ino->super->want_stale = 0;
1132 vfs_s_invalidate (me, ino->super);
1134 return 1;
1136 case VFS_SETCTL_LOGFILE:
1137 MEDATA->logfile = fopen ((char *) arg, "w");
1138 return 1;
1139 case VFS_SETCTL_FLUSH:
1140 MEDATA->flush = 1;
1141 return 1;
1143 return 0;
1147 /* ----------------------------- Stamping support -------------------------- */
1149 static vfsid
1150 vfs_s_getid (struct vfs_class *me, const char *path)
1152 struct vfs_s_super *archive = NULL;
1153 char *p;
1155 p = vfs_s_get_path (me, path, &archive, FL_NO_OPEN);
1156 if (p == NULL)
1157 return NULL;
1158 g_free (p);
1159 return (vfsid) archive;
1162 static int
1163 vfs_s_nothingisopen (vfsid id)
1165 (void) id;
1166 /* Our data structures should survive free of superblock at any time */
1167 return 1;
1170 static void
1171 vfs_s_free (vfsid id)
1173 vfs_s_free_super (((struct vfs_s_super *) id)->me, (struct vfs_s_super *) id);
1176 static int
1177 vfs_s_dir_uptodate (struct vfs_class *me, struct vfs_s_inode *ino)
1179 struct timeval tim;
1181 if (MEDATA->flush)
1183 MEDATA->flush = 0;
1184 return 0;
1187 gettimeofday (&tim, NULL);
1188 if (tim.tv_sec < ino->timestamp.tv_sec)
1189 return 1;
1190 return 0;
1193 /* Initialize one of our subclasses - fill common functions */
1194 void
1195 vfs_s_init_class (struct vfs_class *vclass, struct vfs_s_subclass *sub)
1197 vclass->data = sub;
1198 vclass->fill_names = vfs_s_fill_names;
1199 vclass->open = vfs_s_open;
1200 vclass->close = vfs_s_close;
1201 vclass->read = vfs_s_read;
1202 if (!(sub->flags & VFS_S_READONLY))
1204 vclass->write = vfs_s_write;
1206 vclass->opendir = vfs_s_opendir;
1207 vclass->readdir = vfs_s_readdir;
1208 vclass->closedir = vfs_s_closedir;
1209 vclass->stat = vfs_s_stat;
1210 vclass->lstat = vfs_s_lstat;
1211 vclass->fstat = vfs_s_fstat;
1212 vclass->readlink = vfs_s_readlink;
1213 vclass->chdir = vfs_s_chdir;
1214 vclass->ferrno = vfs_s_ferrno;
1215 vclass->lseek = vfs_s_lseek;
1216 vclass->getid = vfs_s_getid;
1217 vclass->nothingisopen = vfs_s_nothingisopen;
1218 vclass->free = vfs_s_free;
1219 if (sub->flags & VFS_S_REMOTE)
1221 vclass->getlocalcopy = vfs_s_getlocalcopy;
1222 vclass->ungetlocalcopy = vfs_s_ungetlocalcopy;
1223 sub->find_entry = vfs_s_find_entry_linear;
1225 else
1227 sub->find_entry = vfs_s_find_entry_tree;
1229 vclass->setctl = vfs_s_setctl;
1230 sub->dir_uptodate = vfs_s_dir_uptodate;
1233 /* Find VFS id for given directory name */
1234 vfsid
1235 vfs_getid (struct vfs_class *vclass, const char *dir)
1237 char *dir1;
1238 vfsid id = NULL;
1240 /* append slash if needed */
1241 dir1 = concat_dir_and_file (dir, "");
1242 if (vclass->getid)
1243 id = (*vclass->getid) (vclass, dir1);
1245 g_free (dir1);
1246 return id;
1249 /* ----------- Utility functions for networked filesystems -------------- */
1251 #ifdef ENABLE_VFS_NET
1253 vfs_s_select_on_two (int fd1, int fd2)
1255 fd_set set;
1256 struct timeval time_out;
1257 int v;
1258 int maxfd = (fd1 > fd2 ? fd1 : fd2) + 1;
1260 time_out.tv_sec = 1;
1261 time_out.tv_usec = 0;
1262 FD_ZERO (&set);
1263 FD_SET (fd1, &set);
1264 FD_SET (fd2, &set);
1265 v = select (maxfd, &set, 0, 0, &time_out);
1266 if (v <= 0)
1267 return v;
1268 if (FD_ISSET (fd1, &set))
1269 return 1;
1270 if (FD_ISSET (fd2, &set))
1271 return 2;
1272 return -1;
1276 vfs_s_get_line (struct vfs_class *me, int sock, char *buf, int buf_len, char term)
1278 FILE *logfile = MEDATA->logfile;
1279 int i;
1280 char c;
1282 for (i = 0; i < buf_len - 1; i++, buf++)
1284 if (read (sock, buf, sizeof (char)) <= 0)
1285 return 0;
1286 if (logfile)
1288 size_t ret1;
1289 int ret2;
1290 ret1 = fwrite (buf, 1, 1, logfile);
1291 ret2 = fflush (logfile);
1293 if (*buf == term)
1295 *buf = 0;
1296 return 1;
1300 /* Line is too long - terminate buffer and discard the rest of line */
1301 *buf = 0;
1302 while (read (sock, &c, sizeof (c)) > 0)
1304 if (logfile)
1306 size_t ret1;
1307 int ret2;
1308 ret1 = fwrite (&c, 1, 1, logfile);
1309 ret2 = fflush (logfile);
1311 if (c == '\n')
1312 return 1;
1314 return 0;
1318 vfs_s_get_line_interruptible (struct vfs_class *me, char *buffer, int size, int fd)
1320 int n;
1321 int i;
1323 (void) me;
1325 tty_enable_interrupt_key ();
1326 for (i = 0; i < size - 1; i++)
1328 n = read (fd, buffer + i, 1);
1329 tty_disable_interrupt_key ();
1330 if (n == -1 && errno == EINTR)
1332 buffer[i] = 0;
1333 return EINTR;
1335 if (n == 0)
1337 buffer[i] = 0;
1338 return 0;
1340 if (buffer[i] == '\n')
1342 buffer[i] = 0;
1343 return 1;
1346 buffer[size - 1] = 0;
1347 return 0;
1349 #endif /* ENABLE_VFS_NET */