Completely removed MHL stuff
[midnight-commander.git] / vfs / direntry.c
blobc2fff869c153f0a699af77a439674fecddd25f09
1 /* Directory cache support -- so that you do not have copy of this in
2 * each and every filesystem.
4 * Written at 1998 by Pavel Machek <pavel@ucw.cz>, distribute under LGPL.
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 * Paths here do _not_ begin with '/', so root directory of
25 * archive/site is simply "". Beware. */
27 #include <config.h>
29 #include <errno.h>
31 #include "../src/global.h"
32 #include "../src/tty.h" /* enable/disable interrupt key */
33 #include "../src/wtools.h" /* message() */
34 #include "../src/main.h" /* print_vfs_message */
35 #include "utilvfs.h"
36 #include "vfs-impl.h"
37 #include "gc.h" /* vfs_rmstamp */
38 #include "xdirentry.h"
40 #define CALL(x) if (MEDATA->x) MEDATA->x
42 static volatile int total_inodes = 0, total_entries = 0;
44 struct vfs_s_inode *
45 vfs_s_new_inode (struct vfs_class *me, struct vfs_s_super *super, struct stat *initstat)
47 struct vfs_s_inode *ino;
49 ino = g_new0 (struct vfs_s_inode, 1);
50 if (!ino)
51 return NULL;
53 if (initstat)
54 ino->st = *initstat;
55 ino->super = super;
56 ino->st.st_nlink = 0;
57 ino->st.st_ino = MEDATA->inode_counter++;
58 ino->st.st_dev = MEDATA->rdev;
60 super->ino_usage++;
61 total_inodes++;
63 CALL (init_inode) (me, ino);
65 return ino;
68 struct vfs_s_entry *
69 vfs_s_new_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *inode)
71 struct vfs_s_entry *entry;
73 entry = g_new0 (struct vfs_s_entry, 1);
74 total_entries++;
76 if (name)
77 entry->name = g_strdup (name);
79 entry->ino = inode;
80 entry->ino->ent = entry;
81 CALL (init_entry) (me, entry);
83 return entry;
86 static void
87 vfs_s_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
89 if (!ino)
90 vfs_die ("Don't pass NULL to me");
92 /* ==0 can happen if freshly created entry is deleted */
93 if (ino->st.st_nlink <= 1){
94 while (ino->subdir){
95 vfs_s_free_entry (me, ino->subdir);
98 CALL (free_inode) (me, ino);
99 g_free (ino->linkname);
100 if (ino->localname){
101 unlink (ino->localname);
102 g_free(ino->localname);
104 total_inodes--;
105 ino->super->ino_usage--;
106 g_free(ino);
107 } else ino->st.st_nlink--;
110 void
111 vfs_s_free_entry (struct vfs_class *me, struct vfs_s_entry *ent)
113 if (ent->prevp){ /* It is possible that we are deleting freshly created entry */
114 *ent->prevp = ent->next;
115 if (ent->next)
116 ent->next->prevp = ent->prevp;
119 g_free (ent->name);
120 ent->name = NULL;
122 if (ent->ino){
123 ent->ino->ent = NULL;
124 vfs_s_free_inode (me, ent->ino);
125 ent->ino = NULL;
128 total_entries--;
129 g_free(ent);
132 void
133 vfs_s_insert_entry (struct vfs_class *me, struct vfs_s_inode *dir, struct vfs_s_entry *ent)
135 struct vfs_s_entry **ep;
137 (void) me;
139 for (ep = &dir->subdir; *ep != NULL; ep = &((*ep)->next))
141 ent->prevp = ep;
142 ent->next = NULL;
143 ent->dir = dir;
144 *ep = ent;
146 ent->ino->st.st_nlink++;
149 struct stat *
150 vfs_s_default_stat (struct vfs_class *me, mode_t mode)
152 static struct stat st;
153 int myumask;
155 (void) me;
157 myumask = umask (022);
158 umask (myumask);
159 mode &= ~myumask;
161 st.st_mode = mode;
162 st.st_ino = 0;
163 st.st_dev = 0;
164 st.st_rdev = 0;
165 st.st_uid = getuid ();
166 st.st_gid = getgid ();
167 st.st_size = 0;
168 st.st_mtime = st.st_atime = st.st_ctime = time (NULL);
170 return &st;
173 struct vfs_s_entry *
174 vfs_s_generate_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *parent, mode_t mode)
176 struct vfs_s_inode *inode;
177 struct stat *st;
179 st = vfs_s_default_stat (me, mode);
180 inode = vfs_s_new_inode (me, parent->super, st);
182 return vfs_s_new_entry (me, name, inode);
185 /* We were asked to create entries automagically */
186 static struct vfs_s_entry *
187 vfs_s_automake (struct vfs_class *me, struct vfs_s_inode *dir, char *path, int flags)
189 struct vfs_s_entry *res;
190 char *sep = strchr (path, PATH_SEP);
192 if (sep)
193 *sep = 0;
194 res = vfs_s_generate_entry (me, path, dir, flags & FL_MKDIR ? (0777 | S_IFDIR) : 0777);
195 vfs_s_insert_entry (me, dir, res);
197 if (sep)
198 *sep = PATH_SEP;
200 return res;
203 /* If the entry is a symlink, find the entry for its target */
204 static struct vfs_s_entry *
205 vfs_s_resolve_symlink (struct vfs_class *me, struct vfs_s_entry *entry,
206 int follow)
208 char *linkname;
209 char *fullname = NULL;
210 struct vfs_s_entry *target;
212 if (follow == LINK_NO_FOLLOW)
213 return entry;
214 if (follow == 0)
215 ERRNOR (ELOOP, NULL);
216 if (!entry)
217 ERRNOR (ENOENT, NULL);
218 if (!S_ISLNK (entry->ino->st.st_mode))
219 return entry;
221 linkname = entry->ino->linkname;
222 if (linkname == NULL)
223 ERRNOR (EFAULT, NULL);
225 /* make full path from relative */
226 if (*linkname != PATH_SEP) {
227 char *fullpath = vfs_s_fullpath (me, entry->dir);
228 if (fullpath) {
229 fullname = g_strconcat (fullpath, "/", linkname, NULL);
230 linkname = fullname;
231 g_free (fullpath);
235 target =
236 (MEDATA->find_entry) (me, entry->dir->super->root, linkname,
237 follow - 1, 0);
238 g_free (fullname);
239 return target;
243 * Follow > 0: follow links, serves as loop protect,
244 * == -1: do not follow links
246 static struct vfs_s_entry *
247 vfs_s_find_entry_tree (struct vfs_class *me, struct vfs_s_inode *root,
248 const char *a_path, int follow, int flags)
250 size_t pseg;
251 struct vfs_s_entry *ent = NULL;
252 char * const pathref = g_strdup (a_path);
253 char *path = pathref;
255 canonicalize_pathname (path);
257 while (root) {
258 while (*path == PATH_SEP) /* Strip leading '/' */
259 path++;
261 if (!path[0]) {
262 g_free (pathref);
263 return ent;
266 for (pseg = 0; path[pseg] && path[pseg] != PATH_SEP; pseg++);
268 for (ent = root->subdir; ent != NULL; ent = ent->next)
269 if (strlen (ent->name) == pseg
270 && (!strncmp (ent->name, path, pseg)))
271 /* FOUND! */
272 break;
274 if (!ent && (flags & (FL_MKFILE | FL_MKDIR)))
275 ent = vfs_s_automake (me, root, path, flags);
276 if (!ent) {
277 me->verrno = ENOENT;
278 goto cleanup;
280 path += pseg;
281 /* here we must follow leading directories always;
282 only the actual file is optional */
283 ent =
284 vfs_s_resolve_symlink (me, ent,
285 strchr (path,
286 PATH_SEP) ? LINK_FOLLOW :
287 follow);
288 if (!ent)
289 goto cleanup;
290 root = ent->ino;
292 cleanup:
293 g_free (pathref);
294 return NULL;
297 static void
298 split_dir_name (struct vfs_class *me, char *path, char **dir, char **name, char **save)
300 char *s;
302 (void) me;
304 s = strrchr (path, PATH_SEP);
305 if (s == NULL) {
306 *save = NULL;
307 *name = path;
308 *dir = path + strlen(path); /* an empty string */
309 } else {
310 *save = s;
311 *dir = path;
312 *s++ = '\0';
313 *name = s;
317 static struct vfs_s_entry *
318 vfs_s_find_entry_linear (struct vfs_class *me, struct vfs_s_inode *root,
319 const char *a_path, int follow, int flags)
321 struct vfs_s_entry *ent = NULL;
322 char * const path = g_strdup (a_path);
323 struct vfs_s_entry *retval = NULL;
325 if (root->super->root != root)
326 vfs_die ("We have to use _real_ root. Always. Sorry.");
328 canonicalize_pathname (path);
330 if (!(flags & FL_DIR)) {
331 char *dirname, *name, *save;
332 struct vfs_s_inode *ino;
333 split_dir_name (me, path, &dirname, &name, &save);
334 ino =
335 vfs_s_find_inode (me, root->super, dirname, follow,
336 flags | FL_DIR);
337 if (save)
338 *save = PATH_SEP;
339 retval = vfs_s_find_entry_tree (me, ino, name, follow, flags);
340 g_free (path);
341 return retval;
344 for (ent = root->subdir; ent != NULL; ent = ent->next)
345 if (!strcmp (ent->name, path))
346 break;
348 if (ent && (!(MEDATA->dir_uptodate) (me, ent->ino))) {
349 #if 1
350 print_vfs_message (_("Directory cache expired for %s"), path);
351 #endif
352 vfs_s_free_entry (me, ent);
353 ent = NULL;
356 if (!ent) {
357 struct vfs_s_inode *ino;
359 ino =
360 vfs_s_new_inode (me, root->super,
361 vfs_s_default_stat (me, S_IFDIR | 0755));
362 ent = vfs_s_new_entry (me, path, ino);
363 if ((MEDATA->dir_load) (me, ino, path) == -1) {
364 vfs_s_free_entry (me, ent);
365 g_free (path);
366 return NULL;
368 vfs_s_insert_entry (me, root, ent);
370 for (ent = root->subdir; ent != NULL; ent = ent->next)
371 if (!strcmp (ent->name, path))
372 break;
374 if (!ent)
375 vfs_die ("find_linear: success but directory is not there\n");
377 #if 0
378 if (!vfs_s_resolve_symlink (me, ent, follow)) {
379 g_free (path);
380 return NULL;
382 #endif
383 g_free (path);
384 return ent;
387 struct vfs_s_inode *
388 vfs_s_find_inode (struct vfs_class *me, const struct vfs_s_super *super,
389 const char *path, int follow, int flags)
391 struct vfs_s_entry *ent;
392 if (!(MEDATA->flags & VFS_S_REMOTE) && (!*path))
393 return super->root;
394 ent = (MEDATA->find_entry) (me, super->root, path, follow, flags);
395 if (!ent)
396 return NULL;
397 return ent->ino;
400 /* Ook, these were functions around directory entries / inodes */
401 /* -------------------------------- superblock games -------------------------- */
403 static struct vfs_s_super *
404 vfs_s_new_super (struct vfs_class *me)
406 struct vfs_s_super *super;
408 super = g_new0 (struct vfs_s_super, 1);
409 super->me = me;
410 return super;
413 static void
414 vfs_s_insert_super (struct vfs_class *me, struct vfs_s_super *super)
416 super->next = MEDATA->supers;
417 super->prevp = &MEDATA->supers;
419 if (MEDATA->supers != NULL)
420 MEDATA->supers->prevp = &super->next;
421 MEDATA->supers = super;
424 static void
425 vfs_s_free_super (struct vfs_class *me, struct vfs_s_super *super)
427 if (super->root){
428 vfs_s_free_inode (me, super->root);
429 super->root = NULL;
432 #if 0
433 /* FIXME: We currently leak small ammount of memory, sometimes. Fix it if you can. */
434 if (super->ino_usage)
435 message (D_ERROR, " Direntry warning ",
436 "Super ino_usage is %d, memory leak",
437 super->ino_usage);
439 if (super->want_stale)
440 message (D_ERROR, " Direntry warning ", "Super has want_stale set");
441 #endif
443 if (super->prevp){
444 *super->prevp = super->next;
445 if (super->next)
446 super->next->prevp = super->prevp;
449 CALL (free_archive) (me, super);
450 g_free (super->name);
451 g_free(super);
456 * Dissect the path and create corresponding superblock. Note that inname
457 * can be changed and the result may point inside the original string.
459 const char *
460 vfs_s_get_path_mangle (struct vfs_class *me, char *inname,
461 struct vfs_s_super **archive, int flags)
463 const char *retval;
464 char *local, *op;
465 const char *archive_name;
466 int result = -1;
467 struct vfs_s_super *super;
468 void *cookie = NULL;
470 archive_name = inname;
471 vfs_split (inname, &local, &op);
472 retval = (local) ? local : "";
474 if (MEDATA->archive_check)
475 if (!(cookie = MEDATA->archive_check (me, archive_name, op)))
476 return NULL;
478 for (super = MEDATA->supers; super != NULL; super = super->next) {
479 /* 0 == other, 1 == same, return it, 2 == other but stop scanning */
480 int i;
481 if ((i =
482 MEDATA->archive_same (me, super, archive_name, op, cookie))) {
483 if (i == 1)
484 goto return_success;
485 else
486 break;
490 if (flags & FL_NO_OPEN)
491 ERRNOR (EIO, NULL);
493 super = vfs_s_new_super (me);
494 result = MEDATA->open_archive (me, super, archive_name, op);
495 if (result == -1) {
496 vfs_s_free_super (me, super);
497 ERRNOR (EIO, NULL);
499 if (!super->name)
500 vfs_die ("You have to fill name\n");
501 if (!super->root)
502 vfs_die ("You have to fill root inode\n");
504 vfs_s_insert_super (me, super);
505 vfs_stamp_create (me, super);
507 return_success:
508 *archive = super;
509 return retval;
514 * Dissect the path and create corresponding superblock.
515 * The result should be freed.
517 static char *
518 vfs_s_get_path (struct vfs_class *me, const char *inname,
519 struct vfs_s_super **archive, int flags)
521 char *buf, *retval;
523 buf = g_strdup (inname);
524 retval = g_strdup (vfs_s_get_path_mangle (me, buf, archive, flags));
525 g_free (buf);
526 return retval;
529 void
530 vfs_s_invalidate (struct vfs_class *me, struct vfs_s_super *super)
532 if (!super->want_stale){
533 vfs_s_free_inode (me, super->root);
534 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
538 char *
539 vfs_s_fullpath (struct vfs_class *me, struct vfs_s_inode *ino)
541 if (!ino->ent)
542 ERRNOR (EAGAIN, NULL);
544 if (!(MEDATA->flags & VFS_S_REMOTE)) {
545 /* archives */
546 char *newpath;
547 char *path = g_strdup (ino->ent->name);
548 while (1) {
549 ino = ino->ent->dir;
550 if (ino == ino->super->root)
551 break;
552 newpath = g_strconcat (ino->ent->name, "/", path, (char *) NULL);
553 g_free (path);
554 path = newpath;
556 return path;
559 /* remote systems */
560 if ((!ino->ent->dir) || (!ino->ent->dir->ent))
561 return g_strdup (ino->ent->name);
563 return g_strconcat (ino->ent->dir->ent->name, PATH_SEP_STR,
564 ino->ent->name, (char *) NULL);
567 /* Support of archives */
568 /* ------------------------ readdir & friends ----------------------------- */
570 static struct vfs_s_inode *
571 vfs_s_inode_from_path (struct vfs_class *me, const char *name, int flags)
573 struct vfs_s_super *super;
574 struct vfs_s_inode *ino;
575 char *q;
577 if (!(q = vfs_s_get_path (me, name, &super, 0)))
578 return NULL;
580 ino =
581 vfs_s_find_inode (me, super, q,
582 flags & FL_FOLLOW ? LINK_FOLLOW : LINK_NO_FOLLOW,
583 flags & ~FL_FOLLOW);
584 if ((!ino) && (!*q))
585 /* We are asking about / directory of ftp server: assume it exists */
586 ino =
587 vfs_s_find_inode (me, super, q,
588 flags & FL_FOLLOW ? LINK_FOLLOW :
589 LINK_NO_FOLLOW,
590 FL_DIR | (flags & ~FL_FOLLOW));
591 g_free (q);
592 return ino;
595 struct dirhandle {
596 struct vfs_s_entry *cur;
597 struct vfs_s_inode *dir;
600 static void *
601 vfs_s_opendir (struct vfs_class *me, const char *dirname)
603 struct vfs_s_inode *dir;
604 struct dirhandle *info;
606 dir = vfs_s_inode_from_path (me, dirname, FL_DIR | FL_FOLLOW);
607 if (!dir)
608 return NULL;
609 if (!S_ISDIR (dir->st.st_mode))
610 ERRNOR (ENOTDIR, NULL);
612 dir->st.st_nlink++;
613 #if 0
614 if (!dir->subdir) /* This can actually happen if we allow empty directories */
615 ERRNOR (EAGAIN, NULL);
616 #endif
617 info = g_new (struct dirhandle, 1);
618 info->cur = dir->subdir;
619 info->dir = dir;
621 return info;
624 static void *
625 vfs_s_readdir(void *data)
627 static union vfs_dirent dir;
628 struct dirhandle *info = (struct dirhandle *) data;
630 if (!(info->cur))
631 return NULL;
633 if (info->cur->name) {
634 g_strlcpy (dir.dent.d_name, info->cur->name, MC_MAXPATHLEN);
635 } else {
636 vfs_die("Null in structure-cannot happen");
639 compute_namelen(&dir.dent);
640 info->cur = info->cur->next;
642 return (void *) &dir;
645 static int
646 vfs_s_closedir (void *data)
648 struct dirhandle *info = (struct dirhandle *) data;
649 struct vfs_s_inode *dir = info->dir;
651 vfs_s_free_inode (dir->super->me, dir);
652 g_free (data);
653 return 0;
656 static int
657 vfs_s_chdir (struct vfs_class *me, const char *path)
659 void *data;
660 if (!(data = vfs_s_opendir (me, path)))
661 return -1;
662 vfs_s_closedir (data);
663 return 0;
666 /* --------------------------- stat and friends ---------------------------- */
668 static int
669 vfs_s_internal_stat (struct vfs_class *me, const char *path, struct stat *buf, int flag)
671 struct vfs_s_inode *ino;
673 if (!(ino = vfs_s_inode_from_path (me, path, flag)))
674 return -1;
675 *buf = ino->st;
676 return 0;
679 static int
680 vfs_s_stat (struct vfs_class *me, const char *path, struct stat *buf)
682 return vfs_s_internal_stat (me, path, buf, FL_FOLLOW);
685 static int
686 vfs_s_lstat (struct vfs_class *me, const char *path, struct stat *buf)
688 return vfs_s_internal_stat (me, path, buf, FL_NONE);
691 static int
692 vfs_s_fstat (void *fh, struct stat *buf)
694 *buf = FH->ino->st;
695 return 0;
698 static int
699 vfs_s_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
701 struct vfs_s_inode *ino;
702 size_t len;
704 ino = vfs_s_inode_from_path (me, path, 0);
705 if (!ino)
706 return -1;
708 if (!S_ISLNK (ino->st.st_mode))
709 ERRNOR (EINVAL, -1);
711 if (ino->linkname == NULL)
712 ERRNOR (EFAULT, -1);
714 len = strlen (ino->linkname);
715 if (size < len)
716 len = size;
717 /* readlink() does not append a NUL character to buf */
718 memcpy (buf, ino->linkname, len);
719 return len;
722 static void *
723 vfs_s_open (struct vfs_class *me, const char *file, int flags, int mode)
725 int was_changed = 0;
726 struct vfs_s_fh *fh;
727 struct vfs_s_super *super;
728 char *q;
729 struct vfs_s_inode *ino;
731 if ((q = vfs_s_get_path (me, file, &super, 0)) == NULL)
732 return NULL;
733 ino = vfs_s_find_inode (me, super, q, LINK_FOLLOW, FL_NONE);
734 if (ino && ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))) {
735 g_free (q);
736 ERRNOR (EEXIST, NULL);
738 if (!ino) {
739 char *dirname, *name, *save;
740 struct vfs_s_entry *ent;
741 struct vfs_s_inode *dir;
742 int tmp_handle;
744 /* If the filesystem is read-only, disable file creation */
745 if (!(flags & O_CREAT) || !(me->write)) {
746 g_free (q);
747 return NULL;
750 split_dir_name (me, q, &dirname, &name, &save);
751 /* FIXME: check if vfs_s_find_inode returns NULL */
752 dir = vfs_s_find_inode (me, super, dirname, LINK_FOLLOW, FL_DIR);
753 if (save)
754 *save = PATH_SEP;
755 ent = vfs_s_generate_entry (me, name, dir, 0755);
756 ino = ent->ino;
757 vfs_s_insert_entry (me, dir, ent);
758 tmp_handle = vfs_mkstemps (&ino->localname, me->name, name);
759 if (tmp_handle == -1) {
760 g_free (q);
761 return NULL;
763 close (tmp_handle);
764 was_changed = 1;
767 g_free (q);
769 if (S_ISDIR (ino->st.st_mode))
770 ERRNOR (EISDIR, NULL);
772 fh = g_new (struct vfs_s_fh, 1);
773 fh->pos = 0;
774 fh->ino = ino;
775 fh->handle = -1;
776 fh->changed = was_changed;
777 fh->linear = 0;
779 if (IS_LINEAR (flags)) {
780 if (MEDATA->linear_start) {
781 print_vfs_message (_("Starting linear transfer..."));
782 fh->linear = LS_LINEAR_PREOPEN;
784 } else if ((MEDATA->fh_open)
785 && (MEDATA->fh_open (me, fh, flags, mode))) {
786 g_free (fh);
787 return NULL;
790 if (fh->ino->localname) {
791 fh->handle = open (fh->ino->localname, NO_LINEAR (flags), mode);
792 if (fh->handle == -1) {
793 g_free (fh);
794 ERRNOR (errno, NULL);
798 /* i.e. we had no open files and now we have one */
799 vfs_rmstamp (me, (vfsid) super);
800 super->fd_usage++;
801 fh->ino->st.st_nlink++;
802 return fh;
805 static ssize_t
806 vfs_s_read (void *fh, char *buffer, int count)
808 int n;
809 struct vfs_class *me = FH_SUPER->me;
811 if (FH->linear == LS_LINEAR_PREOPEN) {
812 if (!MEDATA->linear_start (me, FH, FH->pos))
813 return -1;
816 if (FH->linear == LS_LINEAR_CLOSED)
817 vfs_die ("linear_start() did not set linear_state!");
819 if (FH->linear == LS_LINEAR_OPEN)
820 return MEDATA->linear_read (me, FH, buffer, count);
822 if (FH->handle != -1){
823 n = read (FH->handle, buffer, count);
824 if (n < 0)
825 me->verrno = errno;
826 return n;
828 vfs_die ("vfs_s_read: This should not happen\n");
829 return -1;
832 static int
833 vfs_s_write (void *fh, const char *buffer, int count)
835 int n;
836 struct vfs_class *me = FH_SUPER->me;
838 if (FH->linear)
839 vfs_die ("no writing to linear files, please");
841 FH->changed = 1;
842 if (FH->handle != -1){
843 n = write (FH->handle, buffer, count);
844 if (n < 0)
845 me->verrno = errno;
846 return n;
848 vfs_die ("vfs_s_write: This should not happen\n");
849 return 0;
852 static off_t
853 vfs_s_lseek (void *fh, off_t offset, int whence)
855 off_t size = FH->ino->st.st_size;
857 if (FH->linear == LS_LINEAR_OPEN)
858 vfs_die ("cannot lseek() after linear_read!");
860 if (FH->handle != -1){ /* If we have local file opened, we want to work with it */
861 int retval = lseek (FH->handle, offset, whence);
862 if (retval == -1)
863 FH->ino->super->me->verrno = errno;
864 return retval;
867 switch (whence){
868 case SEEK_CUR:
869 offset += FH->pos; break;
870 case SEEK_END:
871 offset += size; break;
873 if (offset < 0)
874 FH->pos = 0;
875 else if (offset < size)
876 FH->pos = offset;
877 else
878 FH->pos = size;
879 return FH->pos;
882 static int
883 vfs_s_close (void *fh)
885 int res = 0;
886 struct vfs_class *me = FH_SUPER->me;
888 FH_SUPER->fd_usage--;
889 if (!FH_SUPER->fd_usage)
890 vfs_stamp_create (me, FH_SUPER);
892 if (FH->linear == LS_LINEAR_OPEN)
893 MEDATA->linear_close (me, fh);
894 if (MEDATA->fh_close)
895 res = MEDATA->fh_close (me, fh);
896 if (FH->changed && MEDATA->file_store){
897 char *s = vfs_s_fullpath (me, FH->ino);
898 if (!s)
899 res = -1;
900 else {
901 res = MEDATA->file_store (me, fh, s, FH->ino->localname);
902 g_free (s);
904 vfs_s_invalidate (me, FH_SUPER);
906 if (FH->handle != -1)
907 close (FH->handle);
909 vfs_s_free_inode (me, FH->ino);
910 g_free (fh);
911 return res;
914 static void
915 vfs_s_print_stats (const char *fs_name, const char *action,
916 const char *file_name, off_t have, off_t need)
918 static const char *i18n_percent_transf_format = NULL;
919 static const char *i18n_transf_format = NULL;
921 if (i18n_percent_transf_format == NULL) {
922 i18n_percent_transf_format =
923 _("%s: %s: %s %3d%% (%lu bytes transferred)");
924 i18n_transf_format = _("%s: %s: %s %lu bytes transferred");
927 if (need)
928 print_vfs_message (i18n_percent_transf_format, fs_name, action,
929 file_name, (int) ((double) have * 100 / need),
930 (unsigned long) have);
931 else
932 print_vfs_message (i18n_transf_format, fs_name, action, file_name,
933 (unsigned long) have);
937 vfs_s_retrieve_file (struct vfs_class *me, struct vfs_s_inode *ino)
939 /* If you want reget, you'll have to open file with O_LINEAR */
940 off_t total = 0;
941 char buffer[8192];
942 int handle, n;
943 off_t stat_size = ino->st.st_size;
944 struct vfs_s_fh fh;
946 memset (&fh, 0, sizeof (fh));
948 fh.ino = ino;
949 fh.handle = -1;
951 handle = vfs_mkstemps (&ino->localname, me->name, ino->ent->name);
952 if (handle == -1) {
953 me->verrno = errno;
954 goto error_4;
957 if (!MEDATA->linear_start (me, &fh, 0))
958 goto error_3;
960 /* Clear the interrupt status */
961 got_interrupt ();
962 enable_interrupt_key ();
964 while ((n = MEDATA->linear_read (me, &fh, buffer, sizeof (buffer)))) {
965 int t;
966 if (n < 0)
967 goto error_1;
969 total += n;
970 vfs_s_print_stats (me->name, _("Getting file"), ino->ent->name,
971 total, stat_size);
973 if (got_interrupt ())
974 goto error_1;
976 t = write (handle, buffer, n);
977 if (t != n) {
978 if (t == -1)
979 me->verrno = errno;
980 goto error_1;
983 MEDATA->linear_close (me, &fh);
984 close (handle);
986 disable_interrupt_key ();
987 return 0;
989 error_1:
990 MEDATA->linear_close (me, &fh);
991 error_3:
992 disable_interrupt_key ();
993 close (handle);
994 unlink (ino->localname);
995 error_4:
996 g_free (ino->localname);
997 ino->localname = NULL;
998 return -1;
1001 /* ------------------------------- mc support ---------------------------- */
1003 static void
1004 vfs_s_fill_names (struct vfs_class *me, fill_names_f func)
1006 struct vfs_s_super *a = MEDATA->supers;
1007 char *name;
1009 while (a){
1010 name = g_strconcat ( a->name, "#", me->prefix, "/", /* a->current_dir->name, */ NULL);
1011 (*func)(name);
1012 g_free (name);
1013 a = a->next;
1017 static int
1018 vfs_s_ferrno (struct vfs_class *me)
1020 return me->verrno;
1024 * Get local copy of the given file. We reuse the existing file cache
1025 * for remote filesystems. Archives use standard VFS facilities.
1027 static char *
1028 vfs_s_getlocalcopy (struct vfs_class *me, const char *path)
1030 struct vfs_s_fh *fh;
1031 char *local;
1033 fh = vfs_s_open (me, path, O_RDONLY, 0);
1034 if (!fh || !fh->ino || !fh->ino->localname)
1035 return NULL;
1037 local = g_strdup (fh->ino->localname);
1038 vfs_s_close (fh);
1039 return local;
1043 * Return the local copy. Since we are using our cache, we do nothing -
1044 * the cache will be removed when the archive is closed.
1046 static int
1047 vfs_s_ungetlocalcopy (struct vfs_class *me, const char *path,
1048 const char *local, int has_changed)
1050 (void) me;
1051 (void) path;
1052 (void) local;
1053 (void) has_changed;
1054 return 0;
1057 static int
1058 vfs_s_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1060 switch (ctlop) {
1061 case VFS_SETCTL_STALE_DATA:
1063 struct vfs_s_inode *ino = vfs_s_inode_from_path (me, path, 0);
1065 if (!ino)
1066 return 0;
1067 if (arg)
1068 ino->super->want_stale = 1;
1069 else {
1070 ino->super->want_stale = 0;
1071 vfs_s_invalidate (me, ino->super);
1073 return 1;
1075 case VFS_SETCTL_LOGFILE:
1076 MEDATA->logfile = fopen ((char *) arg, "w");
1077 return 1;
1078 case VFS_SETCTL_FLUSH:
1079 MEDATA->flush = 1;
1080 return 1;
1082 return 0;
1086 /* ----------------------------- Stamping support -------------------------- */
1088 static vfsid
1089 vfs_s_getid (struct vfs_class *me, const char *path)
1091 struct vfs_s_super *archive;
1092 char *p;
1094 if (!(p = vfs_s_get_path (me, path, &archive, FL_NO_OPEN)))
1095 return NULL;
1096 g_free(p);
1097 return (vfsid) archive;
1100 static int
1101 vfs_s_nothingisopen (vfsid id)
1103 (void) id;
1104 /* Our data structures should survive free of superblock at any time */
1105 return 1;
1108 static void
1109 vfs_s_free (vfsid id)
1111 vfs_s_free_super (((struct vfs_s_super *)id)->me, (struct vfs_s_super *)id);
1114 static int
1115 vfs_s_dir_uptodate (struct vfs_class *me, struct vfs_s_inode *ino)
1117 struct timeval tim;
1119 if (MEDATA->flush) {
1120 MEDATA->flush = 0;
1121 return 0;
1124 gettimeofday(&tim, NULL);
1125 if (tim.tv_sec < ino->timestamp.tv_sec)
1126 return 1;
1127 return 0;
1130 /* Initialize one of our subclasses - fill common functions */
1131 void
1132 vfs_s_init_class (struct vfs_class *vclass, struct vfs_s_subclass *sub)
1134 vclass->data = sub;
1135 vclass->fill_names = vfs_s_fill_names;
1136 vclass->open = vfs_s_open;
1137 vclass->close = vfs_s_close;
1138 vclass->read = vfs_s_read;
1139 if (!(sub->flags & VFS_S_READONLY)) {
1140 vclass->write = vfs_s_write;
1142 vclass->opendir = vfs_s_opendir;
1143 vclass->readdir = vfs_s_readdir;
1144 vclass->closedir = vfs_s_closedir;
1145 vclass->stat = vfs_s_stat;
1146 vclass->lstat = vfs_s_lstat;
1147 vclass->fstat = vfs_s_fstat;
1148 vclass->readlink = vfs_s_readlink;
1149 vclass->chdir = vfs_s_chdir;
1150 vclass->ferrno = vfs_s_ferrno;
1151 vclass->lseek = vfs_s_lseek;
1152 vclass->getid = vfs_s_getid;
1153 vclass->nothingisopen = vfs_s_nothingisopen;
1154 vclass->free = vfs_s_free;
1155 if (sub->flags & VFS_S_REMOTE) {
1156 vclass->getlocalcopy = vfs_s_getlocalcopy;
1157 vclass->ungetlocalcopy = vfs_s_ungetlocalcopy;
1158 sub->find_entry = vfs_s_find_entry_linear;
1159 } else {
1160 sub->find_entry = vfs_s_find_entry_tree;
1162 vclass->setctl = vfs_s_setctl;
1163 sub->dir_uptodate = vfs_s_dir_uptodate;
1166 /* ----------- Utility functions for networked filesystems -------------- */
1168 #ifdef USE_NETCODE
1170 vfs_s_select_on_two (int fd1, int fd2)
1172 fd_set set;
1173 struct timeval timeout;
1174 int v;
1175 int maxfd = (fd1 > fd2 ? fd1 : fd2) + 1;
1177 timeout.tv_sec = 1;
1178 timeout.tv_usec = 0;
1179 FD_ZERO (&set);
1180 FD_SET (fd1, &set);
1181 FD_SET (fd2, &set);
1182 v = select (maxfd, &set, 0, 0, &timeout);
1183 if (v <= 0)
1184 return v;
1185 if (FD_ISSET (fd1, &set))
1186 return 1;
1187 if (FD_ISSET (fd2, &set))
1188 return 2;
1189 return -1;
1193 vfs_s_get_line (struct vfs_class *me, int sock, char *buf, int buf_len, char term)
1195 FILE *logfile = MEDATA->logfile;
1196 int i;
1197 char c;
1199 for (i = 0; i < buf_len - 1; i++, buf++){
1200 if (read (sock, buf, sizeof(char)) <= 0)
1201 return 0;
1202 if (logfile){
1203 fwrite (buf, 1, 1, logfile);
1204 fflush (logfile);
1206 if (*buf == term){
1207 *buf = 0;
1208 return 1;
1212 /* Line is too long - terminate buffer and discard the rest of line */
1213 *buf = 0;
1214 while (read (sock, &c, sizeof (c)) > 0) {
1215 if (logfile){
1216 fwrite (&c, 1, 1, logfile);
1217 fflush (logfile);
1219 if (c == '\n')
1220 return 1;
1222 return 0;
1226 vfs_s_get_line_interruptible (struct vfs_class *me, char *buffer, int size, int fd)
1228 int n;
1229 int i;
1231 (void) me;
1233 enable_interrupt_key ();
1234 for (i = 0; i < size-1; i++){
1235 n = read (fd, buffer+i, 1);
1236 disable_interrupt_key ();
1237 if (n == -1 && errno == EINTR){
1238 buffer [i] = 0;
1239 return EINTR;
1241 if (n == 0){
1242 buffer [i] = 0;
1243 return 0;
1245 if (buffer [i] == '\n'){
1246 buffer [i] = 0;
1247 return 1;
1250 buffer [size-1] = 0;
1251 return 0;
1253 #endif /* USE_NETCODE */