Ticket #1395 (Copying to fish is broken)
[midnight-commander.git] / vfs / direntry.c
blobfae1c5a28c2a2cb23d9eb8b25599e77e6d2bb6a3
2 /** \file
3 * \brief Source: directory cache support
5 * So that you do not have copy of this in each and every filesystem.
7 * Very loosely based on tar.c from midnight and archives.[ch] from
8 * avfs by Miklos Szeredi (mszeredi@inf.bme.hu)
10 * Unfortunately, I was unable to keep all filesystems
11 * uniform. tar-like filesystems use tree structure where each
12 * directory has pointers to its subdirectories. We can do this
13 * because we have full information about our archive.
15 * At ftp-like filesystems, situation is a little bit different. When
16 * you cd /usr/src/linux/drivers/char, you do _not_ want /usr,
17 * /usr/src, /usr/src/linux and /usr/src/linux/drivers to be
18 * listed. That means that we do not have complete information, and if
19 * /usr is symlink to /4, we will not know. Also we have to time out
20 * entries and things would get messy with tree-like approach. So we
21 * do different trick: root directory is completely special and
22 * completely fake, it contains entries such as 'usr', 'usr/src', ...,
23 * and we'll try to use custom find_entry function.
25 * \author Pavel Machek <pavel@ucw.cz>, distribute under LGPL.
26 * \date 1998
28 * \warning Paths here do _not_ begin with '/', so root directory of
29 * archive/site is simply "".
32 #include <config.h>
34 #include <errno.h>
35 #include <sys/fcntl.h>
36 #include <time.h>
37 #include <sys/time.h> /* gettimeofday() */
39 #include "../src/global.h"
40 #include "../src/tty.h" /* enable/disable interrupt key */
41 #include "../src/wtools.h" /* message() */
42 #include "../src/main.h" /* print_vfs_message */
43 #include "utilvfs.h"
44 #include "vfs-impl.h"
45 #include "gc.h" /* vfs_rmstamp */
46 #include "xdirentry.h"
48 #define CALL(x) if (MEDATA->x) MEDATA->x
50 static volatile int total_inodes = 0, total_entries = 0;
52 struct vfs_s_inode *
53 vfs_s_new_inode (struct vfs_class *me, struct vfs_s_super *super, struct stat *initstat)
55 struct vfs_s_inode *ino;
57 ino = g_new0 (struct vfs_s_inode, 1);
58 if (!ino)
59 return NULL;
61 if (initstat)
62 ino->st = *initstat;
63 ino->super = super;
64 ino->st.st_nlink = 0;
65 ino->st.st_ino = MEDATA->inode_counter++;
66 ino->st.st_dev = MEDATA->rdev;
68 super->ino_usage++;
69 total_inodes++;
71 CALL (init_inode) (me, ino);
73 return ino;
76 struct vfs_s_entry *
77 vfs_s_new_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *inode)
79 struct vfs_s_entry *entry;
81 entry = g_new0 (struct vfs_s_entry, 1);
82 total_entries++;
84 if (name)
85 entry->name = g_strdup (name);
87 entry->ino = inode;
88 entry->ino->ent = entry;
89 CALL (init_entry) (me, entry);
91 return entry;
94 static void
95 vfs_s_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
97 if (!ino)
98 vfs_die ("Don't pass NULL to me");
100 /* ==0 can happen if freshly created entry is deleted */
101 if (ino->st.st_nlink <= 1){
102 while (ino->subdir){
103 vfs_s_free_entry (me, ino->subdir);
106 CALL (free_inode) (me, ino);
107 g_free (ino->linkname);
108 if (ino->localname){
109 unlink (ino->localname);
110 g_free(ino->localname);
112 total_inodes--;
113 ino->super->ino_usage--;
114 g_free(ino);
115 } else ino->st.st_nlink--;
118 void
119 vfs_s_free_entry (struct vfs_class *me, struct vfs_s_entry *ent)
121 if (ent->prevp){ /* It is possible that we are deleting freshly created entry */
122 *ent->prevp = ent->next;
123 if (ent->next)
124 ent->next->prevp = ent->prevp;
127 g_free (ent->name);
128 ent->name = NULL;
130 if (ent->ino){
131 ent->ino->ent = NULL;
132 vfs_s_free_inode (me, ent->ino);
133 ent->ino = NULL;
136 total_entries--;
137 g_free(ent);
140 void
141 vfs_s_insert_entry (struct vfs_class *me, struct vfs_s_inode *dir, struct vfs_s_entry *ent)
143 struct vfs_s_entry **ep;
145 (void) me;
147 for (ep = &dir->subdir; *ep != NULL; ep = &((*ep)->next))
149 ent->prevp = ep;
150 ent->next = NULL;
151 ent->dir = dir;
152 *ep = ent;
154 ent->ino->st.st_nlink++;
157 struct stat *
158 vfs_s_default_stat (struct vfs_class *me, mode_t mode)
160 static struct stat st;
161 int myumask;
163 (void) me;
165 myumask = umask (022);
166 umask (myumask);
167 mode &= ~myumask;
169 st.st_mode = mode;
170 st.st_ino = 0;
171 st.st_dev = 0;
172 st.st_rdev = 0;
173 st.st_uid = getuid ();
174 st.st_gid = getgid ();
175 st.st_size = 0;
176 st.st_mtime = st.st_atime = st.st_ctime = time (NULL);
178 return &st;
181 struct vfs_s_entry *
182 vfs_s_generate_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *parent, mode_t mode)
184 struct vfs_s_inode *inode;
185 struct stat *st;
187 st = vfs_s_default_stat (me, mode);
188 inode = vfs_s_new_inode (me, parent->super, st);
190 return vfs_s_new_entry (me, name, inode);
193 /* We were asked to create entries automagically */
194 static struct vfs_s_entry *
195 vfs_s_automake (struct vfs_class *me, struct vfs_s_inode *dir, char *path, int flags)
197 struct vfs_s_entry *res;
198 char *sep = strchr (path, PATH_SEP);
200 if (sep)
201 *sep = 0;
202 res = vfs_s_generate_entry (me, path, dir, flags & FL_MKDIR ? (0777 | S_IFDIR) : 0777);
203 vfs_s_insert_entry (me, dir, res);
205 if (sep)
206 *sep = PATH_SEP;
208 return res;
211 /* If the entry is a symlink, find the entry for its target */
212 static struct vfs_s_entry *
213 vfs_s_resolve_symlink (struct vfs_class *me, struct vfs_s_entry *entry,
214 int follow)
216 char *linkname;
217 char *fullname = NULL;
218 struct vfs_s_entry *target;
220 if (follow == LINK_NO_FOLLOW)
221 return entry;
222 if (follow == 0)
223 ERRNOR (ELOOP, NULL);
224 if (!entry)
225 ERRNOR (ENOENT, NULL);
226 if (!S_ISLNK (entry->ino->st.st_mode))
227 return entry;
229 linkname = entry->ino->linkname;
230 if (linkname == NULL)
231 ERRNOR (EFAULT, NULL);
233 /* make full path from relative */
234 if (*linkname != PATH_SEP) {
235 char *fullpath = vfs_s_fullpath (me, entry->dir);
236 if (fullpath) {
237 fullname = g_strconcat (fullpath, "/", linkname, NULL);
238 linkname = fullname;
239 g_free (fullpath);
243 target =
244 (MEDATA->find_entry) (me, entry->dir->super->root, linkname,
245 follow - 1, 0);
246 g_free (fullname);
247 return target;
251 * Follow > 0: follow links, serves as loop protect,
252 * == -1: do not follow links
254 static struct vfs_s_entry *
255 vfs_s_find_entry_tree (struct vfs_class *me, struct vfs_s_inode *root,
256 const char *a_path, int follow, int flags)
258 size_t pseg;
259 struct vfs_s_entry *ent = NULL;
260 char * const pathref = g_strdup (a_path);
261 char *path = pathref;
263 canonicalize_pathname (path);
265 while (root) {
266 while (*path == PATH_SEP) /* Strip leading '/' */
267 path++;
269 if (!path[0]) {
270 g_free (pathref);
271 return ent;
274 for (pseg = 0; path[pseg] && path[pseg] != PATH_SEP; pseg++);
276 for (ent = root->subdir; ent != NULL; ent = ent->next)
277 if (strlen (ent->name) == pseg
278 && (!strncmp (ent->name, path, pseg)))
279 /* FOUND! */
280 break;
282 if (!ent && (flags & (FL_MKFILE | FL_MKDIR)))
283 ent = vfs_s_automake (me, root, path, flags);
284 if (!ent) {
285 me->verrno = ENOENT;
286 goto cleanup;
288 path += pseg;
289 /* here we must follow leading directories always;
290 only the actual file is optional */
291 ent =
292 vfs_s_resolve_symlink (me, ent,
293 strchr (path,
294 PATH_SEP) ? LINK_FOLLOW :
295 follow);
296 if (!ent)
297 goto cleanup;
298 root = ent->ino;
300 cleanup:
301 g_free (pathref);
302 return NULL;
305 static void
306 split_dir_name (struct vfs_class *me, char *path, char **dir, char **name, char **save)
308 char *s;
310 (void) me;
312 s = strrchr (path, PATH_SEP);
313 if (s == NULL) {
314 *save = NULL;
315 *name = path;
316 *dir = path + strlen(path); /* an empty string */
317 } else {
318 *save = s;
319 *dir = path;
320 *s++ = '\0';
321 *name = s;
325 static struct vfs_s_entry *
326 vfs_s_find_entry_linear (struct vfs_class *me, struct vfs_s_inode *root,
327 const char *a_path, int follow, int flags)
329 struct vfs_s_entry *ent = NULL;
330 char * const path = g_strdup (a_path);
331 struct vfs_s_entry *retval = NULL;
333 if (root->super->root != root)
334 vfs_die ("We have to use _real_ root. Always. Sorry.");
336 canonicalize_pathname (path);
338 if (!(flags & FL_DIR)) {
339 char *dirname, *name, *save;
340 struct vfs_s_inode *ino;
341 split_dir_name (me, path, &dirname, &name, &save);
342 ino =
343 vfs_s_find_inode (me, root->super, dirname, follow,
344 flags | FL_DIR);
345 if (save)
346 *save = PATH_SEP;
347 retval = vfs_s_find_entry_tree (me, ino, name, follow, flags);
348 g_free (path);
349 return retval;
352 for (ent = root->subdir; ent != NULL; ent = ent->next)
353 if (!strcmp (ent->name, path))
354 break;
356 if (ent && (!(MEDATA->dir_uptodate) (me, ent->ino))) {
357 #if 1
358 print_vfs_message (_("Directory cache expired for %s"), path);
359 #endif
360 vfs_s_free_entry (me, ent);
361 ent = NULL;
364 if (!ent) {
365 struct vfs_s_inode *ino;
367 ino =
368 vfs_s_new_inode (me, root->super,
369 vfs_s_default_stat (me, S_IFDIR | 0755));
370 ent = vfs_s_new_entry (me, path, ino);
371 if ((MEDATA->dir_load) (me, ino, path) == -1) {
372 vfs_s_free_entry (me, ent);
373 g_free (path);
374 return NULL;
376 vfs_s_insert_entry (me, root, ent);
378 for (ent = root->subdir; ent != NULL; ent = ent->next)
379 if (!strcmp (ent->name, path))
380 break;
382 if (!ent)
383 vfs_die ("find_linear: success but directory is not there\n");
385 #if 0
386 if (!vfs_s_resolve_symlink (me, ent, follow)) {
387 g_free (path);
388 return NULL;
390 #endif
391 g_free (path);
392 return ent;
395 struct vfs_s_inode *
396 vfs_s_find_inode (struct vfs_class *me, const struct vfs_s_super *super,
397 const char *path, int follow, int flags)
399 struct vfs_s_entry *ent;
400 if (!(MEDATA->flags & VFS_S_REMOTE) && (!*path))
401 return super->root;
402 ent = (MEDATA->find_entry) (me, super->root, path, follow, flags);
403 if (!ent)
404 return NULL;
405 return ent->ino;
408 /* Ook, these were functions around directory entries / inodes */
409 /* -------------------------------- superblock games -------------------------- */
411 static struct vfs_s_super *
412 vfs_s_new_super (struct vfs_class *me)
414 struct vfs_s_super *super;
416 super = g_new0 (struct vfs_s_super, 1);
417 super->me = me;
418 return super;
421 static void
422 vfs_s_insert_super (struct vfs_class *me, struct vfs_s_super *super)
424 super->next = MEDATA->supers;
425 super->prevp = &MEDATA->supers;
427 if (MEDATA->supers != NULL)
428 MEDATA->supers->prevp = &super->next;
429 MEDATA->supers = super;
432 static void
433 vfs_s_free_super (struct vfs_class *me, struct vfs_s_super *super)
435 if (super->root){
436 vfs_s_free_inode (me, super->root);
437 super->root = NULL;
440 #if 0
441 /* FIXME: We currently leak small ammount of memory, sometimes. Fix it if you can. */
442 if (super->ino_usage)
443 message (D_ERROR, " Direntry warning ",
444 "Super ino_usage is %d, memory leak",
445 super->ino_usage);
447 if (super->want_stale)
448 message (D_ERROR, " Direntry warning ", "Super has want_stale set");
449 #endif
451 if (super->prevp){
452 *super->prevp = super->next;
453 if (super->next)
454 super->next->prevp = super->prevp;
457 CALL (free_archive) (me, super);
458 g_free (super->name);
459 g_free(super);
464 * Dissect the path and create corresponding superblock. Note that inname
465 * can be changed and the result may point inside the original string.
467 const char *
468 vfs_s_get_path_mangle (struct vfs_class *me, char *inname,
469 struct vfs_s_super **archive, int flags)
471 const char *retval;
472 char *local, *op;
473 const char *archive_name;
474 int result = -1;
475 struct vfs_s_super *super;
476 void *cookie = NULL;
478 archive_name = inname;
479 vfs_split (inname, &local, &op);
480 retval = (local) ? local : "";
482 if (MEDATA->archive_check)
483 if (!(cookie = MEDATA->archive_check (me, archive_name, op)))
484 return NULL;
486 for (super = MEDATA->supers; super != NULL; super = super->next) {
487 /* 0 == other, 1 == same, return it, 2 == other but stop scanning */
488 int i;
489 if ((i =
490 MEDATA->archive_same (me, super, archive_name, op, cookie))) {
491 if (i == 1)
492 goto return_success;
493 else
494 break;
498 if (flags & FL_NO_OPEN)
499 ERRNOR (EIO, NULL);
501 super = vfs_s_new_super (me);
502 result = MEDATA->open_archive (me, super, archive_name, op);
503 if (result == -1) {
504 vfs_s_free_super (me, super);
505 ERRNOR (EIO, NULL);
507 if (!super->name)
508 vfs_die ("You have to fill name\n");
509 if (!super->root)
510 vfs_die ("You have to fill root inode\n");
512 vfs_s_insert_super (me, super);
513 vfs_stamp_create (me, super);
515 return_success:
516 *archive = super;
517 return retval;
522 * Dissect the path and create corresponding superblock.
523 * The result should be freed.
525 static char *
526 vfs_s_get_path (struct vfs_class *me, const char *inname,
527 struct vfs_s_super **archive, int flags)
529 char *buf, *retval;
531 buf = g_strdup (inname);
532 retval = g_strdup (vfs_s_get_path_mangle (me, buf, archive, flags));
533 g_free (buf);
534 return retval;
537 void
538 vfs_s_invalidate (struct vfs_class *me, struct vfs_s_super *super)
540 if (!super->want_stale){
541 vfs_s_free_inode (me, super->root);
542 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
546 char *
547 vfs_s_fullpath (struct vfs_class *me, struct vfs_s_inode *ino)
549 if (!ino->ent)
550 ERRNOR (EAGAIN, NULL);
552 if (!(MEDATA->flags & VFS_S_REMOTE)) {
553 /* archives */
554 char *newpath;
555 char *path = g_strdup (ino->ent->name);
556 while (1) {
557 ino = ino->ent->dir;
558 if (ino == ino->super->root)
559 break;
560 newpath = g_strconcat (ino->ent->name, "/", path, (char *) NULL);
561 g_free (path);
562 path = newpath;
564 return path;
567 /* remote systems */
568 if ((!ino->ent->dir) || (!ino->ent->dir->ent))
569 return g_strdup (ino->ent->name);
571 return g_strconcat (ino->ent->dir->ent->name, PATH_SEP_STR,
572 ino->ent->name, (char *) NULL);
575 /* Support of archives */
576 /* ------------------------ readdir & friends ----------------------------- */
578 static struct vfs_s_inode *
579 vfs_s_inode_from_path (struct vfs_class *me, const char *name, int flags)
581 struct vfs_s_super *super;
582 struct vfs_s_inode *ino;
583 char *q;
585 if (!(q = vfs_s_get_path (me, name, &super, 0)))
586 return NULL;
588 ino =
589 vfs_s_find_inode (me, super, q,
590 flags & FL_FOLLOW ? LINK_FOLLOW : LINK_NO_FOLLOW,
591 flags & ~FL_FOLLOW);
592 if ((!ino) && (!*q))
593 /* We are asking about / directory of ftp server: assume it exists */
594 ino =
595 vfs_s_find_inode (me, super, q,
596 flags & FL_FOLLOW ? LINK_FOLLOW :
597 LINK_NO_FOLLOW,
598 FL_DIR | (flags & ~FL_FOLLOW));
599 g_free (q);
600 return ino;
603 struct dirhandle {
604 struct vfs_s_entry *cur;
605 struct vfs_s_inode *dir;
608 static void *
609 vfs_s_opendir (struct vfs_class *me, const char *dirname)
611 struct vfs_s_inode *dir;
612 struct dirhandle *info;
614 dir = vfs_s_inode_from_path (me, dirname, FL_DIR | FL_FOLLOW);
615 if (!dir)
616 return NULL;
617 if (!S_ISDIR (dir->st.st_mode))
618 ERRNOR (ENOTDIR, NULL);
620 dir->st.st_nlink++;
621 #if 0
622 if (!dir->subdir) /* This can actually happen if we allow empty directories */
623 ERRNOR (EAGAIN, NULL);
624 #endif
625 info = g_new (struct dirhandle, 1);
626 info->cur = dir->subdir;
627 info->dir = dir;
629 return info;
632 static void *
633 vfs_s_readdir(void *data)
635 static union vfs_dirent dir;
636 struct dirhandle *info = (struct dirhandle *) data;
638 if (!(info->cur))
639 return NULL;
641 if (info->cur->name) {
642 g_strlcpy (dir.dent.d_name, info->cur->name, MC_MAXPATHLEN);
643 } else {
644 vfs_die("Null in structure-cannot happen");
647 compute_namelen(&dir.dent);
648 info->cur = info->cur->next;
650 return (void *) &dir;
653 static int
654 vfs_s_closedir (void *data)
656 struct dirhandle *info = (struct dirhandle *) data;
657 struct vfs_s_inode *dir = info->dir;
659 vfs_s_free_inode (dir->super->me, dir);
660 g_free (data);
661 return 0;
664 static int
665 vfs_s_chdir (struct vfs_class *me, const char *path)
667 void *data;
668 if (!(data = vfs_s_opendir (me, path)))
669 return -1;
670 vfs_s_closedir (data);
671 return 0;
674 /* --------------------------- stat and friends ---------------------------- */
676 static int
677 vfs_s_internal_stat (struct vfs_class *me, const char *path, struct stat *buf, int flag)
679 struct vfs_s_inode *ino;
681 if (!(ino = vfs_s_inode_from_path (me, path, flag)))
682 return -1;
683 *buf = ino->st;
684 return 0;
687 static int
688 vfs_s_stat (struct vfs_class *me, const char *path, struct stat *buf)
690 return vfs_s_internal_stat (me, path, buf, FL_FOLLOW);
693 static int
694 vfs_s_lstat (struct vfs_class *me, const char *path, struct stat *buf)
696 return vfs_s_internal_stat (me, path, buf, FL_NONE);
699 static int
700 vfs_s_fstat (void *fh, struct stat *buf)
702 *buf = FH->ino->st;
703 return 0;
706 static int
707 vfs_s_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
709 struct vfs_s_inode *ino;
710 size_t len;
712 ino = vfs_s_inode_from_path (me, path, 0);
713 if (!ino)
714 return -1;
716 if (!S_ISLNK (ino->st.st_mode))
717 ERRNOR (EINVAL, -1);
719 if (ino->linkname == NULL)
720 ERRNOR (EFAULT, -1);
722 len = strlen (ino->linkname);
723 if (size < len)
724 len = size;
725 /* readlink() does not append a NUL character to buf */
726 memcpy (buf, ino->linkname, len);
727 return len;
730 void *
731 vfs_s_open (struct vfs_class *me, const char *file, int flags, int mode)
733 int was_changed = 0;
734 struct vfs_s_fh *fh;
735 struct vfs_s_super *super;
736 char *q;
737 struct vfs_s_inode *ino;
739 if ((q = vfs_s_get_path (me, file, &super, 0)) == NULL)
740 return NULL;
741 ino = vfs_s_find_inode (me, super, q, LINK_FOLLOW, FL_NONE);
742 if (ino && ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))) {
743 g_free (q);
744 ERRNOR (EEXIST, NULL);
746 if (!ino) {
747 char *dirname, *name, *save;
748 struct vfs_s_entry *ent;
749 struct vfs_s_inode *dir;
750 int tmp_handle;
752 /* If the filesystem is read-only, disable file creation */
753 if (!(flags & O_CREAT) || !(me->write)) {
754 g_free (q);
755 return NULL;
758 split_dir_name (me, q, &dirname, &name, &save);
759 /* FIXME: check if vfs_s_find_inode returns NULL */
760 dir = vfs_s_find_inode (me, super, dirname, LINK_FOLLOW, FL_DIR);
761 if (save)
762 *save = PATH_SEP;
763 ent = vfs_s_generate_entry (me, name, dir, 0755);
764 ino = ent->ino;
765 vfs_s_insert_entry (me, dir, ent);
766 tmp_handle = vfs_mkstemps (&ino->localname, me->name, name);
767 if (tmp_handle == -1) {
768 g_free (q);
769 return NULL;
771 close (tmp_handle);
772 was_changed = 1;
775 g_free (q);
777 if (S_ISDIR (ino->st.st_mode))
778 ERRNOR (EISDIR, NULL);
780 fh = g_new (struct vfs_s_fh, 1);
781 fh->pos = 0;
782 fh->ino = ino;
783 fh->handle = -1;
784 fh->changed = was_changed;
785 fh->linear = 0;
787 if (IS_LINEAR (flags)) {
788 if (MEDATA->linear_start) {
789 print_vfs_message (_("Starting linear transfer..."));
790 fh->linear = LS_LINEAR_PREOPEN;
792 } else if ((MEDATA->fh_open)
793 && (MEDATA->fh_open (me, fh, flags, mode))) {
794 g_free (fh);
795 return NULL;
798 if (fh->ino->localname) {
799 fh->handle = open (fh->ino->localname, NO_LINEAR (flags), mode);
800 if (fh->handle == -1) {
801 g_free (fh);
802 ERRNOR (errno, NULL);
806 /* i.e. we had no open files and now we have one */
807 vfs_rmstamp (me, (vfsid) super);
808 super->fd_usage++;
809 fh->ino->st.st_nlink++;
810 return fh;
813 static ssize_t
814 vfs_s_read (void *fh, char *buffer, int count)
816 int n;
817 struct vfs_class *me = FH_SUPER->me;
819 if (FH->linear == LS_LINEAR_PREOPEN) {
820 if (!MEDATA->linear_start (me, FH, FH->pos))
821 return -1;
824 if (FH->linear == LS_LINEAR_CLOSED)
825 vfs_die ("linear_start() did not set linear_state!");
827 if (FH->linear == LS_LINEAR_OPEN)
828 return MEDATA->linear_read (me, FH, buffer, count);
830 if (FH->handle != -1){
831 n = read (FH->handle, buffer, count);
832 if (n < 0)
833 me->verrno = errno;
834 return n;
836 vfs_die ("vfs_s_read: This should not happen\n");
837 return -1;
840 static ssize_t
841 vfs_s_write (void *fh, const char *buffer, int count)
843 int n;
844 struct vfs_class *me = FH_SUPER->me;
846 if (FH->linear)
847 vfs_die ("no writing to linear files, please");
849 FH->changed = 1;
850 if (FH->handle != -1){
851 n = write (FH->handle, buffer, count);
852 if (n < 0)
853 me->verrno = errno;
854 return n;
856 vfs_die ("vfs_s_write: This should not happen\n");
857 return 0;
860 static off_t
861 vfs_s_lseek (void *fh, off_t offset, int whence)
863 off_t size = FH->ino->st.st_size;
865 if (FH->linear == LS_LINEAR_OPEN)
866 vfs_die ("cannot lseek() after linear_read!");
868 if (FH->handle != -1){ /* If we have local file opened, we want to work with it */
869 int retval = lseek (FH->handle, offset, whence);
870 if (retval == -1)
871 FH->ino->super->me->verrno = errno;
872 return retval;
875 switch (whence){
876 case SEEK_CUR:
877 offset += FH->pos; break;
878 case SEEK_END:
879 offset += size; break;
881 if (offset < 0)
882 FH->pos = 0;
883 else if (offset < size)
884 FH->pos = offset;
885 else
886 FH->pos = size;
887 return FH->pos;
890 static int
891 vfs_s_close (void *fh)
893 int res = 0;
894 struct vfs_class *me = FH_SUPER->me;
896 FH_SUPER->fd_usage--;
897 if (!FH_SUPER->fd_usage)
898 vfs_stamp_create (me, FH_SUPER);
900 if (FH->linear == LS_LINEAR_OPEN)
901 MEDATA->linear_close (me, fh);
902 if (MEDATA->fh_close)
903 res = MEDATA->fh_close (me, fh);
904 if (FH->changed && MEDATA->file_store){
905 char *s = vfs_s_fullpath (me, FH->ino);
906 if (!s)
907 res = -1;
908 else {
909 res = MEDATA->file_store (me, fh, s, FH->ino->localname);
910 g_free (s);
912 vfs_s_invalidate (me, FH_SUPER);
914 if (FH->handle != -1)
915 close (FH->handle);
917 vfs_s_free_inode (me, FH->ino);
918 g_free (fh);
919 return res;
922 static void
923 vfs_s_print_stats (const char *fs_name, const char *action,
924 const char *file_name, off_t have, off_t need)
926 static const char *i18n_percent_transf_format = NULL;
927 static const char *i18n_transf_format = NULL;
929 if (i18n_percent_transf_format == NULL) {
930 i18n_percent_transf_format =
931 _("%s: %s: %s %3d%% (%lu bytes transferred)");
932 i18n_transf_format = _("%s: %s: %s %lu bytes transferred");
935 if (need)
936 print_vfs_message (i18n_percent_transf_format, fs_name, action,
937 file_name, (int) ((double) have * 100 / need),
938 (unsigned long) have);
939 else
940 print_vfs_message (i18n_transf_format, fs_name, action, file_name,
941 (unsigned long) have);
945 vfs_s_retrieve_file (struct vfs_class *me, struct vfs_s_inode *ino)
947 /* If you want reget, you'll have to open file with O_LINEAR */
948 off_t total = 0;
949 char buffer[8192];
950 int handle, n;
951 off_t stat_size = ino->st.st_size;
952 struct vfs_s_fh fh;
954 memset (&fh, 0, sizeof (fh));
956 fh.ino = ino;
957 fh.handle = -1;
959 handle = vfs_mkstemps (&ino->localname, me->name, ino->ent->name);
960 if (handle == -1) {
961 me->verrno = errno;
962 goto error_4;
965 if (!MEDATA->linear_start (me, &fh, 0))
966 goto error_3;
968 /* Clear the interrupt status */
969 got_interrupt ();
970 enable_interrupt_key ();
972 while ((n = MEDATA->linear_read (me, &fh, buffer, sizeof (buffer)))) {
973 int t;
974 if (n < 0)
975 goto error_1;
977 total += n;
978 vfs_s_print_stats (me->name, _("Getting file"), ino->ent->name,
979 total, stat_size);
981 if (got_interrupt ())
982 goto error_1;
984 t = write (handle, buffer, n);
985 if (t != n) {
986 if (t == -1)
987 me->verrno = errno;
988 goto error_1;
991 MEDATA->linear_close (me, &fh);
992 close (handle);
994 disable_interrupt_key ();
995 return 0;
997 error_1:
998 MEDATA->linear_close (me, &fh);
999 error_3:
1000 disable_interrupt_key ();
1001 close (handle);
1002 unlink (ino->localname);
1003 error_4:
1004 g_free (ino->localname);
1005 ino->localname = NULL;
1006 return -1;
1009 /* ------------------------------- mc support ---------------------------- */
1011 static void
1012 vfs_s_fill_names (struct vfs_class *me, fill_names_f func)
1014 struct vfs_s_super *a = MEDATA->supers;
1015 char *name;
1017 while (a){
1018 name = g_strconcat ( a->name, "#", me->prefix, "/", /* a->current_dir->name, */ NULL);
1019 (*func)(name);
1020 g_free (name);
1021 a = a->next;
1025 static int
1026 vfs_s_ferrno (struct vfs_class *me)
1028 return me->verrno;
1032 * Get local copy of the given file. We reuse the existing file cache
1033 * for remote filesystems. Archives use standard VFS facilities.
1035 static char *
1036 vfs_s_getlocalcopy (struct vfs_class *me, const char *path)
1038 struct vfs_s_fh *fh;
1039 char *local;
1041 fh = vfs_s_open (me, path, O_RDONLY, 0);
1042 if (!fh || !fh->ino || !fh->ino->localname)
1043 return NULL;
1045 local = g_strdup (fh->ino->localname);
1046 vfs_s_close (fh);
1047 return local;
1051 * Return the local copy. Since we are using our cache, we do nothing -
1052 * the cache will be removed when the archive is closed.
1054 static int
1055 vfs_s_ungetlocalcopy (struct vfs_class *me, const char *path,
1056 const char *local, int has_changed)
1058 (void) me;
1059 (void) path;
1060 (void) local;
1061 (void) has_changed;
1062 return 0;
1065 static int
1066 vfs_s_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1068 switch (ctlop) {
1069 case VFS_SETCTL_STALE_DATA:
1071 struct vfs_s_inode *ino = vfs_s_inode_from_path (me, path, 0);
1073 if (!ino)
1074 return 0;
1075 if (arg)
1076 ino->super->want_stale = 1;
1077 else {
1078 ino->super->want_stale = 0;
1079 vfs_s_invalidate (me, ino->super);
1081 return 1;
1083 case VFS_SETCTL_LOGFILE:
1084 MEDATA->logfile = fopen ((char *) arg, "w");
1085 return 1;
1086 case VFS_SETCTL_FLUSH:
1087 MEDATA->flush = 1;
1088 return 1;
1090 return 0;
1094 /* ----------------------------- Stamping support -------------------------- */
1096 static vfsid
1097 vfs_s_getid (struct vfs_class *me, const char *path)
1099 struct vfs_s_super *archive;
1100 char *p;
1102 if (!(p = vfs_s_get_path (me, path, &archive, FL_NO_OPEN)))
1103 return NULL;
1104 g_free(p);
1105 return (vfsid) archive;
1108 static int
1109 vfs_s_nothingisopen (vfsid id)
1111 (void) id;
1112 /* Our data structures should survive free of superblock at any time */
1113 return 1;
1116 static void
1117 vfs_s_free (vfsid id)
1119 vfs_s_free_super (((struct vfs_s_super *)id)->me, (struct vfs_s_super *)id);
1122 static int
1123 vfs_s_dir_uptodate (struct vfs_class *me, struct vfs_s_inode *ino)
1125 struct timeval tim;
1127 if (MEDATA->flush) {
1128 MEDATA->flush = 0;
1129 return 0;
1132 gettimeofday(&tim, NULL);
1133 if (tim.tv_sec < ino->timestamp.tv_sec)
1134 return 1;
1135 return 0;
1138 /* Initialize one of our subclasses - fill common functions */
1139 void
1140 vfs_s_init_class (struct vfs_class *vclass, struct vfs_s_subclass *sub)
1142 vclass->data = sub;
1143 vclass->fill_names = vfs_s_fill_names;
1144 vclass->open = vfs_s_open;
1145 vclass->close = vfs_s_close;
1146 vclass->read = vfs_s_read;
1147 if (!(sub->flags & VFS_S_READONLY)) {
1148 vclass->write = vfs_s_write;
1150 vclass->opendir = vfs_s_opendir;
1151 vclass->readdir = vfs_s_readdir;
1152 vclass->closedir = vfs_s_closedir;
1153 vclass->stat = vfs_s_stat;
1154 vclass->lstat = vfs_s_lstat;
1155 vclass->fstat = vfs_s_fstat;
1156 vclass->readlink = vfs_s_readlink;
1157 vclass->chdir = vfs_s_chdir;
1158 vclass->ferrno = vfs_s_ferrno;
1159 vclass->lseek = vfs_s_lseek;
1160 vclass->getid = vfs_s_getid;
1161 vclass->nothingisopen = vfs_s_nothingisopen;
1162 vclass->free = vfs_s_free;
1163 if (sub->flags & VFS_S_REMOTE) {
1164 vclass->getlocalcopy = vfs_s_getlocalcopy;
1165 vclass->ungetlocalcopy = vfs_s_ungetlocalcopy;
1166 sub->find_entry = vfs_s_find_entry_linear;
1167 } else {
1168 sub->find_entry = vfs_s_find_entry_tree;
1170 vclass->setctl = vfs_s_setctl;
1171 sub->dir_uptodate = vfs_s_dir_uptodate;
1174 /* ----------- Utility functions for networked filesystems -------------- */
1176 #ifdef USE_NETCODE
1178 vfs_s_select_on_two (int fd1, int fd2)
1180 fd_set set;
1181 struct timeval timeout;
1182 int v;
1183 int maxfd = (fd1 > fd2 ? fd1 : fd2) + 1;
1185 timeout.tv_sec = 1;
1186 timeout.tv_usec = 0;
1187 FD_ZERO (&set);
1188 FD_SET (fd1, &set);
1189 FD_SET (fd2, &set);
1190 v = select (maxfd, &set, 0, 0, &timeout);
1191 if (v <= 0)
1192 return v;
1193 if (FD_ISSET (fd1, &set))
1194 return 1;
1195 if (FD_ISSET (fd2, &set))
1196 return 2;
1197 return -1;
1201 vfs_s_get_line (struct vfs_class *me, int sock, char *buf, int buf_len, char term)
1203 FILE *logfile = MEDATA->logfile;
1204 int i;
1205 char c;
1207 for (i = 0; i < buf_len - 1; i++, buf++){
1208 if (read (sock, buf, sizeof(char)) <= 0)
1209 return 0;
1210 if (logfile){
1211 fwrite (buf, 1, 1, logfile);
1212 fflush (logfile);
1214 if (*buf == term){
1215 *buf = 0;
1216 return 1;
1220 /* Line is too long - terminate buffer and discard the rest of line */
1221 *buf = 0;
1222 while (read (sock, &c, sizeof (c)) > 0) {
1223 if (logfile){
1224 fwrite (&c, 1, 1, logfile);
1225 fflush (logfile);
1227 if (c == '\n')
1228 return 1;
1230 return 0;
1234 vfs_s_get_line_interruptible (struct vfs_class *me, char *buffer, int size, int fd)
1236 int n;
1237 int i;
1239 (void) me;
1241 enable_interrupt_key ();
1242 for (i = 0; i < size-1; i++){
1243 n = read (fd, buffer+i, 1);
1244 disable_interrupt_key ();
1245 if (n == -1 && errno == EINTR){
1246 buffer [i] = 0;
1247 return EINTR;
1249 if (n == 0){
1250 buffer [i] = 0;
1251 return 0;
1253 if (buffer [i] == '\n'){
1254 buffer [i] = 0;
1255 return 1;
1258 buffer [size-1] = 0;
1259 return 0;
1261 #endif /* USE_NETCODE */