patches for AIX with ncurses support
[midnight-commander.git] / lib / vfs / mc-vfs / direntry.c
blob8ecdfde8df2824f8fdc9a06e183435fdcce6bb84
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 */
43 #include "src/wtools.h" /* message() */
44 #include "src/main.h" /* print_vfs_message */
45 #include "vfs.h"
47 #include "utilvfs.h"
48 #include "vfs-impl.h"
49 #include "gc.h" /* vfs_rmstamp */
50 #include "xdirentry.h"
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){
106 while (ino->subdir){
107 vfs_s_free_entry (me, ino->subdir);
110 CALL (free_inode) (me, ino);
111 g_free (ino->linkname);
112 if (ino->localname){
113 unlink (ino->localname);
114 g_free(ino->localname);
116 total_inodes--;
117 ino->super->ino_usage--;
118 g_free(ino);
119 } else ino->st.st_nlink--;
122 void
123 vfs_s_free_entry (struct vfs_class *me, struct vfs_s_entry *ent)
125 if (ent->prevp){ /* It is possible that we are deleting freshly created entry */
126 *ent->prevp = ent->next;
127 if (ent->next)
128 ent->next->prevp = ent->prevp;
131 g_free (ent->name);
132 ent->name = NULL;
134 if (ent->ino){
135 ent->ino->ent = NULL;
136 vfs_s_free_inode (me, ent->ino);
137 ent->ino = NULL;
140 total_entries--;
141 g_free(ent);
144 void
145 vfs_s_insert_entry (struct vfs_class *me, struct vfs_s_inode *dir, struct vfs_s_entry *ent)
147 struct vfs_s_entry **ep;
149 (void) me;
151 for (ep = &dir->subdir; *ep != NULL; ep = &((*ep)->next))
153 ent->prevp = ep;
154 ent->next = NULL;
155 ent->dir = dir;
156 *ep = ent;
158 ent->ino->st.st_nlink++;
161 struct stat *
162 vfs_s_default_stat (struct vfs_class *me, mode_t mode)
164 static struct stat st;
165 int myumask;
167 (void) me;
169 myumask = umask (022);
170 umask (myumask);
171 mode &= ~myumask;
173 st.st_mode = mode;
174 st.st_ino = 0;
175 st.st_dev = 0;
176 st.st_rdev = 0;
177 st.st_uid = getuid ();
178 st.st_gid = getgid ();
179 st.st_size = 0;
180 st.st_mtime = st.st_atime = st.st_ctime = time (NULL);
182 return &st;
185 struct vfs_s_entry *
186 vfs_s_generate_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *parent, mode_t mode)
188 struct vfs_s_inode *inode;
189 struct stat *st;
191 st = vfs_s_default_stat (me, mode);
192 inode = vfs_s_new_inode (me, parent->super, st);
194 return vfs_s_new_entry (me, name, inode);
197 /* We were asked to create entries automagically */
198 static struct vfs_s_entry *
199 vfs_s_automake (struct vfs_class *me, struct vfs_s_inode *dir, char *path, int flags)
201 struct vfs_s_entry *res;
202 char *sep = strchr (path, PATH_SEP);
204 if (sep)
205 *sep = 0;
206 res = vfs_s_generate_entry (me, path, dir, flags & FL_MKDIR ? (0777 | S_IFDIR) : 0777);
207 vfs_s_insert_entry (me, dir, res);
209 if (sep)
210 *sep = PATH_SEP;
212 return res;
215 /* If the entry is a symlink, find the entry for its target */
216 static struct vfs_s_entry *
217 vfs_s_resolve_symlink (struct vfs_class *me, struct vfs_s_entry *entry,
218 int follow)
220 char *linkname;
221 char *fullname = NULL;
222 struct vfs_s_entry *target;
224 if (follow == LINK_NO_FOLLOW)
225 return entry;
226 if (follow == 0)
227 ERRNOR (ELOOP, NULL);
228 if (!entry)
229 ERRNOR (ENOENT, NULL);
230 if (!S_ISLNK (entry->ino->st.st_mode))
231 return entry;
233 linkname = entry->ino->linkname;
234 if (linkname == NULL)
235 ERRNOR (EFAULT, NULL);
237 /* make full path from relative */
238 if (*linkname != PATH_SEP) {
239 char *fullpath = vfs_s_fullpath (me, entry->dir);
240 if (fullpath) {
241 fullname = g_strconcat (fullpath, "/", linkname, (char *) NULL);
242 linkname = fullname;
243 g_free (fullpath);
247 target =
248 (MEDATA->find_entry) (me, entry->dir->super->root, linkname,
249 follow - 1, 0);
250 g_free (fullname);
251 return target;
255 * Follow > 0: follow links, serves as loop protect,
256 * == -1: do not follow links
258 static struct vfs_s_entry *
259 vfs_s_find_entry_tree (struct vfs_class *me, struct vfs_s_inode *root,
260 const char *a_path, int follow, int flags)
262 size_t pseg;
263 struct vfs_s_entry *ent = NULL;
264 char * const pathref = g_strdup (a_path);
265 char *path = pathref;
267 /* canonicalize as well, but don't remove '../' from path */
268 custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
270 while (root) {
271 while (*path == PATH_SEP) /* Strip leading '/' */
272 path++;
274 if (!path[0]) {
275 g_free (pathref);
276 return ent;
279 for (pseg = 0; path[pseg] && path[pseg] != PATH_SEP; pseg++);
281 for (ent = root->subdir; ent != NULL; ent = ent->next)
282 if (strlen (ent->name) == pseg
283 && (!strncmp (ent->name, path, pseg)))
284 /* FOUND! */
285 break;
287 if (!ent && (flags & (FL_MKFILE | FL_MKDIR)))
288 ent = vfs_s_automake (me, root, path, flags);
289 if (!ent) {
290 me->verrno = ENOENT;
291 goto cleanup;
293 path += pseg;
294 /* here we must follow leading directories always;
295 only the actual file is optional */
296 ent =
297 vfs_s_resolve_symlink (me, ent,
298 strchr (path,
299 PATH_SEP) ? LINK_FOLLOW :
300 follow);
301 if (!ent)
302 goto cleanup;
303 root = ent->ino;
305 cleanup:
306 g_free (pathref);
307 return NULL;
310 static void
311 split_dir_name (struct vfs_class *me, char *path, char **dir, char **name, char **save)
313 char *s;
315 (void) me;
317 s = strrchr (path, PATH_SEP);
318 if (s == NULL) {
319 *save = NULL;
320 *name = path;
321 *dir = path + strlen(path); /* an empty string */
322 } else {
323 *save = s;
324 *dir = path;
325 *s++ = '\0';
326 *name = s;
330 static struct vfs_s_entry *
331 vfs_s_find_entry_linear (struct vfs_class *me, struct vfs_s_inode *root,
332 const char *a_path, int follow, int flags)
334 struct vfs_s_entry *ent = NULL;
335 char * const path = g_strdup (a_path);
336 struct vfs_s_entry *retval = NULL;
338 if (root->super->root != root)
339 vfs_die ("We have to use _real_ root. Always. Sorry.");
341 /* canonicalize as well, but don't remove '../' from path */
342 custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
344 if (!(flags & FL_DIR)) {
345 char *dirname, *name, *save;
346 struct vfs_s_inode *ino;
347 split_dir_name (me, path, &dirname, &name, &save);
348 ino =
349 vfs_s_find_inode (me, root->super, dirname, follow,
350 flags | FL_DIR);
351 if (save)
352 *save = PATH_SEP;
353 retval = vfs_s_find_entry_tree (me, ino, name, follow, flags);
354 g_free (path);
355 return retval;
358 for (ent = root->subdir; ent != NULL; ent = ent->next)
359 if (!strcmp (ent->name, path))
360 break;
362 if (ent && (!(MEDATA->dir_uptodate) (me, ent->ino))) {
363 #if 1
364 print_vfs_message (_("Directory cache expired for %s"), path);
365 #endif
366 vfs_s_free_entry (me, ent);
367 ent = NULL;
370 if (!ent) {
371 struct vfs_s_inode *ino;
373 ino =
374 vfs_s_new_inode (me, root->super,
375 vfs_s_default_stat (me, S_IFDIR | 0755));
376 ent = vfs_s_new_entry (me, path, ino);
377 if ((MEDATA->dir_load) (me, ino, path) == -1) {
378 vfs_s_free_entry (me, ent);
379 g_free (path);
380 return NULL;
382 vfs_s_insert_entry (me, root, ent);
384 for (ent = root->subdir; ent != NULL; ent = ent->next)
385 if (!strcmp (ent->name, path))
386 break;
388 if (!ent)
389 vfs_die ("find_linear: success but directory is not there\n");
391 #if 0
392 if (!vfs_s_resolve_symlink (me, ent, follow)) {
393 g_free (path);
394 return NULL;
396 #endif
397 g_free (path);
398 return ent;
401 struct vfs_s_inode *
402 vfs_s_find_inode (struct vfs_class *me, const struct vfs_s_super *super,
403 const char *path, int follow, int flags)
405 struct vfs_s_entry *ent;
407 if (((MEDATA->flags & VFS_S_REMOTE) == 0) && (*path == '\0'))
408 return super->root;
410 ent = (MEDATA->find_entry) (me, super->root, path, follow, flags);
411 return (ent != NULL) ? ent->ino : NULL;
414 /* Ook, these were functions around directory entries / inodes */
415 /* -------------------------------- superblock games -------------------------- */
417 static struct vfs_s_super *
418 vfs_s_new_super (struct vfs_class *me)
420 struct vfs_s_super *super;
422 super = g_new0 (struct vfs_s_super, 1);
423 super->me = me;
424 return super;
427 static void
428 vfs_s_insert_super (struct vfs_class *me, struct vfs_s_super *super)
430 super->next = MEDATA->supers;
431 super->prevp = &MEDATA->supers;
433 if (MEDATA->supers != NULL)
434 MEDATA->supers->prevp = &super->next;
435 MEDATA->supers = super;
438 static void
439 vfs_s_free_super (struct vfs_class *me, struct vfs_s_super *super)
441 if (super->root){
442 vfs_s_free_inode (me, super->root);
443 super->root = NULL;
446 #if 0
447 /* FIXME: We currently leak small ammount of memory, sometimes. Fix it if you can. */
448 if (super->ino_usage)
449 message (D_ERROR, " Direntry warning ",
450 "Super ino_usage is %d, memory leak",
451 super->ino_usage);
453 if (super->want_stale)
454 message (D_ERROR, " Direntry warning ", "Super has want_stale set");
455 #endif
457 if (super->prevp){
458 *super->prevp = super->next;
459 if (super->next)
460 super->next->prevp = super->prevp;
463 CALL (free_archive) (me, super);
464 g_free (super->name);
465 g_free(super);
470 * Dissect the path and create corresponding superblock. Note that inname
471 * can be changed and the result may point inside the original string.
473 const char *
474 vfs_s_get_path_mangle (struct vfs_class *me, char *inname,
475 struct vfs_s_super **archive, int flags)
477 const char *retval;
478 char *local, *op;
479 const char *archive_name;
480 int result = -1;
481 struct vfs_s_super *super;
482 void *cookie = NULL;
484 archive_name = inname;
485 vfs_split (inname, &local, &op);
486 retval = (local) ? local : "";
488 if (MEDATA->archive_check)
489 if (!(cookie = MEDATA->archive_check (me, archive_name, op)))
490 return NULL;
492 for (super = MEDATA->supers; super != NULL; super = super->next) {
493 /* 0 == other, 1 == same, return it, 2 == other but stop scanning */
494 int i = MEDATA->archive_same (me, super, archive_name, op, cookie);
495 if (i != 0) {
496 if (i == 1)
497 goto return_success;
498 else
499 break;
503 if (flags & FL_NO_OPEN)
504 ERRNOR (EIO, NULL);
506 super = vfs_s_new_super (me);
507 result = MEDATA->open_archive (me, super, archive_name, op);
508 if (result == -1) {
509 vfs_s_free_super (me, super);
510 ERRNOR (EIO, NULL);
512 if (!super->name)
513 vfs_die ("You have to fill name\n");
514 if (!super->root)
515 vfs_die ("You have to fill root inode\n");
517 vfs_s_insert_super (me, super);
518 vfs_stamp_create (me, super);
520 return_success:
521 *archive = super;
522 return retval;
527 * Dissect the path and create corresponding superblock.
528 * The result should be freed.
530 static char *
531 vfs_s_get_path (struct vfs_class *me, const char *inname,
532 struct vfs_s_super **archive, int flags)
534 char *buf, *retval;
536 buf = g_strdup (inname);
537 retval = g_strdup (vfs_s_get_path_mangle (me, buf, archive, flags));
538 g_free (buf);
539 return retval;
542 void
543 vfs_s_invalidate (struct vfs_class *me, struct vfs_s_super *super)
545 if (!super->want_stale){
546 vfs_s_free_inode (me, super->root);
547 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
551 char *
552 vfs_s_fullpath (struct vfs_class *me, struct vfs_s_inode *ino)
554 if (!ino->ent)
555 ERRNOR (EAGAIN, NULL);
557 if (!(MEDATA->flags & VFS_S_REMOTE)) {
558 /* archives */
559 char *newpath;
560 char *path = g_strdup (ino->ent->name);
561 while (1) {
562 ino = ino->ent->dir;
563 if (ino == ino->super->root)
564 break;
565 newpath = g_strconcat (ino->ent->name, "/", path, (char *) NULL);
566 g_free (path);
567 path = newpath;
569 return path;
572 /* remote systems */
573 if ((!ino->ent->dir) || (!ino->ent->dir->ent))
574 return g_strdup (ino->ent->name);
576 return g_strconcat (ino->ent->dir->ent->name, PATH_SEP_STR,
577 ino->ent->name, (char *) NULL);
580 /* Support of archives */
581 /* ------------------------ readdir & friends ----------------------------- */
583 static struct vfs_s_inode *
584 vfs_s_inode_from_path (struct vfs_class *me, const char *name, int flags)
586 struct vfs_s_super *super;
587 struct vfs_s_inode *ino;
588 char *q;
590 if (!(q = vfs_s_get_path (me, name, &super, 0)))
591 return NULL;
593 ino =
594 vfs_s_find_inode (me, super, q,
595 flags & FL_FOLLOW ? LINK_FOLLOW : LINK_NO_FOLLOW,
596 flags & ~FL_FOLLOW);
597 if ((!ino) && (!*q))
598 /* We are asking about / directory of ftp server: assume it exists */
599 ino =
600 vfs_s_find_inode (me, super, q,
601 flags & FL_FOLLOW ? LINK_FOLLOW :
602 LINK_NO_FOLLOW,
603 FL_DIR | (flags & ~FL_FOLLOW));
604 g_free (q);
605 return ino;
608 struct dirhandle {
609 struct vfs_s_entry *cur;
610 struct vfs_s_inode *dir;
613 static void *
614 vfs_s_opendir (struct vfs_class *me, const char *dirname)
616 struct vfs_s_inode *dir;
617 struct dirhandle *info;
619 dir = vfs_s_inode_from_path (me, dirname, FL_DIR | FL_FOLLOW);
620 if (!dir)
621 return NULL;
622 if (!S_ISDIR (dir->st.st_mode))
623 ERRNOR (ENOTDIR, NULL);
625 dir->st.st_nlink++;
626 #if 0
627 if (!dir->subdir) /* This can actually happen if we allow empty directories */
628 ERRNOR (EAGAIN, NULL);
629 #endif
630 info = g_new (struct dirhandle, 1);
631 info->cur = dir->subdir;
632 info->dir = dir;
634 return info;
637 static void *
638 vfs_s_readdir(void *data)
640 static union vfs_dirent dir;
641 struct dirhandle *info = (struct dirhandle *) data;
643 if (!(info->cur))
644 return NULL;
646 if (info->cur->name) {
647 g_strlcpy (dir.dent.d_name, info->cur->name, MC_MAXPATHLEN);
648 } else {
649 vfs_die("Null in structure-cannot happen");
652 compute_namelen(&dir.dent);
653 info->cur = info->cur->next;
655 return (void *) &dir;
658 static int
659 vfs_s_closedir (void *data)
661 struct dirhandle *info = (struct dirhandle *) data;
662 struct vfs_s_inode *dir = info->dir;
664 vfs_s_free_inode (dir->super->me, dir);
665 g_free (data);
666 return 0;
669 static int
670 vfs_s_chdir (struct vfs_class *me, const char *path)
672 void *data;
673 if (!(data = vfs_s_opendir (me, path)))
674 return -1;
675 vfs_s_closedir (data);
676 return 0;
679 /* --------------------------- stat and friends ---------------------------- */
681 static int
682 vfs_s_internal_stat (struct vfs_class *me, const char *path, struct stat *buf, int flag)
684 struct vfs_s_inode *ino;
686 if (!(ino = vfs_s_inode_from_path (me, path, flag)))
687 return -1;
688 *buf = ino->st;
689 return 0;
692 static int
693 vfs_s_stat (struct vfs_class *me, const char *path, struct stat *buf)
695 return vfs_s_internal_stat (me, path, buf, FL_FOLLOW);
698 static int
699 vfs_s_lstat (struct vfs_class *me, const char *path, struct stat *buf)
701 return vfs_s_internal_stat (me, path, buf, FL_NONE);
704 static int
705 vfs_s_fstat (void *fh, struct stat *buf)
707 *buf = FH->ino->st;
708 return 0;
711 static int
712 vfs_s_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
714 struct vfs_s_inode *ino;
715 size_t len;
717 ino = vfs_s_inode_from_path (me, path, 0);
718 if (!ino)
719 return -1;
721 if (!S_ISLNK (ino->st.st_mode))
722 ERRNOR (EINVAL, -1);
724 if (ino->linkname == NULL)
725 ERRNOR (EFAULT, -1);
727 len = strlen (ino->linkname);
728 if (size < len)
729 len = size;
730 /* readlink() does not append a NUL character to buf */
731 memcpy (buf, ino->linkname, len);
732 return len;
735 void *
736 vfs_s_open (struct vfs_class *me, const char *file, int flags, int mode)
738 int was_changed = 0;
739 struct vfs_s_fh *fh;
740 struct vfs_s_super *super;
741 char *q;
742 struct vfs_s_inode *ino;
744 if ((q = vfs_s_get_path (me, file, &super, 0)) == NULL)
745 return NULL;
746 ino = vfs_s_find_inode (me, super, q, LINK_FOLLOW, FL_NONE);
747 if (ino && ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))) {
748 g_free (q);
749 ERRNOR (EEXIST, NULL);
751 if (!ino) {
752 char *dirname, *name, *save;
753 struct vfs_s_entry *ent;
754 struct vfs_s_inode *dir;
755 int tmp_handle;
757 /* If the filesystem is read-only, disable file creation */
758 if (!(flags & O_CREAT) || !(me->write)) {
759 g_free (q);
760 return NULL;
763 split_dir_name (me, q, &dirname, &name, &save);
764 /* FIXME: check if vfs_s_find_inode returns NULL */
765 dir = vfs_s_find_inode (me, super, dirname, LINK_FOLLOW, FL_DIR);
766 if (save)
767 *save = PATH_SEP;
768 ent = vfs_s_generate_entry (me, name, dir, 0755);
769 ino = ent->ino;
770 vfs_s_insert_entry (me, dir, ent);
771 tmp_handle = vfs_mkstemps (&ino->localname, me->name, name);
772 if (tmp_handle == -1) {
773 g_free (q);
774 return NULL;
776 close (tmp_handle);
777 was_changed = 1;
780 g_free (q);
782 if (S_ISDIR (ino->st.st_mode))
783 ERRNOR (EISDIR, NULL);
785 fh = g_new (struct vfs_s_fh, 1);
786 fh->pos = 0;
787 fh->ino = ino;
788 fh->handle = -1;
789 fh->changed = was_changed;
790 fh->linear = 0;
792 if (IS_LINEAR (flags)) {
793 if (MEDATA->linear_start) {
794 print_vfs_message (_("Starting linear transfer..."));
795 fh->linear = LS_LINEAR_PREOPEN;
797 } else if ((MEDATA->fh_open)
798 && (MEDATA->fh_open (me, fh, flags, mode))) {
799 g_free (fh);
800 return NULL;
803 if (fh->ino->localname) {
804 fh->handle = open (fh->ino->localname, NO_LINEAR (flags), mode);
805 if (fh->handle == -1) {
806 g_free (fh);
807 ERRNOR (errno, NULL);
811 /* i.e. we had no open files and now we have one */
812 vfs_rmstamp (me, (vfsid) super);
813 super->fd_usage++;
814 fh->ino->st.st_nlink++;
815 return fh;
818 static ssize_t
819 vfs_s_read (void *fh, char *buffer, int count)
821 int n;
822 struct vfs_class *me = FH_SUPER->me;
824 if (FH->linear == LS_LINEAR_PREOPEN) {
825 if (!MEDATA->linear_start (me, FH, FH->pos))
826 return -1;
829 if (FH->linear == LS_LINEAR_CLOSED)
830 vfs_die ("linear_start() did not set linear_state!");
832 if (FH->linear == LS_LINEAR_OPEN)
833 return MEDATA->linear_read (me, FH, buffer, count);
835 if (FH->handle != -1){
836 n = read (FH->handle, buffer, count);
837 if (n < 0)
838 me->verrno = errno;
839 return n;
841 vfs_die ("vfs_s_read: This should not happen\n");
842 return -1;
845 static ssize_t
846 vfs_s_write (void *fh, const char *buffer, int count)
848 int n;
849 struct vfs_class *me = FH_SUPER->me;
851 if (FH->linear)
852 vfs_die ("no writing to linear files, please");
854 FH->changed = 1;
855 if (FH->handle != -1){
856 n = write (FH->handle, buffer, count);
857 if (n < 0)
858 me->verrno = errno;
859 return n;
861 vfs_die ("vfs_s_write: This should not happen\n");
862 return 0;
865 static off_t
866 vfs_s_lseek (void *fh, off_t offset, int whence)
868 off_t size = FH->ino->st.st_size;
870 if (FH->linear == LS_LINEAR_OPEN)
871 vfs_die ("cannot lseek() after linear_read!");
873 if (FH->handle != -1){ /* If we have local file opened, we want to work with it */
874 int retval = lseek (FH->handle, offset, whence);
875 if (retval == -1)
876 FH->ino->super->me->verrno = errno;
877 return retval;
880 switch (whence){
881 case SEEK_CUR:
882 offset += FH->pos; break;
883 case SEEK_END:
884 offset += size; break;
886 if (offset < 0)
887 FH->pos = 0;
888 else if (offset < size)
889 FH->pos = offset;
890 else
891 FH->pos = size;
892 return FH->pos;
895 static int
896 vfs_s_close (void *fh)
898 int res = 0;
899 struct vfs_class *me = FH_SUPER->me;
901 FH_SUPER->fd_usage--;
902 if (!FH_SUPER->fd_usage)
903 vfs_stamp_create (me, FH_SUPER);
905 if (FH->linear == LS_LINEAR_OPEN)
906 MEDATA->linear_close (me, fh);
907 if (MEDATA->fh_close)
908 res = MEDATA->fh_close (me, fh);
909 if (FH->changed && MEDATA->file_store){
910 char *s = vfs_s_fullpath (me, FH->ino);
911 if (!s)
912 res = -1;
913 else {
914 res = MEDATA->file_store (me, fh, s, FH->ino->localname);
915 g_free (s);
917 vfs_s_invalidate (me, FH_SUPER);
919 if (FH->handle != -1)
920 close (FH->handle);
922 vfs_s_free_inode (me, FH->ino);
923 g_free (fh);
924 return res;
927 static void
928 vfs_s_print_stats (const char *fs_name, const char *action,
929 const char *file_name, off_t have, off_t need)
931 static const char *i18n_percent_transf_format = NULL;
932 static const char *i18n_transf_format = NULL;
934 if (i18n_percent_transf_format == NULL) {
935 i18n_percent_transf_format =
936 _("%s: %s: %s %3d%% (%lu bytes transferred)");
937 i18n_transf_format = _("%s: %s: %s %lu bytes transferred");
940 if (need)
941 print_vfs_message (i18n_percent_transf_format, fs_name, action,
942 file_name, (int) ((double) have * 100 / need),
943 (unsigned long) have);
944 else
945 print_vfs_message (i18n_transf_format, fs_name, action, file_name,
946 (unsigned long) have);
950 vfs_s_retrieve_file (struct vfs_class *me, struct vfs_s_inode *ino)
952 /* If you want reget, you'll have to open file with O_LINEAR */
953 off_t total = 0;
954 char buffer[8192];
955 int handle, n;
956 off_t stat_size = ino->st.st_size;
957 struct vfs_s_fh fh;
959 memset (&fh, 0, sizeof (fh));
961 fh.ino = ino;
962 fh.handle = -1;
964 handle = vfs_mkstemps (&ino->localname, me->name, ino->ent->name);
965 if (handle == -1) {
966 me->verrno = errno;
967 goto error_4;
970 if (!MEDATA->linear_start (me, &fh, 0))
971 goto error_3;
973 /* Clear the interrupt status */
974 tty_got_interrupt ();
975 tty_enable_interrupt_key ();
977 while ((n = MEDATA->linear_read (me, &fh, buffer, sizeof (buffer)))) {
978 int t;
979 if (n < 0)
980 goto error_1;
982 total += n;
983 vfs_s_print_stats (me->name, _("Getting file"), ino->ent->name,
984 total, stat_size);
986 if (tty_got_interrupt ())
987 goto error_1;
989 t = write (handle, buffer, n);
990 if (t != n) {
991 if (t == -1)
992 me->verrno = errno;
993 goto error_1;
996 MEDATA->linear_close (me, &fh);
997 close (handle);
999 tty_disable_interrupt_key ();
1000 return 0;
1002 error_1:
1003 MEDATA->linear_close (me, &fh);
1004 error_3:
1005 tty_disable_interrupt_key ();
1006 close (handle);
1007 unlink (ino->localname);
1008 error_4:
1009 g_free (ino->localname);
1010 ino->localname = NULL;
1011 return -1;
1014 /* ------------------------------- mc support ---------------------------- */
1016 static void
1017 vfs_s_fill_names (struct vfs_class *me, fill_names_f func)
1019 struct vfs_s_super *a = MEDATA->supers;
1020 char *name;
1022 while (a){
1023 name = g_strconcat ( a->name, "#", me->prefix, "/",
1024 /* a->current_dir->name, */ (char *) NULL);
1025 (*func)(name);
1026 g_free (name);
1027 a = a->next;
1031 static int
1032 vfs_s_ferrno (struct vfs_class *me)
1034 return me->verrno;
1038 * Get local copy of the given file. We reuse the existing file cache
1039 * for remote filesystems. Archives use standard VFS facilities.
1041 static char *
1042 vfs_s_getlocalcopy (struct vfs_class *me, const char *path)
1044 struct vfs_s_fh *fh;
1045 char *local;
1047 fh = vfs_s_open (me, path, O_RDONLY, 0);
1048 if (!fh || !fh->ino || !fh->ino->localname)
1049 return NULL;
1051 local = g_strdup (fh->ino->localname);
1052 vfs_s_close (fh);
1053 return local;
1057 * Return the local copy. Since we are using our cache, we do nothing -
1058 * the cache will be removed when the archive is closed.
1060 static int
1061 vfs_s_ungetlocalcopy (struct vfs_class *me, const char *path,
1062 const char *local, int has_changed)
1064 (void) me;
1065 (void) path;
1066 (void) local;
1067 (void) has_changed;
1068 return 0;
1071 static int
1072 vfs_s_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1074 switch (ctlop) {
1075 case VFS_SETCTL_STALE_DATA:
1077 struct vfs_s_inode *ino = vfs_s_inode_from_path (me, path, 0);
1079 if (!ino)
1080 return 0;
1081 if (arg)
1082 ino->super->want_stale = 1;
1083 else {
1084 ino->super->want_stale = 0;
1085 vfs_s_invalidate (me, ino->super);
1087 return 1;
1089 case VFS_SETCTL_LOGFILE:
1090 MEDATA->logfile = fopen ((char *) arg, "w");
1091 return 1;
1092 case VFS_SETCTL_FLUSH:
1093 MEDATA->flush = 1;
1094 return 1;
1096 return 0;
1100 /* ----------------------------- Stamping support -------------------------- */
1102 static vfsid
1103 vfs_s_getid (struct vfs_class *me, const char *path)
1105 struct vfs_s_super *archive;
1106 char *p;
1108 if (!(p = vfs_s_get_path (me, path, &archive, FL_NO_OPEN)))
1109 return NULL;
1110 g_free(p);
1111 return (vfsid) archive;
1114 static int
1115 vfs_s_nothingisopen (vfsid id)
1117 (void) id;
1118 /* Our data structures should survive free of superblock at any time */
1119 return 1;
1122 static void
1123 vfs_s_free (vfsid id)
1125 vfs_s_free_super (((struct vfs_s_super *)id)->me, (struct vfs_s_super *)id);
1128 static int
1129 vfs_s_dir_uptodate (struct vfs_class *me, struct vfs_s_inode *ino)
1131 struct timeval tim;
1133 if (MEDATA->flush) {
1134 MEDATA->flush = 0;
1135 return 0;
1138 gettimeofday(&tim, NULL);
1139 if (tim.tv_sec < ino->timestamp.tv_sec)
1140 return 1;
1141 return 0;
1144 /* Initialize one of our subclasses - fill common functions */
1145 void
1146 vfs_s_init_class (struct vfs_class *vclass, struct vfs_s_subclass *sub)
1148 vclass->data = sub;
1149 vclass->fill_names = vfs_s_fill_names;
1150 vclass->open = vfs_s_open;
1151 vclass->close = vfs_s_close;
1152 vclass->read = vfs_s_read;
1153 if (!(sub->flags & VFS_S_READONLY)) {
1154 vclass->write = vfs_s_write;
1156 vclass->opendir = vfs_s_opendir;
1157 vclass->readdir = vfs_s_readdir;
1158 vclass->closedir = vfs_s_closedir;
1159 vclass->stat = vfs_s_stat;
1160 vclass->lstat = vfs_s_lstat;
1161 vclass->fstat = vfs_s_fstat;
1162 vclass->readlink = vfs_s_readlink;
1163 vclass->chdir = vfs_s_chdir;
1164 vclass->ferrno = vfs_s_ferrno;
1165 vclass->lseek = vfs_s_lseek;
1166 vclass->getid = vfs_s_getid;
1167 vclass->nothingisopen = vfs_s_nothingisopen;
1168 vclass->free = vfs_s_free;
1169 if (sub->flags & VFS_S_REMOTE) {
1170 vclass->getlocalcopy = vfs_s_getlocalcopy;
1171 vclass->ungetlocalcopy = vfs_s_ungetlocalcopy;
1172 sub->find_entry = vfs_s_find_entry_linear;
1173 } else {
1174 sub->find_entry = vfs_s_find_entry_tree;
1176 vclass->setctl = vfs_s_setctl;
1177 sub->dir_uptodate = vfs_s_dir_uptodate;
1180 /* ----------- Utility functions for networked filesystems -------------- */
1182 #ifdef USE_NETCODE
1184 vfs_s_select_on_two (int fd1, int fd2)
1186 fd_set set;
1187 struct timeval timeout;
1188 int v;
1189 int maxfd = (fd1 > fd2 ? fd1 : fd2) + 1;
1191 timeout.tv_sec = 1;
1192 timeout.tv_usec = 0;
1193 FD_ZERO (&set);
1194 FD_SET (fd1, &set);
1195 FD_SET (fd2, &set);
1196 v = select (maxfd, &set, 0, 0, &timeout);
1197 if (v <= 0)
1198 return v;
1199 if (FD_ISSET (fd1, &set))
1200 return 1;
1201 if (FD_ISSET (fd2, &set))
1202 return 2;
1203 return -1;
1207 vfs_s_get_line (struct vfs_class *me, int sock, char *buf, int buf_len, char term)
1209 FILE *logfile = MEDATA->logfile;
1210 int i;
1211 char c;
1213 for (i = 0; i < buf_len - 1; i++, buf++){
1214 if (read (sock, buf, sizeof(char)) <= 0)
1215 return 0;
1216 if (logfile){
1217 fwrite (buf, 1, 1, logfile);
1218 fflush (logfile);
1220 if (*buf == term){
1221 *buf = 0;
1222 return 1;
1226 /* Line is too long - terminate buffer and discard the rest of line */
1227 *buf = 0;
1228 while (read (sock, &c, sizeof (c)) > 0) {
1229 if (logfile){
1230 fwrite (&c, 1, 1, logfile);
1231 fflush (logfile);
1233 if (c == '\n')
1234 return 1;
1236 return 0;
1240 vfs_s_get_line_interruptible (struct vfs_class *me, char *buffer, int size, int fd)
1242 int n;
1243 int i;
1245 (void) me;
1247 tty_enable_interrupt_key ();
1248 for (i = 0; i < size-1; i++){
1249 n = read (fd, buffer+i, 1);
1250 tty_disable_interrupt_key ();
1251 if (n == -1 && errno == EINTR){
1252 buffer [i] = 0;
1253 return EINTR;
1255 if (n == 0){
1256 buffer [i] = 0;
1257 return 0;
1259 if (buffer [i] == '\n'){
1260 buffer [i] = 0;
1261 return 1;
1264 buffer [size-1] = 0;
1265 return 0;
1267 #endif /* USE_NETCODE */