From 4706638b225ae2e76071d7df18d9a1e75665c9a9 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Sat, 21 Nov 1998 19:36:01 +0000 Subject: [PATCH] Big merge: fish now uses proper caching, small fixes almost everywhere. I hope I did not break it too much. --- config.h.in | 12 +- gtkedit/editcmd.c | 4 - lib/mc.ext.in | 6 +- src/ChangeLog | 4 + src/mad.c | 9 + src/mad.h | 5 + vfs/ChangeLog | 12 + vfs/Make-mc.in | 6 +- vfs/Makefile.in | 6 +- vfs/direntry.c | 572 +++++++++++++++++++++----- vfs/fish.c | 906 ++++++++++++++++++++---------------------- vfs/fish.h | 66 +-- vfs/ftpfs.c | 13 +- vfs/mcfs.c | 2 +- vfs/shared_ftp_fish.c | 2 + vfs/tar.c | 37 +- vfs/utilvfs.c | 25 +- vfs/vfs.c | 37 +- vfs/vfs.h | 31 +- vfs/xdirentry.h | 98 ++++- 20 files changed, 1114 insertions(+), 739 deletions(-) diff --git a/config.h.in b/config.h.in index 0ec3aeb0f..3798afe54 100644 --- a/config.h.in +++ b/config.h.in @@ -101,9 +101,9 @@ /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ #undef STACK_DIRECTION @@ -113,9 +113,6 @@ /* Define to `int' if doesn't define. */ #undef uid_t -/* Define if the X Window System is missing or not being used. */ -#undef X_DISPLAY_MISSING - #undef VERSION #undef PACKAGE @@ -408,6 +405,9 @@ /* Define if you have the truncate function. */ #undef HAVE_TRUNCATE +/* Define if you have the valloc function. */ +#undef HAVE_VALLOC + /* Define if you have the header file. */ #undef HAVE_ARGZ_H diff --git a/gtkedit/editcmd.c b/gtkedit/editcmd.c index 1c872194f..8f7a3bc96 100644 --- a/gtkedit/editcmd.c +++ b/gtkedit/editcmd.c @@ -260,11 +260,7 @@ int edit_save_file (WEdit * edit, const char *filename) return 0; savedir[slashpos - filename + 1] = '\0'; } -#ifdef HAVE_MAD - savename = strdup (tempnam (savedir, "cooledit")); -#else savename = tempnam (savedir, "cooledit"); -#endif if (slashpos) free (savedir); if (!savename) diff --git a/lib/mc.ext.in b/lib/mc.ext.in index bc50a1c80..abc68d36a 100644 --- a/lib/mc.ext.in +++ b/lib/mc.ext.in @@ -543,19 +543,19 @@ type/Microsoft Word Document # cpio rules shell/.cpio.Z - Open=%cd cpio:%d/%p/ + Open=%cd %d/%p#ucpio/ View=%view{ascii} compress -dc '%f' | cpio -ictv Extract=compress -dc '%f' | cpio -ic Icon=compressed.xpm shell/.cpio.gz - Open=%cd cpio:%d/%p/ + Open=%cd %d/%p#ucpio/ View=%view{ascii} gzip -dc '%f' | cpio -ictv Extract=gzip -dc '%f' | cpio -ic Icon=compressed.xpm shell/.cpio - Open=%cd cpio:%d/%p/ + Open=%cd %d/%p#ucpio/ View=%view{ascii} cat '%f' | cpio -ictv Extract=cat '%f' | cpio -ic Icon=cpio.xpm diff --git a/src/ChangeLog b/src/ChangeLog index 4aff42ad4..c6a682ddc 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,7 @@ +Sun Nov 15 00:37:58 1998 Pavel Machek + + * mad.c (mad_tempnam): needed wrapper for tempnam added + 1998-11-17 Miguel de Icaza * dlg.c (destroy_dlg): Do not refresh. diff --git a/src/mad.c b/src/mad.c index dccb9bfba..54c877f0d 100644 --- a/src/mad.c +++ b/src/mad.c @@ -225,6 +225,15 @@ void mad_free (void *ptr, char *file, int line) mem_areas [i].in_use = 0; } +char *mad_tempnam (char *a, char *b) +{ + char *t, *u; + t = tempnam(a,b); + u = mad_strdup(t, "(mad_tempnam)", 0); + free(t); + return u; +} + /* Outputs a list of unfreed memory areas, to be called as a last thing before exiting */ void mad_finalize (char *file, int line) diff --git a/src/mad.h b/src/mad.h index 5f024c784..e22e148b1 100644 --- a/src/mad.h +++ b/src/mad.h @@ -26,6 +26,9 @@ # undef calloc #endif +#define tempnam(x,y) mad_tempnam (x, y) + + #define malloc(x) mad_alloc (x, __FILE__, __LINE__) #define calloc(x, y) mad_alloc ((x) * (y), __FILE__, __LINE__) #define realloc(x, y) mad_realloc (x, y, __FILE__, __LINE__) @@ -39,6 +42,8 @@ void *mad_realloc (void *ptr, int newsize, char *file, int line); char *mad_strdup (const char *s, char *file, int line); void mad_free (void *ptr, char *file, int line); void mad_finalize (char *file, int line); +char *mad_tempnam (char *s1, char *s2); + #else #define mad_finalize(x, y) diff --git a/vfs/ChangeLog b/vfs/ChangeLog index 357e84cce..f57ed173c 100644 --- a/vfs/ChangeLog +++ b/vfs/ChangeLog @@ -1,3 +1,15 @@ +Sat Nov 7 20:19:14 1998 Pavel Machek + + * vfs.c: Converted g_return_if_fail into vfs_dies(), removed them + where unneccessary. I do not see why passing NULL to vfs_add_stamp + should be invalid -> removed check. + +Mon Nov 2 23:09:55 1998 Pavel Machek + + * direntry.c, xdirentry.h: should be now prepared to handle + ftp-like filesystems. This is rather BIG change, altrough it does + not look so. I hope I do not break too many things. + Mon Nov 16 16:39:39 1998 Pavel Machek * vfs.c (vfs_parse_ls_lga): Report unrecognized lines. diff --git a/vfs/Make-mc.in b/vfs/Make-mc.in index 6ada962a2..2c646530a 100644 --- a/vfs/Make-mc.in +++ b/vfs/Make-mc.in @@ -99,13 +99,13 @@ EXTFSSTUFF = $(EXTFS_MISC) $(EXTFS_CONST) $(EXTFS_IN) VFSSOOBJS = tcputil.so fish.so ftpfs.so mcfs.so utilvfs.so local.so vfs.so \ tar.so sfs.so names.so container.so extfs.so util-alone.so \ - util.sor utilunix.sor + util.sor utilunix.sor direntry.so %.sor: ../src/%.c - $(CC) -c $(CPPFLAGS) $(DEFS) $(CFLAGS) -DVFS_STANDALONE $< -o $@ + $(CC) -fpic -c $(CPPFLAGS) $(DEFS) $(CFLAGS) -DVFS_STANDALONE $< -o $@ %.so: %.c - $(CC) -c $(CPPFLAGS) $(DEFS) $(CFLAGS) -DVFS_STANDALONE $< -o $@ + $(CC) -fpic -c $(CPPFLAGS) $(DEFS) $(CFLAGS) -DVFS_STANDALONE $< -o $@ libvfs.so: $(VFSSOOBJS) gcc $(VFSSOOBJS) -shared -o libvfs.so diff --git a/vfs/Makefile.in b/vfs/Makefile.in index 679e4e46e..b79613673 100644 --- a/vfs/Makefile.in +++ b/vfs/Makefile.in @@ -40,13 +40,13 @@ EXTFSSTUFF = $(EXTFS_MISC) $(EXTFS_CONST) $(EXTFS_IN) # Commands to build standalone version (.so) # -VFSSOOBJS = tcputil.so ftpfs.so mcfs.so utilvfs.so local.so vfs.so tar.so names.so container.so extfs.so util-alone.so util.sor utilunix.sor sfs.so fish.so +VFSSOOBJS = tcputil.so ftpfs.so mcfs.so utilvfs.so local.so vfs.so tar.so names.so container.so extfs.so util-alone.so util.sor utilunix.sor sfs.so fish.so direntry.so %.sor: ../src/%.c - $(CC) -c $(CPPFLAGS) $(DEFS) $(CFLAGS) -DVFS_STANDALONE $< -o $@ + $(CC) -c -fpic $(CPPFLAGS) $(DEFS) $(CFLAGS) -DVFS_STANDALONE $< -o $@ %.so: %.c - $(CC) -c $(CPPFLAGS) $(DEFS) $(CFLAGS) -DVFS_STANDALONE $< -o $@ + $(CC) -c -fpic $(CPPFLAGS) $(DEFS) $(CFLAGS) -DVFS_STANDALONE $< -o $@ libvfs.so: $(VFSSOOBJS) gcc $(VFSSOOBJS) -shared -o libvfs.so diff --git a/vfs/direntry.c b/vfs/direntry.c index d3eee20af..72606e3ea 100644 --- a/vfs/direntry.c +++ b/vfs/direntry.c @@ -4,25 +4,52 @@ * Written at 1998 by Pavel Machek , distribute under LGPL. * * Based on tar.c from midnight and archives.[ch] from avfs by Miklos - * Szeredi (mszeredi@inf.bme.hu) */ + * Szeredi (mszeredi@inf.bme.hu) + * + * Unfortunately, I was unable to keep all filesystems + * uniform. tar-like filesystems use tree structure where each + * directory has pointers to its subdirectories. We can do this + * because we have full information about our archive. + * + * At ftp-like filesystems, situation is a little bit different. When + * you cd /usr/src/linux/drivers/char, you do _not_ want /usr, + * /usr/src, /usr/src/linux and /usr/src/linux/drivers to be + * listed. That means that we do not have complete information, and if + * /usr is symlink to /4, we will not know. Also we have to time out + * entries and things would get messy with tree-like approach. So we + * do different trick: root directory is completely special and + * completely fake, it contains entries such as 'usr', 'usr/src', ..., + * and we'll try to use custom find_entry function. + * + * Paths here do _not_ begin with '/', so root directory of + * archive/site is simply "". Beware. */ + +static volatile int total_inodes = 0, total_entries = 0; #include "xdirentry.h" #define CALL(x) if (MEDATA->x) MEDATA->x -vfs_s_inode *vfs_s_new_inode (vfs *me, struct stat *initstat) +vfs_s_inode *vfs_s_new_inode (vfs *me, vfs_s_super *super, struct stat *initstat) { vfs_s_inode *ino; ino = xmalloc(sizeof (vfs_s_inode), "Dcache inode"); if (!ino) return NULL; + ino->linkname = ino->localname = NULL; ino->subdir = NULL; if (initstat) ino->st = *initstat; + ino->super = super; + ino->ent = NULL; + ino->flags = 0; ino->st.st_nlink = 0; + super->fd_usage++; + total_inodes++; + CALL(init_inode) (me, ino); return ino; @@ -33,44 +60,67 @@ vfs_s_entry *vfs_s_new_entry (vfs *me, char *name, vfs_s_inode *inode) vfs_s_entry *entry; entry = (struct vfs_s_entry *) xmalloc (sizeof (struct vfs_s_entry), "Dcache entry"); + total_entries++; - entry->name = strdup (name); + if (name) + entry->name = strdup (name); + else entry->name = NULL; entry->dir = NULL; entry->next = NULL; entry->prevp = NULL; entry->ino = inode; + entry->ino->ent = entry; CALL(init_entry) (me, entry); return entry; } -void vfs_s_free_entry (vfs *me, vfs_s_entry *ent) +void vfs_s_free_inode (vfs *me, vfs_s_inode *ino) { - *ent->prevp = ent->next; - if (ent->next) ent->next->prevp = ent->prevp; + if (!ino) vfs_die("Don't pass NULL to me"); - free(ent->name); - ent->name = NULL; - if(ent->ino->st.st_nlink == 1) { - CALL(free_inode) (me, ent->ino); - ifree(ent->ino->linkname); - ifree(ent->ino->localname); - ifree(ent->ino); - ent->ino = NULL; - } else ent->ino->st.st_nlink--; + /* ==0 can happen if freshly created entry is deleted */ + if(ino->st.st_nlink <= 1) { + while(ino->subdir) { + vfs_s_entry *ent; + ent = ino->subdir; + vfs_s_free_entry(me, ent); + } - free(ent); + CALL(free_inode) (me, ino); + ifree(ino->linkname); + if (ino->localname) { + unlink(ino->localname); + free(ino->localname); + } + total_inodes--; + ino->super->fd_usage--; + free(ino); + } else ino->st.st_nlink--; } -void vfs_s_free_tree (vfs *me, vfs_s_inode *dir, vfs_s_inode *parentdir) +void vfs_s_free_entry (vfs *me, vfs_s_entry *ent) { - vfs_s_entry *ent; - - while(dir->subdir) { - ent = dir->subdir; - if(ent->ino != dir && ent->ino != parentdir) vfs_s_free_tree(me, ent->ino, dir); - vfs_s_free_entry(me, ent); + int is_dot = 0; + if (ent->prevp) { /* It is possible that we are deleting freshly created entry */ + *ent->prevp = ent->next; + if (ent->next) ent->next->prevp = ent->prevp; } + + if (ent->name) { + is_dot = (!strcmp(ent->name, ".")) || (!strcmp(ent->name, "..")); + free (ent->name); + ent->name = NULL; + } + + if (!is_dot && ent->ino) { + ent->ino->ent = NULL; + vfs_s_free_inode (me, ent->ino); + ent->ino = NULL; + } + + total_entries--; + free(ent); } void vfs_s_insert_entry (vfs *me, vfs_s_inode *dir, vfs_s_entry *ent) @@ -103,7 +153,6 @@ struct stat *vfs_s_default_stat (vfs *me, mode_t mode) st.st_gid = getgid (); st.st_size = 0; st.st_mtime = st.st_atime = st.st_ctime = time (NULL); - st.st_nlink = 1; return &st; } @@ -115,12 +164,10 @@ void vfs_s_add_dots (vfs *me, vfs_s_inode *dir, vfs_s_inode *parent) if (!parent) parent = dir; dot = vfs_s_new_entry (me, ".", dir); - dot->ino = dir; dotdot = vfs_s_new_entry (me, "..", parent); - dot->ino = parent; vfs_s_insert_entry(me, dir, dot); vfs_s_insert_entry(me, dir, dotdot); - dir->st.st_nlink += 2; + dir->st.st_nlink--; parent->st.st_nlink--; /* We do not count "." and ".." into nlinks */ } struct vfs_s_entry *vfs_s_generate_entry (vfs *me, char *name, struct vfs_s_inode *parent, mode_t mode) @@ -130,7 +177,7 @@ struct vfs_s_entry *vfs_s_generate_entry (vfs *me, char *name, struct vfs_s_inod struct stat *st; st = vfs_s_default_stat (me, mode); - inode = vfs_s_new_inode (me, st); + inode = vfs_s_new_inode (me, parent->super, st); if (S_ISDIR (mode)) vfs_s_add_dots (me, inode, parent); @@ -139,8 +186,7 @@ struct vfs_s_entry *vfs_s_generate_entry (vfs *me, char *name, struct vfs_s_inod return entry; } -/* We were asked to create entries automagically - */ +/* We were asked to create entries automagically */ vfs_s_entry *vfs_s_automake(vfs *me, vfs_s_inode *dir, char *path, int flags) { struct vfs_s_entry *res; @@ -153,9 +199,8 @@ vfs_s_entry *vfs_s_automake(vfs *me, vfs_s_inode *dir, char *path, int flags) } /* Follow > 0: follow links, serves as loop protect, - * == -1: do not follow links - */ -vfs_s_entry *vfs_s_find_entry(vfs *me, vfs_s_inode *root, char *path, int follow, int flags) + * == -1: do not follow links */ +vfs_s_entry *vfs_s_find_entry_tree(vfs *me, vfs_s_inode *root, char *path, int follow, int flags) { unsigned int pseg; vfs_s_entry* ent = NULL; @@ -170,12 +215,12 @@ vfs_s_entry *vfs_s_find_entry(vfs *me, vfs_s_inode *root, char *path, int follow found = 0; for(ent = root->subdir; ent != NULL; ent = ent->next) - if(strlen(ent->name) == pseg && strncmp(ent->name, path, pseg) == 0) + if(strlen(ent->name) == pseg && (!strncmp(ent->name, path, pseg))) /* FOUND! */ break; if (!ent && (flags & (FL_MKFILE | FL_MKDIR))) - ent = vfs_s_automake(me, root, path/*+pseg*/, flags); + ent = vfs_s_automake(me, root, path, flags); if (!ent) ERRNOR (ENOENT, NULL); path += pseg; if (!vfs_s_resolve_symlink(me, ent, follow)) return NULL; @@ -183,12 +228,83 @@ vfs_s_entry *vfs_s_find_entry(vfs *me, vfs_s_inode *root, char *path, int follow } } +static void split_dir_name(vfs *me, char *path, char **dir, char **name, char **save) +{ + char *s; + s = strrchr(path, DIR_SEP_CHAR); + if (!s) { + *save = NULL; + *name = path; + *dir = ""; + } else { + *save = s; + *dir = path; + *s++ = 0; + if (!*s) /* This can happen if someone does stat("/"); */ + *name = ""; + else + *name = s; + } +} + +vfs_s_entry *vfs_s_find_entry_linear(vfs *me, vfs_s_inode *root, char *path, int follow, int flags) +{ + char *s; + vfs_s_entry* ent = NULL; + + if (!(flags & FL_DIR)) { + char *dirname, *name, *save; + vfs_s_inode *ino; + vfs_s_entry *ent; + split_dir_name(me, path, &dirname, &name, &save); + ino = vfs_s_find_inode(me, root, dirname, follow, flags | FL_DIR); + if (save) + *save = DIR_SEP_CHAR; + ent = vfs_s_find_entry_tree(me, ino, name, follow, flags); + return ent; + } + + for(ent = root->subdir; ent != NULL; ent = ent->next) + if (!strcmp(ent->name, path)) + break; + + if (ent && (! (MEDATA->dir_uptodate) (me, ent->ino))) { +#if 0 + message_1s( 1, "Dir cache expired for", path); +#endif + vfs_s_free_entry (me, ent); + } + + if (!ent) { + vfs_s_inode *ino; + + ino = vfs_s_new_inode(me, root->super, vfs_s_default_stat (me, S_IFDIR | 0755)); + ent = vfs_s_new_entry(me, path, ino); + if ((MEDATA->dir_load) (me, ino, path) == -1) { + vfs_s_free_entry(me, ent); + return NULL; + } + vfs_s_insert_entry(me, root, ent); + + for(ent = root->subdir; ent != NULL; ent = ent->next) + if (!strcmp(ent->name, path)) + break; + } + if (!ent) + vfs_die("find_linear: success but directory is not there\n"); + +#if 0 + if (!vfs_s_resolve_symlink(me, ent, follow)) return NULL; +#endif + return ent; +} + vfs_s_inode *vfs_s_find_inode(vfs *me, vfs_s_inode *root, char *path, int follow, int flags) { vfs_s_entry *ent; - if (!path || !path[0]) + if ((MEDATA->find_entry == vfs_s_find_entry_tree) && (!*path)) return root; - ent = vfs_s_find_entry(me, root, path, follow, flags); + ent = (MEDATA->find_entry)(me, root, path, follow, flags); if (!ent) return NULL; return ent->ino; @@ -213,10 +329,11 @@ vfs_s_entry *vfs_s_resolve_symlink (vfs *me, vfs_s_entry *entry, int follow) return entry; /* We have to handle "/" by ourself; "." and ".." have - corresponding entries, so there's no problem with them. */ + corresponding entries, so there's no problem with them. + FIXME: no longer true */ if (*entry->ino->linkname == '/') dir = vfs_s_find_root(me, entry); else dir = entry->dir; - return vfs_s_find_entry (me, dir, entry->ino->linkname, follow-1, 0); + return (MEDATA->find_entry) (me, dir, entry->ino->linkname, follow-1, 0); } /* Ook, these were functions around direcory entries / inodes */ @@ -227,11 +344,15 @@ vfs_s_super *vfs_s_new_super (vfs *me) vfs_s_super *super; super = xmalloc( sizeof( struct vfs_s_super ), "Direntry: superblock" ); + bzero(super, sizeof(struct vfs_s_super)); super->root = NULL; super->name = NULL; super->fd_usage = 0; super->me = me; +} +void vfs_s_insert_super (vfs *me, vfs_s_super *super) +{ super->next = MEDATA->supers; super->prevp = &MEDATA->supers; @@ -242,17 +363,29 @@ vfs_s_super *vfs_s_new_super (vfs *me) void vfs_s_free_super (vfs *me, vfs_s_super *super) { - if (super->root) - vfs_s_free_tree (me, super->root, NULL); + if (super->root) { + vfs_s_free_inode (me, super->root); + super->root = NULL; + } - *super->prevp = super->next; - if (super->next) super->next->prevp = super->prevp; +#if 0 + /* We currently leak small ammount of memory, sometimes. Fix it if you can. */ + if (super->fd_usage) + message_1s1d (1, " Direntry warning ", "Super fd_usage is %d, memory leak", super->fd_usage); +#endif + + if (super->prevp) { + *super->prevp = super->next; + if (super->next) super->next->prevp = super->prevp; + } CALL(free_archive) (me, super); + ifree(super->name); + super->name = NULL; free(super); } -/* ========================================================================= */ +/* ------------------------------------------------------------------------= */ static void vfs_s_stamp_me (vfs *me, struct vfs_s_super *psup, char *fs_name) { @@ -276,7 +409,7 @@ char *vfs_s_get_path_mangle (vfs *me, char *inname, struct vfs_s_super **archive { char *local, *op, *archive_name; int result = -1; - struct vfs_s_super *psup; + struct vfs_s_super *super; void *cookie; archive_name = inname; @@ -284,12 +417,13 @@ char *vfs_s_get_path_mangle (vfs *me, char *inname, struct vfs_s_super **archive if (!local) local = ""; - if (! (cookie = MEDATA->archive_check (me, archive_name, op))) - return NULL; + if (MEDATA->archive_check) + if (! (cookie = MEDATA->archive_check (me, archive_name, op))) + return NULL; - for (psup = MEDATA->supers; psup != NULL; psup = psup->next) { + for (super = MEDATA->supers; super != NULL; super = super->next) { int i; /* 0 == other, 1 == same, return it, 2 == other but stop scanning */ - if ((i = MEDATA->archive_same (me, psup, archive_name, cookie))) { + if ((i = MEDATA->archive_same (me, super, archive_name, op, cookie))) { if (i==1) goto return_success; else break; } @@ -297,17 +431,22 @@ char *vfs_s_get_path_mangle (vfs *me, char *inname, struct vfs_s_super **archive if (flags & FL_NO_OPEN) ERRNOR (EIO, NULL); - psup = vfs_s_new_super (me); - result = MEDATA->open_archive (me, psup, archive_name, op); + super = vfs_s_new_super (me); + result = MEDATA->open_archive (me, super, archive_name, op); if (result == -1) { - vfs_s_free_super (me, psup); + vfs_s_free_super (me, super); ERRNOR (EIO, NULL); } + if (!super->name) + vfs_die( "You have to fill name\n" ); + if (!super->root) + vfs_die( "You have to fill root inode\n" ); - vfs_s_stamp_me (me, psup, archive_name); + vfs_s_insert_super (me, super); + vfs_s_stamp_me (me, super, archive_name); return_success: - *archive = psup; + *archive = super; return local; } @@ -322,41 +461,73 @@ char *vfs_s_get_path (vfs *me, char *inname, struct vfs_s_super **archive, int f return res2; } +void vfs_s_invalidate (vfs *me, vfs_s_super *super) +{ + if (!super->want_stale) { + vfs_s_free_inode(me, super->root); + super->root = vfs_s_new_inode (me, super, vfs_s_default_stat(me, S_IFDIR | 0755)); + } +} + +char *vfs_s_fullpath (vfs *me, vfs_s_inode *ino) +{ /* For now, usable only on filesystems with _linear structure */ + if (MEDATA->find_entry != vfs_s_find_entry_linear) + vfs_die( "Implement me!" ); + if ((!ino->ent) || (!ino->ent->dir) || (!ino->ent->dir->ent)) + ERRNOR(EAGAIN, NULL); + return copy_strings( ino->ent->dir->ent->name, "/", ino->ent->name, NULL ); +} + /* Support of archives */ -/* ========================== readdir & friends ========================================= */ +/* ------------------------ readdir & friends ----------------------------- */ + +vfs_s_super *vfs_s_super_from_path (vfs *me, char *name) +{ + struct vfs_s_super *super; + + if (!vfs_s_get_path_mangle (me, name, &super, 0)) + return NULL; + return super; +} vfs_s_inode *vfs_s_inode_from_path (vfs *me, char *name, int flags) { struct vfs_s_super *super; - char *q; struct vfs_s_inode *ino; + char *q; if (!(q = vfs_s_get_path_mangle (me, name, &super, 0))) return NULL; - ino = vfs_s_find_inode (me, super->root, q, flags & FL_FOLLOW ? FOLLOW : NO_FOLLOW, FL_NONE); + ino = vfs_s_find_inode (me, super->root, q, flags & FL_FOLLOW ? FOLLOW : NO_FOLLOW, flags & ~FL_FOLLOW); if (ino) return ino; - if ((flags & FL_DIR) && MEDATA->load_dir) { - MEDATA->load_dir (me, super, q); - ino = vfs_s_find_inode (me, super->root, q, flags & FL_FOLLOW ? FOLLOW : NO_FOLLOW, FL_NONE); - } - return ino; + } +struct dirhandle { + vfs_s_entry *cur; + vfs_s_inode *dir; +}; + void * vfs_s_opendir (vfs *me, char *dirname) { - struct vfs_s_inode *ino; - struct vfs_s_entry **info; + struct vfs_s_inode *dir; + struct dirhandle *info; - ino = vfs_s_inode_from_path (me, dirname, FL_DIR | FL_FOLLOW); - if (!ino) return NULL; - if (!S_ISDIR (ino->st.st_mode)) ERRNOR (ENOTDIR, NULL); + dir = vfs_s_inode_from_path (me, dirname, FL_DIR | FL_FOLLOW); + if (!dir) return NULL; + if (!S_ISDIR (dir->st.st_mode)) ERRNOR (ENOTDIR, NULL); - info = (struct vfs_s_entry **) xmalloc (2*sizeof (struct vfs_s_entry *), "Dentry opendir"); - info[0] = ino->subdir; - info[1] = ino->subdir; + dir->st.st_nlink++; +#if 0 + if (!dir->subdir) /* This can actually happen if we allow empty directories */ + ERRNOR(EAGAIN, NULL); +#endif + info = (struct dirhandle *) xmalloc (sizeof (struct dirhandle), "Shared opendir"); + info->cur = dir->subdir; + info->dir = dir; return info; } @@ -370,30 +541,33 @@ void * vfs_s_readdir (void *data) #endif } dir; - struct vfs_s_entry **info = (struct vfs_s_entry **) data; + struct dirhandle *info = (struct dirhandle *) data; - if (!*info) + if (!(info->cur)) return NULL; - strcpy (&(dir.dir.d_name [0]), (*info)->name); + if (info->cur->name) + strcpy (&(dir.dir.d_name [0]), info->cur->name); + else + vfs_die( "Null in structure-can not happen"); #ifndef DIRENT_LENGTH_COMPUTED dir.d_namlen = strlen (dir.dir.d_name); #endif - *info = (*info)->next; + info->cur = info->cur->next; return (void *)&dir; } int vfs_s_telldir (void *data) { - struct vfs_s_entry **info = (struct vfs_s_entry **) data; + struct dirhandle *info = (struct dirhandle *) data; struct vfs_s_entry *cur; int num = 0; - cur = info[1]; + cur = info->dir->subdir; while (cur!=NULL) { - if (cur == info[0]) return num; + if (cur == info->cur) return num; num++; cur = cur->next; } @@ -402,15 +576,19 @@ int vfs_s_telldir (void *data) void vfs_s_seekdir (void *data, int offset) { - struct vfs_s_entry **info = (struct vfs_s_entry **) data; + struct dirhandle *info = (struct dirhandle *) data; int i; - info[0] = info[1]; + info->cur = info->dir->subdir; for (i=0; idir; + + vfs_s_free_inode(dir->super->me, dir); free (data); return 0; } @@ -424,7 +602,7 @@ int vfs_s_chdir (vfs *me, char *path) return 0; } -/* =========================== stat and friends ============================== */ +/* --------------------------- stat and friends ---------------------------- */ static int vfs_s_internal_stat (vfs *me, char *path, struct stat *buf, int flag) { @@ -466,6 +644,7 @@ int vfs_s_readlink (vfs *me, char *path, char *buf, int size) void *vfs_s_open (vfs *me, char *file, int flags, int mode) { + int was_changed = 0; struct vfs_s_fh *fh; vfs_s_super *super; char *q; @@ -473,31 +652,111 @@ void *vfs_s_open (vfs *me, char *file, int flags, int mode) if ((q = vfs_s_get_path_mangle (me, file, &super, 0)) == NULL) return NULL; - if (!(ino = vfs_s_find_inode (me, super->root, q, FOLLOW, FL_NONE))) - return NULL; - if (S_ISDIR (ino->st.st_mode)) ERRNOR (EISDIR, NULL); - if ((flags & O_ACCMODE) != O_RDONLY) ERRNOR (EROFS, NULL); + ino = vfs_s_find_inode (me, super->root, q, FOLLOW, FL_NONE); + if (ino && ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))) + ERRNOR (EEXIST, NULL); + if (!ino) { + char *dirname, *name, *save; + vfs_s_entry *ent; + vfs_s_inode *dir; + if (!(flags & O_CREAT)) + return NULL; + + split_dir_name(me, q, &dirname, &name, &save); + dir = vfs_s_find_inode(me, super->root, dirname, FOLLOW, FL_DIR); + if (save) + *save = DIR_SEP_CHAR; + ent = vfs_s_generate_entry (me, name, dir, 0755); + ino = ent->ino; + vfs_s_insert_entry(me, dir, ent); + ino->localname = tempnam (NULL, me->name); + was_changed = 1; + } + + if (ino && S_ISDIR (ino->st.st_mode)) ERRNOR (EISDIR, NULL); fh = (struct vfs_s_fh *) xmalloc (sizeof (struct vfs_s_fh), "Direntry: filehandle"); - fh->super = super; fh->pos = 0; fh->ino = ino; fh->handle = -1; + fh->changed = was_changed; + fh->linear = 0; if (MEDATA->fh_open) - if (MEDATA->fh_open (me, fh)) { + if (MEDATA->fh_open (me, fh, flags, mode)) { free(fh); return NULL; } + if (fh->ino->localname) { + fh->handle = open(fh->ino->localname, flags, mode); + if (fh->handle == -1) { + free(fh); + ERRNOR(errno, NULL); + } + } + /* i.e. we had no open files and now we have one */ vfs_rmstamp (&vfs_tarfs_ops, (vfsid) super, 1); super->fd_usage++; + fh->ino->st.st_nlink++; return fh; } +int vfs_s_read (void *fh, char *buffer, int count) +{ + int n; + vfs *me = FH_SUPER->me; + + if (FH->linear == LS_LINEAR_CLOSED) { + print_vfs_message ("Starting linear transfer..."); + if (!MEDATA->linear_start (me, FH, 0)) + return -1; + } + + if (FH->linear == LS_LINEAR_CLOSED) + vfs_die ("linear_start() did not set linear_state!"); + + if (FH->linear == LS_LINEAR_OPEN) + return MEDATA->linear_read (me, FH, buffer, count); + + if (FH->handle) { + n = read (FH->handle, buffer, count); + if (n < 0) + me->verrno = errno; + return n; + } + vfs_die( "vfs_s_read: This should not happen\n" ); +} + +int vfs_s_write (void *fh, char *buffer, int count) +{ + int n; + vfs *me = FH_SUPER->me; + + if (FH->linear) + vfs_die ("no writing to linear files, please" ); + + FH->changed = 1; + if (FH->handle) { + n = write (FH->handle, buffer, count); + if (n < 0) + me->verrno = errno; + return n; + } + vfs_die( "vfs_s_write: This should not happen\n" ); +} + int vfs_s_lseek (void *fh, off_t offset, int whence) { off_t size = FH->ino->st.st_size; + + if (FH->handle) { /* If we have local file opened, we want to work with it */ + int retval = lseek(FH->handle, offset, whence); + if (retval == -1) + FH->ino->super->me->verrno = errno; + return retval; + } + switch (whence) { case SEEK_CUR: offset += FH->pos; break; @@ -515,31 +774,42 @@ int vfs_s_lseek (void *fh, off_t offset, int whence) int vfs_s_close (void *fh) { - vfs *me = FH->super->me; + int res = 0; + vfs *me = FH_SUPER->me; - FH->super->fd_usage--; - if (!FH->super->fd_usage) { + FH_SUPER->fd_usage--; + if (!FH_SUPER->fd_usage) { struct vfs_stamping *parent; vfs *v; - v = vfs_type (FH->super->name); + v = vfs_type (FH_SUPER->name); if (v == &vfs_local_ops) { parent = NULL; } else { parent = xmalloc (sizeof (struct vfs_stamping), "vfs stamping"); parent->v = v; parent->next = 0; - parent->id = (*v->getid) (v, FH->super->name, &(parent->parent)); + parent->id = (*v->getid) (v, FH_SUPER->name, &(parent->parent)); } - vfs_add_noncurrent_stamps (&vfs_tarfs_ops, (vfsid) (FH->super), parent); + vfs_add_noncurrent_stamps (&vfs_tarfs_ops, (vfsid) (FH_SUPER), parent); vfs_rm_parents (parent); } - CALL(fh_close) (me, fh); + if (FH->linear == LS_LINEAR_OPEN) + MEDATA->linear_close (me, fh); + if (MEDATA->fh_close) + res = MEDATA->fh_close (me, fh); + if (FH->changed && MEDATA->file_store) { + char *s = vfs_s_fullpath( me, FH->ino ); + if (!s) res = -1; + else res = MEDATA->file_store (me, FH_SUPER, s, FH->ino->localname); + vfs_s_invalidate(me, FH_SUPER); + } if (FH->handle) - mc_close(FH->handle); + close(FH->handle); + vfs_s_free_inode (me, FH->ino); free (fh); - return 0; + return res; } /* ------------------------------- mc support ---------------------------- */ @@ -599,7 +869,34 @@ char *vfs_s_getlocalcopy (vfs *me, char *path) return ino->localname; } -/* ----------------------------- Stamping support ----------------------------- */ +int +vfs_s_setctl (vfs *me, char *path, int ctlop, char *arg) +{ + vfs_s_inode *ino = vfs_s_inode_from_path(me, path, 0); + if (!ino) + return 0; + switch (ctlop) { + case MCCTL_WANT_STALE_DATA: +#warning Should se this to 1 + ino->super->want_stale = 0; + return 1; + case MCCTL_NO_STALE_DATA: + ino->super->want_stale = 0; + vfs_s_invalidate(me, ino->super); + return 1; +#if 0 /* FIXME: We should implement these */ + case MCCTL_REMOVELOCALCOPY: + return remove_temp_file (path); + case MCCTL_FORGET_ABOUT: + my_forget(path); + return 0; +#endif + } + return 0; +} + + +/* ----------------------------- Stamping support -------------------------- */ vfsid vfs_s_getid (vfs *me, char *path, struct vfs_stamping **parent) { @@ -637,3 +934,86 @@ void vfs_s_free (vfsid id) { vfs_s_free_super (((vfs_s_super *)id)->me, (vfs_s_super *)id); } + +/* ----------- Utility functions for networked filesystems -------------- */ + +int +vfs_s_select_on_two (int fd1, int fd2) +{ + fd_set set; + struct timeval timeout; + int v; + int maxfd = (fd1 > fd2 ? fd1 : fd2) + 1; + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + FD_ZERO(&set); + FD_SET(fd1, &set); + FD_SET(fd2, &set); + v = select (maxfd, &set, 0, 0, &timeout); + if (v <= 0) + return v; + if (FD_ISSET (fd1, &set)) + return 1; + if (FD_ISSET (fd2, &set)) + return 2; + return -1; +} + +int +vfs_s_get_line (vfs *me, int sock, char *buf, int buf_len, char term) +{ + FILE *logfile = MEDATA->logfile; + int i, status; + char c; + + for (i = 0; i < buf_len; i++, buf++) { + if (read(sock, buf, sizeof(char)) <= 0) + return 0; + if (logfile){ + fwrite (buf, 1, 1, logfile); + fflush (logfile); + } + if (*buf == term) { + *buf = 0; + return 1; + } + } + *buf = 0; + while ((status = read(sock, &c, sizeof(c))) > 0){ + if (logfile){ + fwrite (&c, 1, 1, logfile); + fflush (logfile); + } + if (c == '\n') + return 1; + } + return 0; +} + +int +vfs_s_get_line_interruptible (vfs *me, char *buffer, int size, int fd) +{ + int n; + int i = 0; + + enable_interrupt_key(); + for (i = 0; i < size-1; i++) { + n = read (fd, buffer+i, 1); + disable_interrupt_key(); + if (n == -1 && errno == EINTR){ + buffer [i] = 0; + return EINTR; + } + if (n == 0){ + buffer [i] = 0; + return 0; + } + if (buffer [i] == '\n'){ + buffer [i] = 0; + return 1; + } + } + buffer [size-1] = 0; + return 0; +} diff --git a/vfs/fish.c b/vfs/fish.c index dd8b9d2f8..c7aa53c06 100644 --- a/vfs/fish.c +++ b/vfs/fish.c @@ -30,53 +30,20 @@ * * Namespace: fish_vfs_ops exported. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For isdigit */ -#include -#ifdef SCO_FLAVOR -# include /* alex: for struct timeb definition */ -#endif /* SCO_FLAVOR */ -#include -#include -#if defined(HAVE_UNISTD_H) -#include -#endif -#if HAVE_SYS_SELECT_H -# include -#endif -#include "../src/fs.h" -#include "../src/mad.h" -#include "../src/setup.h" + +/* Define this if your ssh can take -I option */ + +#define HAVE_HACKED_SSH + +#include "xdirentry.h" #include "../src/tty.h" /* enable/disable interrupt key */ #include "../src/main.h" -#ifndef SCO_FLAVOR -# include /* alex: this redefines struct timeval */ -#endif /* SCO_FLAVOR */ -#include - #include "../src/mem.h" #include "vfs.h" #include "tcputil.h" -#include "../src/util.h" -#include "../src/dialog.h" #include "container.h" #include "fish.h" -#ifndef MAXHOSTNAMELEN -# define MAXHOSTNAMELEN 64 -#endif - -#define ERRNOR(x,y) do { my_errno = x; return y; } while(0) +#include /* * Reply codes. @@ -87,18 +54,10 @@ #define TRANSIENT 4 /* transient negative completion */ #define ERROR 5 /* permanent negative completion */ -#define UPLOAD_ZERO_LENGTH_FILE - -static int my_errno; -static int code; - -/* Where we store the transactions */ -static FILE *logfile = NULL; - /* If true, the directory cache is forced to reload */ static int force_expiration = 0; -static struct linklist *connections_list; +/* FIXME: prev two variables should be killed */ /* command wait_flag: */ #define NONE 0x00 @@ -106,20 +65,9 @@ static struct linklist *connections_list; #define WANT_STRING 0x02 static char reply_str [80]; -static char *fish_get_current_directory(struct connection *bucket); -static void free_bucket (void *data); -static void connection_destructor(void *data); -static void flush_all_directory(struct connection *bucket); -static int get_line (int sock, char *buf, int buf_len, char term); -static char *get_path (struct connection **bucket, char *path); - -static char *my_get_host_and_username (char *path, char **host, char **user, int *flags, char **pass) -{ - return vfs_get_host_and_username (path, host, user, flags, 0, 0, pass); -} - static int decode_reply (char *s, int was_garbage) { + int code; if (!sscanf(s, "%d", &code)) { code = 500; return 5; @@ -129,16 +77,15 @@ static int decode_reply (char *s, int was_garbage) } /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */ -static int get_reply (int sock, char *string_buf, int string_len) +static int get_reply (vfs *me, int sock, char *string_buf, int string_len) { char answer[1024]; int was_garbage = 0; for (;;) { - if (!get_line(sock, answer, sizeof(answer), '\n')) { + if (!vfs_s_get_line(me, sock, answer, sizeof(answer), '\n')) { if (string_buf) *string_buf = 0; - code = 421; return 4; } if (strncmp(answer, "### ", 4)) { @@ -151,74 +98,72 @@ static int get_reply (int sock, char *string_buf, int string_len) } } -int got_sigpipe = 0; +#define SUP super->u.fish -static int command (struct connection *bucket, int wait_reply, - char *fmt, ...) +static int command (vfs *me, vfs_s_super *super, int wait_reply, char *fmt, ...) { va_list ap; char *str; int n, status; + FILE *logfile = MEDATA->logfile; va_start (ap, fmt); + str = g_strdup_vprintf (fmt, ap); va_end (ap); - + if (logfile){ fwrite (str, strlen (str), 1, logfile); fflush (logfile); } enable_interrupt_key(); - status = write (qsockw (bucket), str, strlen (str)); + + status = write(SUP.sockw, str, strlen(str)); g_free (str); - - if (status < 0){ - code = 421; - if (errno == EPIPE){ - got_sigpipe = 1; - } - disable_interrupt_key(); - return TRANSIENT; - } + disable_interrupt_key(); + if (status < 0) + return TRANSIENT; if (wait_reply) - return get_reply (qsockr (bucket), (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str) - 1); + return get_reply (me, SUP.sockr, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1); return COMPLETE; } static void -connection_close (void *data) +free_archive (vfs *me, vfs_s_super *super) { - struct connection *bucket = data; - - if ((qsockw (bucket) != -1) || (qsockr (bucket) != -1)){ - print_vfs_message ("fish: Disconnecting from %s", qhost(bucket)); - command(bucket, NONE, "logout\n"); - close(qsockw(bucket)); - close(qsockr(bucket)); + if ((SUP.sockw != -1) || (SUP.sockr != -1)){ + print_vfs_message ("fish: Disconnecting from %s", super->name); + command(me, super, NONE, "#BYE\nlogout\n"); + close(SUP.sockw); + close(SUP.sockr); + SUP.sockw = SUP.sockr = -1; } + ifree (SUP.host); + ifree (SUP.home); + ifree (SUP.user); + ifree (SUP.cwdir); + ifree (SUP.password); } static void -pipeopen(struct connection *bucket, char *path, char *argv[]) +pipeopen(vfs_s_super *super, char *path, char *argv[]) { int fileset1[2], fileset2[2]; - FILE *retval; int res; - if (pipe(fileset1)<0) vfs_die("Could not pipe(): %m."); - if (pipe(fileset2)<0) vfs_die("Could not pipe(): %m."); + if ((pipe(fileset1)<0) || (pipe(fileset2)<0)) + vfs_die("Could not pipe(): %m."); if ((res = fork())) { if (res<0) vfs_die("Could not fork(): %m."); /* We are the parent */ close(fileset1[0]); - qsockw(bucket) = fileset1[1]; + SUP.sockw = fileset1[1]; close(fileset2[1]); - qsockr(bucket) = fileset2[0]; - if (!retval) vfs_die( "Could not fdopen(): %m." ); + SUP.sockr = fileset2[0]; } else { close(0); dup(fileset1[0]); @@ -232,291 +177,225 @@ pipeopen(struct connection *bucket, char *path, char *argv[]) } } - -static struct connection * -open_command_connection (char *host, char *user, int flags, char *netrcpass) +/* The returned directory should always contain a trailing slash */ +static char *fish_getcwd(vfs *me, vfs_s_super *super) +{ + if (command(me, super, WANT_STRING, "#PWD\npwd; echo '### 200'\n") == COMPLETE) + return copy_strings(reply_str, "/", NULL); + ERRNOR (EIO, NULL); +} +static int +open_archive_int (vfs *me, vfs_s_super *super) { - struct connection *bucket; char *argv[100]; + char *xsh = (SUP.flags == FISH_FLAG_RSH ? "rsh" : "ssh"); int i = 0; - bucket = xmalloc(sizeof(struct connection), - "struct connection"); - - if (bucket == NULL) ERRNOR (ENOMEM, NULL); - - qhost(bucket) = strdup (host); - quser(bucket) = strdup (user); - qcdir(bucket) = NULL; - qflags(bucket) = flags; - qlock(bucket) = 0; - qhome(bucket) = NULL; - qupdir(bucket)= 0; - qdcache(bucket)=0; - bucket->__inode_counter = 0; - bucket->lock = 0; - bucket->password = 0; - - my_errno = ENOMEM; - if ((qdcache(bucket) = linklist_init()) == NULL) - goto error; - -#define XSH (flags == FISH_FLAG_RSH ? "rsh" : "ssh") - argv[i++] = XSH; + argv[i++] = xsh; +#ifdef HAVE_HACKED_SSH + argv[i++] = "-I"; +#endif argv[i++] = "-l"; - argv[i++] = user; - argv[i++] = host; - if (flags == FISH_FLAG_COMPRESSED) + argv[i++] = SUP.user; + argv[i++] = SUP.host; + if (SUP.flags == FISH_FLAG_COMPRESSED) argv[i++] = "-C"; argv[i++] = "echo FISH:; /bin/sh"; argv[i++] = NULL; - pipeopen(bucket, XSH, argv ); + /* Debugging hack */ +#warning Debugging hack, delete me + if (!MEDATA->logfile) + MEDATA->logfile = fopen( "/home/pavel/talk.fish", "w+" ); /* FIXME */ + pipeopen(super, xsh, argv ); { char answer[2048]; - print_vfs_message( "FISH: Waiting for initial line..." ); - if (!get_line(qsockr(bucket), answer, sizeof(answer), ':')) - goto error_2; + print_vfs_message( "fish: Waiting for initial line..." ); + if (!vfs_s_get_line(me, SUP.sockr, answer, sizeof(answer), ':')) + ERRNOR (E_PROTO, -1); print_vfs_message( answer ); if (strstr(answer, "assword")) { /* Currently, this does not work. ssh reads passwords from /dev/tty, not from stdin :-(. */ - message_1s (1, MSG_ERROR, _("Sorry, we can not do password authenticated connections for now.")); - my_errno = EPERM; - goto error_2; - - if (!bucket->password){ - char *p, *op; - p = copy_strings (" FISH: Password required for ", quser(bucket), +#ifndef HAVE_HACKED_SSH + message_1s (1, _(" Error "), _("Sorry, we can not do password authenticated connections for now.")); + ERRNOR (EPERM, -1); +#endif + if (!SUP.password){ + char *p, *op; + p = copy_strings (" fish: Password required for ", SUP.user, " ", NULL); op = vfs_get_password (p); free (p); - my_errno = EPERM; if (op == NULL) - goto error_2; - bucket->password = strdup (op); + ERRNOR (EPERM, -1); + SUP.password = strdup (op); wipe_password(op); } - print_vfs_message( "FISH: Sending password..." ); - write(qsockw(bucket), bucket->password, strlen(bucket->password)); - write(qsockw(bucket), "\r\n", 2); + print_vfs_message( "fish: Sending password..." ); + write(SUP.sockw, SUP.password, strlen(SUP.password)); + write(SUP.sockw, "\n", 1); } } print_vfs_message( "FISH: Sending initial line..." ); - my_errno = ECONNREFUSED; - if (command (bucket, WAIT_REPLY, "#FISH\necho; start_fish_server; echo '### 200'\n") != COMPLETE) - goto error_2; + if (command (me, super, WAIT_REPLY, "#FISH\necho; start_fish_server; echo '### 200'\n") != COMPLETE) + ERRNOR (E_PROTO, -1); print_vfs_message( "FISH: Handshaking version..." ); - if (command (bucket, WAIT_REPLY, "#VER 0.0.0\necho '### 000'\n") != COMPLETE) - goto error_2; + if (command (me, super, WAIT_REPLY, "#VER 0.0.0\necho '### 000'\n") != COMPLETE) + ERRNOR (E_PROTO, -1); print_vfs_message( "FISH: Setting up current directory..." ); - qhome(bucket) = fish_get_current_directory (bucket); - if (!qhome(bucket)) - qhome(bucket) = strdup ("/"); - print_vfs_message( "FISH: Connected." ); - return bucket; + SUP.home = fish_getcwd (me, super); + print_vfs_message( "FISH: Connected, home %s.", SUP.home ); +#if 0 + super->name = copy_strings( "/#sh:", SUP.user, "@", SUP.host, "/", NULL ); +#endif + super->name = strdup( "/" ); -error_2: - close(qsockr(bucket)); - close(qsockw(bucket)); -error: - free (qhost(bucket)); - free (quser(bucket)); - free (bucket); - return NULL; + super->root = vfs_s_new_inode (me, super, vfs_s_default_stat(me, S_IFDIR | 0755)); + return 0; } -/* This routine keeps track of open connections */ -/* Returns a connected socket to host */ -static struct connection * -open_link (char *host, char *user, int flags, char *netrcpass) +int +open_archive (vfs *me, vfs_s_super *super, char *archive_name, char *op) { - struct connection *bucket; - struct linklist *lptr; - - for (lptr = connections_list->next; - lptr != connections_list; lptr = lptr->next) { - bucket = lptr->data; - if ((strcmp (host, qhost(bucket)) == 0) && - (strcmp (user, quser(bucket)) == 0) && - (flags == qflags(bucket))) - return bucket; + char *host, *user, *password; + int flags; + + vfs_split_url (strchr(op, ':')+1, &host, &user, &flags, &password, 0, URL_NOSLASH); + SUP.host = strdup (host); + SUP.user = strdup (user); + SUP.flags = flags; + if (!strncmp( op, "rsh:", 4 )) + SUP.flags |= FISH_FLAG_RSH; + SUP.home = NULL; + if (password) + SUP.password = strdup (password); + return open_archive_int (me, super); +} + +static int +archive_same(vfs *me, vfs_s_super *super, char *archive_name, char *op, void *cookie) +{ + char *host, *user, *dummy2; + int flags; + vfs_split_url (strchr(op, ':')+1, &host, &user, &flags, &dummy2, 0, URL_NOSLASH); + return ((strcmp (host, SUP.host) == 0) && + (strcmp (user, SUP.user) == 0) && + (flags == SUP.flags)); +} + +int +fish_which (vfs *me, char *path) +{ + if (!strncmp (path, "/#sh:", 5)) + return 1; + if (!strncmp (path, "/#ssh:", 6)) + return 1; + if (!strncmp (path, "/#rsh:", 6)) + return 1; + return 0; +} + +int +dir_uptodate(vfs *me, vfs_s_inode *ino) +{ + struct timeval tim; + + return 1; /* Timeouting of directories does not work too well :-(. */ + gettimeofday(&tim, NULL); + if (force_expiration) { + force_expiration = 0; + return 0; } - bucket = open_command_connection(host, user, flags, netrcpass); - if (bucket == NULL) - return NULL; - if (!linklist_insert(connections_list, bucket)) { - connection_destructor(bucket); - ERRNOR (ENOMEM, NULL); - } - return bucket; + if (tim.tv_sec < ino->u.fish.timestamp.tv_sec) + return 1; + return 0; } -/* The returned directory should always contain a trailing slash */ -static char *fish_get_current_directory(struct connection *bucket) +static int +dir_load(vfs *me, vfs_s_inode *dir, char *remote_path) { - if (command(bucket, WANT_STRING, "#PWD\npwd; echo '### 200'\n") == COMPLETE) - return copy_strings(reply_str, "/", NULL); - ERRNOR (EIO, NULL); -} - -static void my_forget (char *path) -{ -} - -#define X "fish" -#define X_myname "/#sh:" -#define vfs_X_ops vfs_fish_ops -#define X_fill_names fish_fill_names -#define X_hint_reread fish_hint_reread -#define X_flushdir fish_flushdir -#define X_done fish_done -#include "shared_ftp_fish.c" - -static char* -get_path (struct connection **bucket, char *path) -{ - char *res; - if ((res = s_get_path (bucket, path, "/#sh:"))) - return res; - if ((res = s_get_path (bucket, path, "/#ssh:"))) - return res; - if ((res = s_get_path (bucket, path, "/#rsh:"))) { - qflags((*bucket)) |= FISH_FLAG_RSH; - return res; - } - return NULL; -} - -/* - * This is the 'new' code - */ -/* - * Last parameter (resolve_symlinks) is currently not used. Due to - * the code sharing (file shared_ftp_fish.c) the fish and ftp interface - * have to be the same (Norbert). - */ - -static struct dir * -retrieve_dir(struct connection *bucket, char *remote_path, int resolve_symlinks) -{ - int has_symlinks; - struct linklist *file_list, *p; - struct direntry *fe; + vfs_s_super *super = dir->super; char buffer[8192]; - struct dir *dcache; - int got_intr = 0; + vfs_s_entry *ent = NULL; + FILE *logfile; - for (p = qdcache(bucket)->next;p != qdcache(bucket); - p = p->next) { - dcache = p->data; - if (strcmp(dcache->remote_path, remote_path) == 0) { - struct timeval tim; + logfile = MEDATA->logfile; - gettimeofday(&tim, NULL); - if ((tim.tv_sec < dcache->timestamp.tv_sec) && !force_expiration) - return dcache; - else { - force_expiration = 0; - p->next->prev = p->prev; - p->prev->next = p->next; - dir_destructor(dcache); - free (p); - break; - } - } - } + print_vfs_message("fish: Reading directory %s...", remote_path); - has_symlinks = 0; - print_vfs_message("fish: Reading FTP directory..."); + gettimeofday(&dir->u.fish.timestamp, NULL); + dir->u.fish.timestamp.tv_sec += 10; /* was 360: 10 is good for + stressing direntry layer a bit */ - my_errno = ENOMEM; - if (!(file_list = linklist_init())) - return NULL; - if (!(dcache = xmalloc(sizeof(struct dir), "struct dir"))) { - linklist_destroy(file_list, NULL); - return NULL; - } - gettimeofday(&dcache->timestamp, NULL); - dcache->timestamp.tv_sec += 360; - dcache->file_list = file_list; - dcache->remote_path = strdup(remote_path); - dcache->count = 1; - - command(bucket, NONE, - "#LIST %s\nls -lLa %s | grep '^[^cbt]' | ( while read p x u g s m d y n; do echo \"P$p $u.$g\n" + command(me, super, NONE, + "#LIST /%s\nls -lLa /%s | grep '^[^cbt]' | ( while read p x u g s m d y n; do echo \"P$p $u.$g\n" "S$s\nd$m $d $y\n:$n\n\"; done )\n" - "ls -lLa %s | grep '^[cb]' | ( while read p x u g a i m d y n; do echo \"P$p $u.$g\n" + "ls -lLa /%s | grep '^[cb]' | ( while read p x u g a i m d y n; do echo \"P$p $u.$g\n" "E$a$i\nd$m $d $y\n:$n\n\"; done ); echo '### 200'\n", remote_path, remote_path, remote_path); - /* Clear the interrupt flag */ - enable_interrupt_key (); - - fe = NULL; - errno = 0; - my_errno = ENOMEM; - while ((got_intr = get_line_interruptible (buffer, sizeof (buffer), qsockr(bucket))) != EINTR){ - int eof = (got_intr == 0); - +#define SIMPLE_ENTRY vfs_s_generate_entry(me, NULL, dir, 0) + ent = SIMPLE_ENTRY; + while (1) { + int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr); + if ((!res) || (res == EINTR)) { + vfs_s_free_entry(me, ent); + me->verrno = ECONNRESET; + goto error; + } if (logfile){ fputs (buffer, logfile); fputs ("\n", logfile); fflush (logfile); } - if (eof) { - if (fe) - free(fe); - my_errno = ECONNRESET; - goto error_1; - } if (!strncmp(buffer, "### ", 4)) break; - if ((!buffer[0]) && fe) { - if (!linklist_insert(file_list, fe)) { - free(fe); - goto error_1; + if ((!buffer[0])) { + if (ent->name) { + vfs_s_insert_entry(me, dir, ent); + ent = SIMPLE_ENTRY; } - fe = NULL; continue; } - if (!fe) { - if (!(fe = xmalloc(sizeof(struct direntry), "struct direntry"))) - goto error_1; - bzero(fe, sizeof(struct direntry)); - fe->count = 1; - fe->bucket = bucket; - fe->s.st_ino = bucket->__inode_counter++; - fe->s.st_nlink = 1; - fe->local_filename = NULL; - } +#define ST ent->ino->st switch(buffer[0]) { - case ':': fe->name = strdup(buffer+1); break; - case 'S': fe->s.st_size = atoi(buffer+1); break; + case ':': { + char *c; + if (!strcmp(buffer+1, ".") || !strcmp(buffer+1, "..")) + break; /* We'll do . and .. ourself */ + ent->name = strdup(buffer+1); + if ((c=strchr(ent->name, ' '))) + *c = 0; /* this is ugly, but we can not handle " " in name */ + break; + } + case 'S': ST.st_size = atoi(buffer+1); break; case 'P': { int i; if ((i = vfs_parse_filetype(buffer[1])) ==-1) break; - fe->s.st_mode = i; + ST.st_mode = i; if ((i = vfs_parse_filemode(buffer+2)) ==-1) break; - fe->s.st_mode |= i; + ST.st_mode |= i; + if (S_ISLNK(ST.st_mode)) + ST.st_mode = 0; } break; case 'd': { vfs_split_text(buffer+1); - if (!vfs_parse_filedate(0, &fe->s.st_ctime)) + if (!vfs_parse_filedate(0, &ST.st_ctime)) break; - fe->s.st_atime = fe->s.st_mtime = fe->s.st_ctime; + ST.st_atime = ST.st_mtime = ST.st_ctime; } break; case 'D': { @@ -524,7 +403,7 @@ retrieve_dir(struct connection *bucket, char *remote_path, int resolve_symlinks) if (sscanf(buffer+1, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon, &tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6) break; - fe->s.st_atime = fe->s.st_mtime = fe->s.st_ctime = mktime(&tim); + ST.st_atime = ST.st_mtime = ST.st_ctime = mktime(&tim); } break; case 'E': { @@ -532,186 +411,166 @@ retrieve_dir(struct connection *bucket, char *remote_path, int resolve_symlinks) if (sscanf(buffer+1, "%d,%d", &maj, &min) != 2) break; #ifdef HAVE_ST_RDEV - fe->s.st_rdev = (maj << 8) | min; + ST.st_rdev = (maj << 8) | min; #endif } - case 'L': fe->linkname = strdup(buffer+1); + case 'L': ent->ino->linkname = strdup(buffer+1); break; } } - disable_interrupt_key(); -#if 0 - if (got_intr) - vfs_die("fish: reading FTP directory interrupted by user"); -#endif + + vfs_s_free_entry (me, ent); + me->verrno = E_REMOTE; + if (decode_reply(buffer+4, 0) != COMPLETE) + goto error; - if (decode_reply(buffer+4, 0) != COMPLETE) { - my_errno = EIO; - goto error_3; - } - if (file_list->next == file_list) { - my_errno = EACCES; - goto error_3; - } - if (!linklist_insert(qdcache(bucket), dcache)) { - my_errno = ENOMEM; - goto error_3; - } print_vfs_message("fish: got listing"); - return dcache; -error_1: - disable_interrupt_key(); -error_3: - free(dcache->remote_path); - free(dcache); - linklist_destroy(file_list, direntry_destructor); + return 0; + +error: print_vfs_message("fish: failed"); - return NULL; + return 1; } static int -store_file(struct direntry *fe) +file_store(vfs *me, vfs_s_super *super, char *name, char *localname) { - int local_handle, n, total; + int n, total; char buffer[8192]; struct stat s; int was_error = 0; + int h; - local_handle = open(fe->local_filename, O_RDONLY); - unlink (fe->local_filename); - my_errno = EIO; - if (local_handle == -1) - return 0; + h = open(localname, O_RDONLY); - fstat(local_handle, &s); + if (fstat(h, &s)<0) + ERRNOR (EIO, -1); /* Use this as stor: ( dd block ; dd smallblock ) | ( cat > file; cat > /dev/null ) */ - print_vfs_message("FISH: store: sending command..." ); - if (command (fe->bucket, WAIT_REPLY, - "#STOR %d %s\n> %s; echo '### 001'; ( dd bs=4096 count=%d; dd bs=%d count=1 ) 2>/dev/null | ( cat > %s; cat > /dev/null ); echo '### 200'\n", - s.st_size, fe->remote_filename, - fe->remote_filename, - s.st_size / 4096, s.st_size % 4096, fe->remote_filename) + print_vfs_message("FISH: store %s: sending command...", name ); + if (command (me, super, WAIT_REPLY, + "#STOR %d /%s\n> /%s; echo '### 001'; ( dd bs=4096 count=%d; dd bs=%d count=1 ) 2>/dev/null | ( cat > /%s; cat > /dev/null ); echo '### 200'\n", + s.st_size, name, name, + s.st_size / 4096, s.st_size % 4096, name) != PRELIM) - return 0; + ERRNOR(E_REMOTE, -1); total = 0; - enable_interrupt_key(); while (1) { - while ((n = read(local_handle, buffer, sizeof(buffer))) < 0) { + while ((n = read(h, buffer, sizeof(buffer))) < 0) { if ((errno == EINTR) && got_interrupt) continue; print_vfs_message("FISH: Local read failed, sending zeros" ); - close(local_handle); - local_handle = open( "/dev/zero", O_RDONLY ); + close(h); + h = open( "/dev/zero", O_RDONLY ); } if (n == 0) break; - while (write(qsockw(fe->bucket), buffer, n) < 0) { - if (errno == EINTR) { - if (got_interrupt()) { - my_errno = EINTR; - goto error_return; - } - else - continue; - } - my_errno = errno; + while (write(SUP.sockw, buffer, n) < 0) { + me->verrno = errno; goto error_return; } + disable_interrupt_key(); total += n; print_vfs_message("fish: storing %s %d (%d)", was_error ? "zeros" : "file", total, s.st_size); } - disable_interrupt_key(); - close(local_handle); - if (get_reply (qsockr(fe->bucket), NULL, 0) != COMPLETE) - ERRNOR (EIO, 0); - return (!was_error); -error_return: - disable_interrupt_key(); - close(local_handle); - get_reply(qsockr(fe->bucket), NULL, 0); + if ((get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error) + ERRNOR (E_REMOTE, 0); + close(h); return 0; +error_return: + close(h); + get_reply(me, SUP.sockr, NULL, 0); + return -1; } -static int linear_start(struct direntry *fe, int offset) +static int linear_start(vfs *me, vfs_s_fh *fh, int offset) { + char *name; if (offset) - ERRNOR (EOPNOTSUPP, 0); - fe->local_stat.st_mtime = 0; - if (command(fe->bucket, WANT_STRING, - "#RETR %s\nls -l %s | ( read var1 var2 var3 var4 var5 var6; echo $var5 ); echo '### 100'; cat %s; echo '### 200'\n", - fe->remote_filename, fe->remote_filename, fe->remote_filename ) - != PRELIM) ERRNOR (EACCES, 0); - fe->linear_state = LS_LINEAR_OPEN; - fe->got = 0; - fe->total = atoi(reply_str); + ERRNOR (E_NOTSUPP, 0); +/* fe->local_stat.st_mtime = 0; FIXME: what is this good for? */ + name = vfs_s_fullpath (me, fh->ino); + if (!name) + return 0; + if (command(me, FH_SUPER, WANT_STRING, + "#RETR /%s\nls -l /%s | ( read var1 var2 var3 var4 var5 var6; echo $var5 ); echo '### 100'; cat /%s; echo '### 200'\n", + name, name, name ) + != PRELIM) ERRNOR (E_REMOTE, 0); + fh->linear = LS_LINEAR_OPEN; + fh->u.fish.got = 0; + if (sscanf( reply_str, "%d", &fh->u.fish.total )!=1) + ERRNOR (E_REMOTE, 0); return 1; } static void -linear_abort (struct direntry *fe) +linear_abort (vfs *me, vfs_s_fh *fh) { + vfs_s_super *super = FH_SUPER; char buffer[8192]; int n; print_vfs_message( "Aborting transfer..." ); do { - n = VFS_MIN(8192, fe->total - fe->got); + n = VFS_MIN(8192, fh->u.fish.total - fh->u.fish.got); if (n) - if ((n = read(qsockr(fe->bucket), buffer, n)) < 0) + if ((n = read(SUP.sockr, buffer, n)) < 0) return; } while (n); - if (get_reply (qsockr(fe->bucket), NULL, 0) != COMPLETE) + if (get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) print_vfs_message( "Error reported after abort." ); else print_vfs_message( "Aborted transfer would be successfull." ); } static int -linear_read (struct direntry *fe, void *buf, int len) +linear_read (vfs *me, vfs_s_fh *fh, void *buf, int len) { + vfs_s_super *super = FH_SUPER; int n = 0; - len = VFS_MIN( fe->total - fe->got, len ); - while (len && ((n = read (qsockr(fe->bucket), buf, len))<0)) { + len = VFS_MIN( fh->u.fish.total - fh->u.fish.got, len ); + disable_interrupt_key(); + while (len && ((n = read (SUP.sockr, buf, len))<0)) { if ((errno == EINTR) && !got_interrupt()) continue; break; } + enable_interrupt_key(); - if (n>0) fe->got += n; - if (n<0) linear_abort(fe); - if ((!n) && ((get_reply (qsockr (fe->bucket), NULL, 0) != COMPLETE))) - ERRNOR (EIO, -1); + if (n>0) fh->u.fish.got += n; + if (n<0) linear_abort(me, fh); + if ((!n) && ((get_reply (me, SUP.sockr, NULL, 0) != COMPLETE))) + ERRNOR (E_REMOTE, -1); ERRNOR (errno, n); } static void -linear_close (struct direntry *fe) +linear_close (vfs *me, vfs_s_fh *fh) { - if (fe->total != fe->got) - linear_abort(fe); + if (fh->u.fish.total != fh->u.fish.got) + linear_abort(me, fh); } static int -fish_ctl (void *data, int ctlop, int arg) +fish_ctl (void *fh, int ctlop, int arg) { - struct filp *fp = data; + return 0; switch (ctlop) { case MCCTL_IS_NOTREADY: { int v; - if (!fp->fe->linear_state) + if (!FH->linear) vfs_die ("You may not do this"); - if (fp->fe->linear_state == LS_LINEAR_CLOSED) + if (FH->linear == LS_LINEAR_CLOSED) return 0; - v = select_on_two (qsockr(fp->fe->bucket), 0); + v = vfs_s_select_on_two (FH_SUPER->u.fish.sockr, 0); if (((v < 0) && (errno == EINTR)) || v == 0) return 1; return 0; @@ -722,72 +581,62 @@ fish_ctl (void *data, int ctlop, int arg) } static int -send_fish_command(struct connection *bucket, char *cmd, int flags) +send_fish_command(vfs *me, vfs_s_super *super, char *cmd, int flags) { int r; - int flush_directory_cache = (flags & OPT_FLUSH) && (normal_flush > 0); - r = command (bucket, WAIT_REPLY, cmd); - vfs_add_noncurrent_stamps (&vfs_fish_ops, (vfsid) bucket, NULL); - if (r != COMPLETE) ERRNOR (EPERM, -1); - if (flush_directory_cache) - flush_all_directory(bucket); + r = command (me, super, WAIT_REPLY, cmd); + vfs_add_noncurrent_stamps (&vfs_fish_ops, (vfsid) super, NULL); + if (r != COMPLETE) ERRNOR (E_REMOTE, -1); + if (flags & OPT_FLUSH) + vfs_s_invalidate(me, super); return 0; } -static int -fish_init (vfs *me) -{ - connections_list = linklist_init(); -#if 0 - logfile = fopen ("/tmp/talk.fish", "w+"); -#endif - return 1; -} - #define PREFIX \ char buf[999]; \ - char *remote_path; \ - struct connection *bucket; \ - if (!(remote_path = get_path(&bucket, path))) \ + char *rpath; \ + vfs_s_super *super; \ + if (!(rpath = vfs_s_get_path_mangle(me, path, &super, 0))) \ return -1; #define POSTFIX(flags) \ - free(remote_path); \ - return send_fish_command(bucket, buf, flags); + return send_fish_command(me, super, buf, flags); static int fish_chmod (vfs *me, char *path, int mode) { PREFIX - sprintf(buf, "#CHMOD %4.4o %s\nchmod %4.4o %s; echo '### 000'\n", - mode & 07777, remote_path, - mode & 07777, remote_path); + sprintf(buf, "#CHMOD %4.4o /%s\nchmod %4.4o /%s; echo '### 000'\n", + mode & 07777, rpath, + mode & 07777, rpath); POSTFIX(OPT_FLUSH); } #define FISH_OP(name, chk, string) \ static int fish_##name (vfs *me, char *path1, char *path2) \ { \ - char buf[120]; \ - char *remote_path1 = NULL, *remote_path2 = NULL; \ - struct connection *bucket1, *bucket2; \ - if (!(remote_path1 = get_path(&bucket1, path1))) \ + char buf[1024]; \ + char *rpath1 = NULL, *rpath2 = NULL; \ + vfs_s_super *super1, *super2; \ + if (!(rpath1 = vfs_s_get_path_mangle(me, path1, &super1, 0))) \ return -1; \ - if (!(remote_path2 = get_path(&bucket2, path2))) { \ - free(remote_path1); \ + if (!(rpath2 = vfs_s_get_path_mangle(me, path2, &super2, 0))) \ return -1; \ - } \ - sprintf(buf, string, path1, path2, path1, path2 ); \ - free(remote_path1); \ - free(remote_path2); \ - return send_fish_command(bucket2, buf, OPT_FLUSH); \ + snprintf(buf, 1023, string "\n", rpath1, rpath2, rpath1, rpath2 ); \ + return send_fish_command(me, super2, buf, OPT_FLUSH); \ } -#define XTEST if (bucket1 != bucket2) { free(remote_path1); free(remote_path2); ERRNOR (EXDEV, -1); } -FISH_OP(rename, XTEST, "#RENAME %s %s\nmv %s %s; echo '*** 000'" ); -FISH_OP(link, XTEST, "#LINK %s %s\nln %s %s; echo '*** 000'" ); -FISH_OP(symlink, , "#SYMLINK %s %s\nln -s %s %s; echo '*** 000'" ); +#define XTEST if (bucket1 != bucket2) { ERRNOR (EXDEV, -1); } +FISH_OP(rename, XTEST, "#RENAME /%s /%s\nmv /%s /%s; echo '### 000'" ); +FISH_OP(link, XTEST, "#LINK /%s /%s\nln /%s /%s; echo '### 000'" ); + +static int fish_symlink (vfs *me, char *setto, char *path) +{ + PREFIX + sprintf(buf, "#SYMLINK %s /%s\nln -s %s /%s; echo '### 000'\n", setto, rpath, setto, rpath); + POSTFIX(OPT_FLUSH); +} static int fish_chown (vfs *me, char *path, int owner, int group) @@ -796,92 +645,193 @@ fish_chown (vfs *me, char *path, int owner, int group) PREFIX sowner = getpwuid( owner )->pw_name; sgroup = getgrgid( group )->gr_name; - sprintf(buf, "#CHOWN %s %s\nchown %s %s; echo '### 000'\n", - sowner, remote_path, - sowner, remote_path); - send_fish_command(bucket, buf, OPT_FLUSH); + sprintf(buf, "#CHOWN /%s /%s\nchown /%s /%s; echo '### 000'\n", + sowner, rpath, + sowner, rpath); + send_fish_command(me, super, buf, OPT_FLUSH); /* FIXME: what should we report if chgrp succeeds but chown fails? */ - sprintf(buf, "#CHGRP %s %s\nchgrp %s %s; echo '### 000'\n", - sgroup, remote_path, - sgroup, remote_path); - free(remote_path); + sprintf(buf, "#CHGRP /%s /%s\nchgrp /%s /%s; echo '### 000'\n", + sgroup, rpath, + sgroup, rpath); POSTFIX(OPT_FLUSH) } static int fish_unlink (vfs *me, char *path) { PREFIX - sprintf(buf, "#DELE %s\nrm -f %s; echo '### 000'\n", remote_path, remote_path); + sprintf(buf, "#DELE /%s\nrm -f /%s; echo '### 000'\n", rpath, rpath); POSTFIX(OPT_FLUSH); } static int fish_mkdir (vfs *me, char *path, mode_t mode) { PREFIX - sprintf(buf, "#MKD %s\nmkdir %s; echo '### 000'\n", remote_path, remote_path); + sprintf(buf, "#MKD /%s\nmkdir /%s; echo '### 000'\n", rpath, rpath); POSTFIX(OPT_FLUSH); } static int fish_rmdir (vfs *me, char *path) { PREFIX - sprintf(buf, "#RMD %s\nrmdir %s; echo '### 000'\n", remote_path, remote_path); + sprintf(buf, "#RMD /%s\nrmdir /%s; echo '### 000'\n", rpath, rpath); POSTFIX(OPT_FLUSH); } +static int retrieve_file(vfs *me, struct vfs_s_inode *ino) +{ + /* If you want reget, you'll have to open file with O_LINEAR */ + int total = 0; + char buffer[8192]; + int handle, n; + int stat_size = ino->st.st_size; + struct vfs_s_fh fh; + + memset(&fh, 0, sizeof(fh)); + + fh.ino = ino; + if (!(ino->localname = tempnam (NULL, me->name))) ERRNOR (ENOMEM, 0); + + handle = open(ino->localname, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600); + if (handle == -1) { + me->verrno = errno; + goto error_4; + } + + if (!MEDATA->linear_start (me, &fh, 0)) + goto error_3; + + /* Clear the interrupt status */ + + while (1) { + n = linear_read(me, &fh, buffer, sizeof(buffer)); + if (n < 0) + goto error_1; + if (!n) + break; + + total += n; + vfs_print_stats (me->name, "Getting file", ino->ent->name, total, stat_size); + + if (write(handle, buffer, n) < 0) { + me->verrno = errno; + goto error_1; + } + } + linear_close(me, &fh); + close(handle); + + if (stat (ino->localname, &ino->u.fish.local_stat) < 0) + ino->u.fish.local_stat.st_mtime = 0; + + return 0; +error_1: + linear_close(me, &fh); +error_3: + disable_interrupt_key(); + close(handle); + unlink(ino->localname); +error_4: + free(ino->localname); + ino->localname = NULL; + return -1; +} + +static int fish_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode) +{ + if (IS_LINEAR(mode)) { + message_1s(1, "Linear mode requested", "?!" ); + fh->linear = LS_LINEAR_CLOSED; + return 0; + } + if (!fh->ino->localname) + if (retrieve_file (me, fh->ino)==-1) + return -1; + if (!fh->ino->localname) + vfs_die( "retrieve_file failed to fill in localname" ); + return 0; +} + +static struct vfs_s_data fish_data = { + NULL, + 0, + 0, + NULL, + + NULL, /* init_inode */ + NULL, /* free_inode */ + NULL, /* init_entry */ + + NULL, /* archive_check */ + archive_same, + open_archive, + free_archive, + + fish_fh_open, /* fh_open */ + NULL, /* fh_close */ + + vfs_s_find_entry_linear, + dir_load, + dir_uptodate, + file_store, + + linear_start, + linear_read, + linear_close +}; + vfs vfs_fish_ops = { NULL, /* This is place of next pointer */ "FIles tranferred over SHell", F_EXEC, /* flags */ "sh:", /* prefix */ - NULL, /* data */ + &fish_data, /* data */ 0, /* errno */ - fish_init, - fish_done, - fish_fill_names, + NULL, + NULL, + vfs_s_fill_names, NULL, - s_open, - s_close, - s_read, - s_write, + vfs_s_open, + vfs_s_close, + vfs_s_read, + vfs_s_write, - s_opendir, - s_readdir, - s_closedir, - s_telldir, - s_seekdir, + vfs_s_opendir, + vfs_s_readdir, + vfs_s_closedir, + vfs_s_telldir, + vfs_s_seekdir, - s_stat, - s_lstat, - s_fstat, + vfs_s_stat, + vfs_s_lstat, + vfs_s_fstat, fish_chmod, - fish_chown, /* not really implemented but returns success */ + fish_chown, NULL, /* utime */ - s_readlink, + vfs_s_readlink, fish_symlink, /* symlink */ fish_link, /* link */ fish_unlink, fish_rename, /* rename */ - s_chdir, - s_errno, - s_lseek, + vfs_s_chdir, + vfs_s_ferrno, + vfs_s_lseek, NULL, /* mknod */ - s_getid, - s_nothingisopen, - s_free, + vfs_s_getid, + vfs_s_nothingisopen, + vfs_s_free, - s_getlocalcopy, - s_ungetlocalcopy, + NULL, /* vfs_s_getlocalcopy, */ + NULL, /* vfs_s_ungetlocalcopy, */ fish_mkdir, fish_rmdir, fish_ctl, - s_setctl + vfs_s_setctl MMAPNULL }; diff --git a/vfs/fish.h b/vfs/fish.h index 47cb816d5..c81721510 100644 --- a/vfs/fish.h +++ b/vfs/fish.h @@ -3,69 +3,6 @@ #if !defined(__FISH_H) #define __FISH_H -struct direntry -{ - char *name; - int count; - char *linkname; - char *local_filename; - int local_is_temp:1; - int freshly_created:1; - struct stat local_stat; - char *remote_filename; - struct stat s; - struct stat *l_stat; - struct connection *bucket; - - int got, total; /* Bytes transfered / bytes need to be transfered */ - int linear_state; -#define LS_NONLIN 0 /* Not using linear access at all */ -#define LS_LINEAR_CLOSED 1 /* Using linear access, but not open, yet */ -#define LS_LINEAR_OPEN 2 /* Using linear access, open */ -}; - -struct dir -{ - int count; - struct timeval timestamp; - char *remote_path; - struct linklist *file_list; -}; - -struct connection { - char *host; - char *user; - char *current_directory; - char *home; - char *updir; - char *password; - int flags; - int sockr, sockw; - struct linklist *dcache; - ino_t __inode_counter; - int lock; - int failed_on_login; /* used to pass the failure reason to upper levels */ - int use_proxy; /* use a proxy server */ - int result_pending; - int use_source_route; - int use_passive_connection; - int isbinary; - int cwd_defered; /* current_directory was changed but CWD command hasn't - been sent yet */ -}; - -#define qhost(b) (b)->host -#define quser(b) (b)->user -#define qcdir(b) (b)->current_directory -#define qflags(b) (b)->flags -#define qsockr(b) (b)->sockr -#define qsockw(b) (b)->sockw -#define qlock(b) (b)->lock -#define qdcache(b) (b)->dcache -#define qhome(b) (b)->home -#define qupdir(b) (b)->updir -#define qproxy(b) (b)->use_proxy - /* Increased since now we may use C-r to reread the contents */ #define FISH_DIRECTORY_TIMEOUT 30 * 60 @@ -76,4 +13,7 @@ struct connection { #define FISH_FLAG_COMPRESSED 1 #define FISH_FLAG_RSH 2 +#define OPT_FLUSH 1 +#define OPT_IGNORE_ERROR 2 + #endif diff --git a/vfs/ftpfs.c b/vfs/ftpfs.c index 754b2545f..078f2ed88 100644 --- a/vfs/ftpfs.c +++ b/vfs/ftpfs.c @@ -173,7 +173,7 @@ static char *get_path (struct connection **bucket, static char * my_get_host_and_username (char *path, char **host, char **user, int *port, char **pass) { - return vfs_get_host_and_username (path, host, user, port, 21, 1, pass); + return vfs_split_url (path, host, user, port, pass, 21, URL_DEFAULTANON); } /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */ @@ -230,6 +230,7 @@ command (struct connection *bucket, int wait_reply, char *fmt, ...) int sock = qsock (bucket); va_start (ap, fmt); + fmt_str = g_strdup_vprintf (fmt, ap); va_end (ap); @@ -265,7 +266,6 @@ command (struct connection *bucket, int wait_reply, char *fmt, ...) if (wait_reply) return get_reply (sock, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1); - return COMPLETE; } @@ -562,13 +562,12 @@ ftpfs_get_proxy_host_and_port (char *proxy, char **host, int *port) char *user, *pass, *dir; #if defined(HSC_PROXY) -#define HSC_DEFAULT_PORT 9875 - dir = vfs_get_host_and_username(proxy, host, &user, port, HSC_DEFAULT_PORT, 1, - &pass); +#define PORT 9875 #else - dir = vfs_get_host_and_username(proxy, host, &user, port, 21, 1, - &pass); +#define PORT 21 #endif + dir = vfs_split_url(proxy, host, &user, port, &pass, PORT, URL_DEFAULTANON); + free(user); if (pass) wipe_password (pass); diff --git a/vfs/mcfs.c b/vfs/mcfs.c index 8d44d59bd..bd9423a12 100644 --- a/vfs/mcfs.c +++ b/vfs/mcfs.c @@ -79,7 +79,7 @@ static char *mcfs_gethome (mcfs_connection *mc); static char *mcfs_get_host_and_username (char *path, char **host, char **user, int *port, char **pass) { - return vfs_get_host_and_username (path, host, user, port, 0, 0, pass); + return vfs_split_url (path, host, user, port, pass, 0, 0); } static void mcfs_fill_names (vfs *me, void (*func)(char *)) diff --git a/vfs/shared_ftp_fish.c b/vfs/shared_ftp_fish.c index a3e5ce54f..1098071b9 100644 --- a/vfs/shared_ftp_fish.c +++ b/vfs/shared_ftp_fish.c @@ -1,6 +1,8 @@ /* * Shared code between the fish.c and the ftp.c file systems * + * Actually, this code is not being used by fish.c any more :-). + * * Namespace pollution: X_hint_reread, X_flushdir. */ static int store_file (struct direntry *fe); diff --git a/vfs/tar.c b/vfs/tar.c index de6927d65..8073e3f75 100644 --- a/vfs/tar.c +++ b/vfs/tar.c @@ -67,8 +67,6 @@ static void tar_free_archive (vfs *me, vfs_s_super *archive) { if (archive->u.tar.fd != -1) mc_close(archive->u.tar.fd); - - free (archive->name); } struct tar_super { @@ -119,9 +117,10 @@ static int tar_open_archive (vfs *me, char *name, vfs_s_super *archive) if (mode & 0004) mode |= 0001; mode |= S_IFDIR; - root = vfs_s_new_inode (me, &archive->u.tar.tarstat); + root = vfs_s_new_inode (me, archive, &archive->u.tar.tarstat); root->st.st_mode = mode; root->u.tar.data_offset = -1; + root->st.st_nlink++; vfs_s_add_dots (me, root, NULL); archive->root = root; @@ -340,7 +339,6 @@ static int read_header (vfs *me, vfs_s_super *archive, int tard) message_1s (1, MSG_ERROR, _("Inconsistent tar archive")); } else { inode = parent; - inode->st.st_nlink++; entry = vfs_s_new_entry(me, p, inode); vfs_s_insert_entry(me, parent, entry); free (current_link_name); @@ -349,16 +347,11 @@ static int read_header (vfs *me, vfs_s_super *archive, int tard) } fill_stat_from_header (me, &st, header); - inode = vfs_s_new_inode (me, &st); + inode = vfs_s_new_inode (me, archive, &st); inode->u.tar.data_offset = data_position; - if (*current_link_name) { + if (*current_link_name) inode->linkname = current_link_name; - } else { - free (current_link_name); - inode->linkname = NULL; - } - entry = vfs_s_new_entry (me, p, inode); vfs_s_insert_entry (me, parent, entry); @@ -387,7 +380,6 @@ static int open_archive (vfs *me, vfs_s_super *archive, char *name, char *op) if ((tard = tar_open_archive (me, name, archive)) == -1) /* Open for reading */ return -1; - archive->name = strdup(name); for (;;) { prev_status = status; status = read_header (me, archive, tard); @@ -433,7 +425,7 @@ void *tar_super_check(vfs *me, char *archive_name, char *op) return &stat_buf; } -int tar_super_same(vfs *me, struct vfs_s_super *parc, char *archive_name, void *cookie) +int tar_super_same(vfs *me, struct vfs_s_super *parc, char *archive_name, char *op, void *cookie) { struct stat *archive_stat = cookie; /* stat of main archive */ @@ -457,8 +449,8 @@ int tar_super_same(vfs *me, struct vfs_s_super *parc, char *archive_name, void * static int tar_read (void *fh, char *buffer, int count) { off_t begin = FH->ino->u.tar.data_offset; - int fd = FH->super->u.tar.fd; - vfs *me = FH->super->me; + int fd = FH_SUPER->u.tar.fd; + vfs *me = FH_SUPER->me; if (mc_lseek (fd, begin + FH->pos, SEEK_SET) != begin + FH->pos) ERRNOR (EIO, -1); @@ -477,10 +469,17 @@ static void tar_ungetlocalcopy (vfs *me, char *path, char *local, int has_change since it will be freed when tar archive will be freed */ } +static int tar_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode) +{ + if ((flags & O_ACCMODE) != O_RDONLY) ERRNOR (EROFS, -1); + return 0; +} + static struct vfs_s_data tarfs_data = { NULL, 0, 0, + NULL, /* logfile */ NULL, /* init_inode */ NULL, /* free_inode */ @@ -491,10 +490,12 @@ static struct vfs_s_data tarfs_data = { open_archive, tar_free_archive, - NULL, /* load_dir */ + tar_fh_open, /* fh_open */ + NULL, /* fh_close */ - NULL, /* fh_open */ - NULL /* fh_close */ + vfs_s_find_entry_tree, + NULL, + NULL }; vfs vfs_tarfs_ops = diff --git a/vfs/utilvfs.c b/vfs/utilvfs.c index b47e6a255..b438c312e 100644 --- a/vfs/utilvfs.c +++ b/vfs/utilvfs.c @@ -41,6 +41,7 @@ #endif #include +#include "vfs.h" /* Extract the hostname and username from the path */ /* path is in the form: [user@]hostname:port/remote-dir, e.g.: @@ -58,27 +59,31 @@ * returns a malloced strings with the pathname relative to the host. * */ -char *vfs_get_host_and_username (char *path, char **host, char **user, int *port, - int default_port, int default_is_anon, char **pass) +char *vfs_split_url (char *path, char **host, char **user, int *port, char **pass, + int default_port, int flags) { struct passwd *passwd_info; char *dir, *colon, *inner_colon, *at, *rest; char *retval; char *pcopy = strdup (path); char *pend = pcopy + strlen (pcopy); + int default_is_anon = flags & URL_DEFAULTANON; *pass = NULL; *port = default_port; *user = NULL; - /* locate path component */ - for (dir = pcopy; *dir != '/' && *dir; dir++) - ; - if (*dir){ - retval = strdup (dir); - *dir = 0; - } else - retval = strdup ("/"); + dir = pcopy; + if (!flags & URL_NOSLASH) { + /* locate path component */ + for (; *dir != '/' && *dir; dir++) + ; + if (*dir){ + retval = strdup (dir); + *dir = 0; + } else + retval = strdup ("/"); + } /* search for any possible user */ at = strchr (pcopy, '@'); diff --git a/vfs/vfs.c b/vfs/vfs.c index 2f4323049..b3a79911b 100644 --- a/vfs/vfs.c +++ b/vfs/vfs.c @@ -115,7 +115,7 @@ vfs_register (vfs *vfs) { int res; - g_return_if_fail (vfs != NULL); + if (!vfs) vfs_die("You can not register NULL."); res = (vfs->init) ? (*vfs->init)(vfs) : 1; @@ -132,9 +132,10 @@ vfs_type_from_op (char *path) { vfs *vfs; - g_return_if_fail (path != NULL); + if (!path) vfs_die( "vfs_type_from_op got NULL: impossible" ); for (vfs = vfs_list; vfs; vfs = vfs->next){ + /* FIXME: this code could be much more elegant */ if (vfs == &vfs_local_ops) /* local catches all */ return NULL; if (vfs->which) { @@ -155,8 +156,6 @@ path_magic (char *path) { struct stat buf; - g_return_if_fail (path != NULL); - if (vfs_flags & FL_ALWAYS_MAGIC) return 1; @@ -178,7 +177,7 @@ vfs_split (char *path, char **inpath, char **op) char *slash; vfs *ret; - g_return_val_if_fail (path != NULL, NULL); + if (!path) vfs_die("Can not split NULL"); semi = strrchr (path, '#'); if (!semi || !path_magic(path)) @@ -218,7 +217,7 @@ vfs_rosplit (char *path) char *slash; vfs *ret; - g_return_val_if_fail (path != NULL, NULL); + if (!path) vfs_die( "Can not rosplit NULL" ); semi = strrchr (path, '#'); if (!semi || !path_magic (path)) @@ -245,8 +244,6 @@ vfs_type (char *path) { vfs *vfs; - g_return_val_if_fail (path != NULL, NULL); - vfs = vfs_rosplit(path); if (!vfs) @@ -271,8 +268,6 @@ vfs_timeouts () void vfs_addstamp (vfs *v, vfsid id, struct vfs_stamping *parent) { - g_return_if_fail (v != NULL); - if (v != &vfs_local_ops && id != (vfsid)-1){ struct vfs_stamping *stamp, *st1; @@ -312,8 +307,6 @@ vfs_stamp (vfs *v, vfsid id) { struct vfs_stamping *stamp; - g_return_if_fail (v != NULL); - for (stamp = stamps; stamp != NULL; stamp = stamp->next) if (stamp->v == v && stamp->id == id){ @@ -364,7 +357,7 @@ vfs_rmstamp (vfs *v, vfsid id, int removeparents) static int ferrno (vfs *vfs) { - return vfs->ferrno ? (*vfs->ferrno)(vfs) : EOPNOTSUPP; + return vfs->ferrno ? (*vfs->ferrno)(vfs) : E_UNKNOWN; /* Hope that error message is obscure enough ;-) */ } @@ -415,7 +408,7 @@ mc_open (char *file, int flags, ...) result = vfs->name ? (*vfs->name)callarg : -1; \ post \ if (result == -1) \ - errno = vfs->name ? ferrno (vfs) : EOPNOTSUPP; \ + errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \ return result; \ } @@ -482,6 +475,8 @@ mc_opendir (char *dirname) char *p = NULL; int i = strlen (dirname); +message_1s(1, " Opening ", dirname ); + if (dirname [i - 1] != '/'){ /* We should make possible reading of the root directory in a tar file */ p = xmalloc (i + 2, "slash"); @@ -497,7 +492,7 @@ mc_opendir (char *dirname) if (p) free (p); if (!info){ - errno = vfs->opendir ? ferrno (vfs) : EOPNOTSUPP; + errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP; return NULL; } handle = get_bucket (); @@ -527,7 +522,7 @@ mc_seekdir (DIR *dirp, int offset) if (vfs->seekdir) (*vfs->seekdir) (vfs_info (handle), offset); else - errno = EOPNOTSUPP; + errno = E_NOTSUPP; } #define MC_DIROP(name, type, onerr ) \ @@ -545,7 +540,7 @@ type mc_##name (DIR *dirp) \ vfs = vfs_op (handle); \ result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \ if (result == onerr) \ - errno = vfs->name ? ferrno(vfs) : EOPNOTSUPP; \ + errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \ return result; \ } @@ -639,7 +634,7 @@ int mc_##name (char *name1, char *name2) \ free (name1); \ free (name2); \ if (result == -1) \ - errno = vfs->name ? ferrno (vfs) : EOPNOTSUPP; \ + errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \ return result; \ } @@ -659,7 +654,7 @@ off_t mc_lseek (int fd, off_t offset, int whence) vfs = vfs_op (fd); result = vfs->lseek ? (*vfs->lseek)(vfs_info (fd), offset, whence) : -1; if (result == -1) - errno = vfs->lseek ? ferrno (vfs) : EOPNOTSUPP; + errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP; return result; } @@ -672,7 +667,7 @@ off_t mc_lseek (int fd, off_t offset, int whence) char * vfs_canon (char *path) { - g_return_if_fail (path != NULL); + if (!path) vfs_die("Can not canonize NULL"); /* Tilde expansion */ if (*path == '~'){ @@ -705,7 +700,9 @@ vfs_canon (char *path) * So we have path of following form: * /p1/p2#op/.././././p3#op/p4. Good luck. */ + mad_check( "(pre-canonicalize)", 0); canonicalize_pathname (path); + mad_check( "(post-canonicalize)", 0); return strdup (path); } diff --git a/vfs/vfs.h b/vfs/vfs.h index a8f6a11ff..9a79033dc 100644 --- a/vfs/vfs.h +++ b/vfs/vfs.h @@ -1,6 +1,14 @@ #ifndef __VFS_H #define __VFS_H +#include +#include +#include + +#if 0 +#include "src/mad.h" +#endif + #if !defined(SCO_FLAVOR) || !defined(_SYS_SELECT_H) # include /* alex: this redefines struct timeval */ #endif /* SCO_FLAVOR */ @@ -316,21 +324,21 @@ extern int vfs_parse_filedate(int idx, time_t *t); extern void vfs_die (char *msg); extern char *vfs_get_password (char *msg); -extern char *vfs_get_host_and_username (char *path, char **host, char **user, int *port, - int default_port, int default_is_anon, char **pass); +extern char *vfs_split_url (char *path, char **host, char **user, int *port, char **pass, + int default_port, int flags); +#define URL_DEFAULTANON 1 +#define URL_NOSLASH 2 extern void vfs_print_stats (char *fs_name, char *action, char *file_name, int have, int need); -#define MCCTL_SETREMOTECOPY 0 -#define MCCTL_ISREMOTECOPY 1 -#define MCCTL_REMOTECOPYCHUNK 2 -#define MCCTL_FINISHREMOTE 3 -#define MCCTL_FLUSHDIR 4 /* Unreferenced */ +/* Dont use values 0..4 for a while -- 10/98, pavel@ucw.cz */ #define MCCTL_REMOVELOCALCOPY 5 #define MCCTL_IS_NOTREADY 6 #define MCCTL_FORGET_ABOUT 7 #define MCCTL_EXTFS_RUN 8 +/* These two make vfs layer give out potentially incorrect data, but + they also make some operation 100 times faster. Use with caution. */ #define MCCTL_WANT_STALE_DATA 9 -#define MCCTL_NO_STALE_DATA 10 +#define MCCTL_NO_STALE_DATA 10 extern int vfs_flags; extern uid_t vfs_uid; @@ -393,4 +401,11 @@ extern void mc_vfs_done( void ); #define DIR_SEP_CHAR '/' +/* And now some defines for our errors. */ + +#define E_NOTSUPP ENOSYS /* for use in vfs when module does not provide function */ +#define E_UNKNOWN ENOMSG /* if we do not know what error happened */ +#define E_REMOTE EREMOTEIO /* if other side of ftp/fish reports error */ +#define E_PROTO EPROTO /* if other side fails to follow protocol */ + #endif /* __VFS_H */ diff --git a/vfs/xdirentry.h b/vfs/xdirentry.h index a2265619c..3590a22e3 100644 --- a/vfs/xdirentry.h +++ b/vfs/xdirentry.h @@ -12,7 +12,9 @@ #include #include #include -#include +#include +#include + #include "../src/fs.h" #include "../src/util.h" #include "../src/mem.h" @@ -27,32 +29,45 @@ #define FL_NONE 0 #define FL_MKDIR 1 #define FL_MKFILE 2 +#define FL_DIR 4 /* For open_super */ #define FL_NO_OPEN 1 /* For vfs_s_entry_from_path */ #define FL_FOLLOW 1 -#define FL_DIR 2 +#define FL_DIR 4 typedef struct vfs_s_entry { struct vfs_s_entry **prevp, *next; - struct vfs_s_inode *dir; /* Directory we are in - FIXME: is this needed? */ + struct vfs_s_inode *dir; /* Directory we are in - needed for invalidating directory when file in it changes */ char *name; /* Name of this entry */ struct vfs_s_inode *ino; /* ... and its inode */ + int magic; +#define ENTRY_MAGIC 0x014512563 } vfs_s_entry; typedef struct vfs_s_inode { struct vfs_s_entry *subdir; + struct vfs_s_super *super; struct stat st; /* Parameters of this inode */ char *linkname; /* Symlink's contents */ - char *localname; /* filename of local file, if we have one */ + char *localname; /* Filename of local file, if we have one */ + int flags; + + vfs_s_entry *ent; /* ftp needs this backpointer; don't use if you can avoid it */ union { struct { long data_offset; } tar; + struct { + struct timeval timestamp; + struct stat local_stat; + } fish; } u; + int magic; +#define INODE_MAGIC 0x93451656 } vfs_s_inode; typedef struct vfs_s_super { @@ -61,63 +76,96 @@ typedef struct vfs_s_super { vfs_s_inode *root; char *name; /* My name, whatever it means */ int fd_usage; /* Usage count of this superblock */ + int want_stale; /* If set, we do not flush cache properly */ union { struct { int fd; struct stat tarstat; } tar; + struct { + int sockr, sockw; + char *home, *cwdir; + char *host, *user; + char *password; + int flags; + } fish; + struct { + int sockr, sockw; + char *home, *cwdir; + int isbinary; + } ftp; } u; + int magic; +#define SUPER_MAGIC 0x915ac312 } vfs_s_super; typedef struct vfs_s_fh { - struct vfs_s_super *super; /* Is this field needed? */ struct vfs_s_inode *ino; long pos; /* This is for module's use */ int handle; /* This is for module's use, but if != -1, will be mc_close()d */ + int changed; /* Did this file change? */ + int linear; /* Is that file open with O_LINEAR? */ + union { + struct { + int got, total; + } fish; + } u; + int magic; +#define FH_MAGIC 0x91324682 } vfs_s_fh; struct vfs_s_data { struct vfs_s_super *supers; int inode_counter; dev_t rdev; + FILE *logfile; - int (*init_inode) (vfs *me, vfs_s_inode *ino); - void (*free_inode) (vfs *me, vfs_s_inode *ino); - int (*init_entry) (vfs *me, vfs_s_entry *entry); + int (*init_inode) (vfs *me, vfs_s_inode *ino); /* optional */ + void (*free_inode) (vfs *me, vfs_s_inode *ino); /* optional */ + int (*init_entry) (vfs *me, vfs_s_entry *entry); /* optional */ - void* (*archive_check) (vfs *me, char *name, char *op); - int (*archive_same) (vfs *me, vfs_s_super *psup, char *archive_name, void *cookie); + void* (*archive_check) (vfs *me, char *name, char *op); /* optional */ + int (*archive_same) (vfs *me, vfs_s_super *psup, char *archive_name, char *op, void *cookie); int (*open_archive) (vfs *me, vfs_s_super *psup, char *archive_name, char *op); void (*free_archive) (vfs *me, vfs_s_super *psup); - int (*load_dir) (vfs *me, vfs_s_super *super, char *q); - - int (*fh_open) (vfs *me, vfs_s_fh *fh); + int (*fh_open) (vfs *me, vfs_s_fh *fh, int flags, int mode); int (*fh_close) (vfs *me, vfs_s_fh *fh); + + vfs_s_entry* (*find_entry) (vfs *me, vfs_s_inode *root, char *path, int follow, int flags); + int (*dir_load) (vfs *me, vfs_s_inode *ino, char *path); + int (*dir_uptodate) (vfs *me, vfs_s_inode *ino); + int (*file_store) (vfs *me, vfs_s_super *super, char *path, char *localname); + + int (*linear_start) (vfs *me, vfs_s_fh *fh, int from); + int (*linear_read) (vfs *me, vfs_s_fh *fh, void *buf, int len); + void (*linear_close) (vfs *me, vfs_s_fh *fh); }; /* entries and inodes */ -vfs_s_inode *vfs_s_new_inode (vfs *me, struct stat *initstat); +vfs_s_inode *vfs_s_new_inode (vfs *me, vfs_s_super *super, struct stat *initstat); vfs_s_entry *vfs_s_new_entry (vfs *me, char *name, vfs_s_inode *inode); void vfs_s_free_entry (vfs *me, vfs_s_entry *ent); -void vfs_s_free_tree (vfs *me, vfs_s_inode *dir, vfs_s_inode *parentdir); void vfs_s_insert_entry (vfs *me, vfs_s_inode *dir, vfs_s_entry *ent); struct stat *vfs_s_default_stat (vfs *me, mode_t mode); void vfs_s_add_dots (vfs *me, vfs_s_inode *dir, vfs_s_inode *parent); struct vfs_s_entry *vfs_s_generate_entry (vfs *me, char *name, struct vfs_s_inode *parent, mode_t mode); vfs_s_entry *vfs_s_automake(vfs *me, vfs_s_inode *dir, char *path, int flags); -vfs_s_entry *vfs_s_find_entry(vfs *me, vfs_s_inode *root, char *path, int follow, int flags); +vfs_s_entry *vfs_s_find_entry_tree(vfs *me, vfs_s_inode *root, char *path, int follow, int flags); +vfs_s_entry *vfs_s_find_entry_linear(vfs *me, vfs_s_inode *root, char *path, int follow, int flags); vfs_s_inode *vfs_s_find_inode(vfs *me, vfs_s_inode *root, char *path, int follow, int flags); vfs_s_inode *vfs_s_find_root(vfs *me, vfs_s_entry *entry); -struct vfs_s_entry *vfs_s_resolve_symlink (vfs *me, struct vfs_s_entry *entry, int follow); +struct vfs_s_entry *vfs_s_resolve_symlink (vfs *me, vfs_s_entry *entry, int follow); /* superblock games */ vfs_s_super *vfs_s_new_super (vfs *me); void vfs_s_free_super (vfs *me, vfs_s_super *super); /* outside interface */ -char *vfs_s_get_path_mangle (vfs *me, char *inname, struct vfs_s_super **archive, int flags); -char *vfs_s_get_path (vfs *me, char *inname, struct vfs_s_super **archive, int flags); +char *vfs_s_get_path_mangle (vfs *me, char *inname, vfs_s_super **archive, int flags); +char *vfs_s_get_path (vfs *me, char *inname, vfs_s_super **archive, int flags); +void vfs_s_invalidate (vfs *me, vfs_s_super *super); /* readdir & friends */ +vfs_s_super *vfs_s_super_from_path (vfs *me, char *name); vfs_s_inode *vfs_s_inode_from_path (vfs *me, char *name, int flags); void * vfs_s_opendir (vfs *me, char *dirname); void * vfs_s_readdir (void *data); @@ -131,6 +179,8 @@ int vfs_s_lstat (vfs *me, char *path, struct stat *buf); int vfs_s_fstat (void *fh, struct stat *buf); int vfs_s_readlink (vfs *me, char *path, char *buf, int size); void *vfs_s_open (vfs *me, char *file, int flags, int mode); +int vfs_s_read (void *fh, char *buffer, int count); +int vfs_s_write (void *fh, char *buffer, int count); int vfs_s_lseek (void *fh, off_t offset, int whence); int vfs_s_close (void *fh); /* mc support */ @@ -142,6 +192,11 @@ char *vfs_s_getlocalcopy (vfs *me, char *path); vfsid vfs_s_getid (vfs *me, char *path, struct vfs_stamping **parent); int vfs_s_nothingisopen (vfsid id); void vfs_s_free (vfsid id); +int vfs_s_setctl (vfs *me, char *path, int ctlop, char *arg); +/* network filesystems support */ +int vfs_s_select_on_two (int fd1, int fd2); +int vfs_s_get_line (vfs *me, int sock, char *buf, int buf_len, char term); +int vfs_s_get_line_interruptible (vfs *me, char *buffer, int size, int fd); /* If non-null, FREE */ #define ifree(ptr) do { if (ptr) free(ptr); } while (0) @@ -149,5 +204,10 @@ void vfs_s_free (vfsid id); #define MEDATA ((struct vfs_s_data *) me->data) #define ERRNOR(a, b) do { me->verrno = a; return b; } while (0) #define FH ((struct vfs_s_fh *) fh) +#define FH_SUPER FH->ino->super + +#define LS_NOT_LINEAR 0 +#define LS_LINEAR_CLOSED 1 +#define LS_LINEAR_OPEN 2 #endif