diff -urN newtree/fs/Kconfig newtree.2/fs/Kconfig --- newtree/fs/Kconfig 2006-08-02 07:14:21.000000000 -0700 +++ newtree.2/fs/Kconfig 2006-08-04 10:03:28.000000000 -0700 @@ -1939,6 +1939,23 @@ If unsure, say N. +config SH_FS + tristate "SSH File System support (SHFS) (Experimental)" + depends on INET && EXPERIMENTAL + help + SHFS is a simple and easy to use Linux kernel module which + allows you to mount remote filesystems using plain shell (ssh/rsh) + connection. It supports some nice features like number of different + caches for access speedup, target system optimisations, etc. + + Further information on mounting ssh shares and the options + available can be found at http://shfs.sourceforge.net. + + If you want to compile the SSH support as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want), say M here and read Documentation/modules.txt. The module + will be called shfs.o. Most people say N, however. + config RXRPC tristate diff -urN newtree/fs/Makefile newtree.2/fs/Makefile --- newtree/fs/Makefile 2006-08-02 07:14:21.000000000 -0700 +++ newtree.2/fs/Makefile 2006-08-04 10:03:28.000000000 -0700 @@ -101,6 +101,7 @@ obj-$(CONFIG_9P_FS) += 9p/ obj-$(CONFIG_AFS_FS) += afs/ obj-$(CONFIG_BEFS_FS) += befs/ +obj-$(CONFIG_SH_FS) += shfs/ obj-$(CONFIG_HOSTFS) += hostfs/ obj-$(CONFIG_HPPFS) += hppfs/ obj-$(CONFIG_DEBUG_FS) += debugfs/ diff -urN newtree/fs/shfs/Changelog newtree.2/fs/shfs/Changelog --- newtree/fs/shfs/Changelog 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/Changelog 2006-08-04 10:03:28.000000000 -0700 @@ -0,0 +1,176 @@ +shfs (0.35) unstable; urgency=low + + * fixed MAN_PAGE_DIR (thanks to Janus N. Tondering) + * shfsumount added to shfs-utils.spec file (reported by + Richard Lucassen & waldeck at abacho de) + * build process improvements (make dist includes html pages now) + * group write access is sufficient to mount dir as ordinary user + * fsync() should not return -EINVAL + * ls shows incorrect sizes for file bigger than 2GB (thanks to Christoph + camikusch at web de) + + -- Miroslav Spousta Wed, 24 Mar 2004 16:42:46 +0100 + +shfs (0.34) unstable; urgency=low + + * manpage is installed by default (suggested by Ladislav Hagara + hgr at vabo.cz) + * faq updated + * handling of ' fixed (reported by richard lucassen spamtrap at + lucassen.org) + * du command show incorrent sizes + * perl version 5.00x should be correctly recognised + * statfs should work on most systems (df -k -P => df -k) + * shfsumount added again (makes possible user umounts) + * shfsmount -o stable option fixed + * copy read-only files fixed + * kernel version is now part of the module package name + * fcache size made configurable + * max. number of cached files is configurable + * MacOS X fixes: ls resturns 0 on file-not-found + ls -l month & day columns are swapped + * lockup fix on ssh exit fixed + * test -e warning on SunOS fixed + * test for perl modules fixed + * nomtab (-n) option added (by Marc Welz) + * stderr is /dev/tty now + + -- Miroslav Spousta Fri, 28 May 2004 12:06:28 +0200 + +shfs (0.33) unstable; urgency=low + + * huge portions rewritten, smbfs/ncpfs dir cache code assimilated + * "server" code moved to userspace + * user option added for shfsmount, shfsumount removed (thanks + to Markus Kuhn, mkuhn at users.sourceforge.net). + * remote OS detection removed (great tips/patches from Csaba Henk, + ekho at renyi.hu) + * shell code fixes + * perl server support added + * persistent option added + * preserve works r/w (perl server) + * any command is supported for connection (--cmd option) + * shfsmount options added (automounter support) + * debug support changed (can be turned on at mount time: -v option) + * file cache memory leak hopefully fixed + * SMP fixes + * emacs should be working now (time sync troubles workarounded) + * docs update + + -- Miroslav Spousta Sat, 20 Mar 2004 17:50:43 +0100 + +shfs (0.32) unstable; urgency=low + + * uid & gid mount options should work corrctly now + * do not close stderr for ssh (sshv1 should work now) + * docbase path fixed for debian package + (thanks to Christian Schrader, Christian.Schrader at gmx.de) + + -- Miroslav Spousta Tue, 4 Nov 2003 10:53:43 +0100 + +shfs (0.32pre2) unstable; urgency=low + + * dentry caching rewritten, dcache.c does not use hash table. + * Oops fixed (thanks to Antoine Labour, antoine.labour at m4x.org). + * hash tables removed from the file cache. + * Directory order fixed (fix from Dylan Hall, dylan.hall at tsnz.net). + * SIGPIPE fixed on dir open when connection is dead (thanks to + Michael Bartlett, michael.bartlett at workshare.com) + * SIGINT is postponed while in reading remote data. + * ttl option added for shfsmount + * Cygwin support added + * statfs implemented (df shows remote share) + * stable (autoresolve) symlinks option added + * mount<->module API version introduced + * full disk behaviour corrected + * AMD automounter support removed. + * autofs automounter freeze fix (thanks to Wataru Fujishige, + wataru at fujishige.net) + * module sources split between 2.4 and 2.6 version + * kernel patch make option added. + * docs update. + * settime implemented. + * "preserve" implies "ro" mount attribute. + * close() return value fixed. + + -- Miroslav Spousta Sat, 6 Sep 2003 12:46:07 +0200 + +shfs (0.32pre1) unstable; urgency=low + + * speed improvement: use socket instead of pair of pipes + * rpm depmod call fixed (thanks to Nathaniel Gray, n8gray at caltech.edu) + * Install ln -fs fix (thanks to Brett Kuskie, mstrprgmmr at chek.com) + * DEBUG variable fixes + * 32bit uid/gid fix + * rpm build fix + * compiler warning fixed + * docs typos fixed (thanks to Makis Marmaridis, makis at marmaridis.org) + * rpm build fix (thanks to Richard Lucassen, spamtrap at lucassen.org) + * inode attributes fix + * does not use release numbers + * build fixes (does not require root) + + -- Miroslav Spousta Tue, 10 Jun 2003 10:03:00 +0200 + +shfs (0.31-1) unstable; urgency=low + + * top Makefile DEBUG variable added + * user mounts fully supported (thanks to Grzegorz Jasko) + * shfsumount command added + * suid, nosuid, dev, nodev, exec, noexec mount options added + * doc updates + + -- Miroslav Spousta Sat, 10 May 2003 9:23:08 +0200 + +shfs (0.30-1) unstable; urgency=low + + * linux 2.4.19+ stale NFS handle fix + * OSF1 support added (thanks to stanimir at rdscv.ro) + + -- Miroslav Spousta Tue, 6 May 2003 15:09:08 +0200 + +shfs (0.29-2) unstable; urgency=low + + * rc 2 + * IRIX64 symlink fix + * create () should respect given access mode now + * shell protocol fixes + * gcc 3.x compilation fixes + + -- Miroslav Spousta Fri, 31 Jan 2003 10:16:00 +0100 + +shfs (0.29-1) unstable; urgency=low + + * release candidate 1 :-) + * shell protocol rewritten to use shell functions + * removed dentry ops -> nasty invalid symlink bug (Oops) fixed + * write does not invalidate parent dir cache entry + * dcache validity improvements + * no more kmem alloc for dir entries -> (SLAB) name_cache + * docs update + + -- Miroslav Spousta Sun, 26 Jan 2003 20:06:51 +0100 + +shfs (0.28) unstable; urgency=low + + * Makefiles cleanup & rewrite + * shfsmount rewritten, options changed and added + * debian package support + * rpm package support + * readahead cache speedup for small files + * "write to full disk" error detection and recovery + * file append fix (file_commitwrite()) + * clean garbage fix + * remote timezone fix + * invalid dd usage (locale) in read fixed + * directory traversal fix (no more "find: changed during execution") + * create symlink fix + + -- Miroslav Spousta Thu, 1 Aug 2002 10:15:11 +0200 + +shfs (0.27) unstable; urgency=low + + * Version 0.27 released (first public) + + -- Miroslav Spousta Sat, 8 Jun 2002 08:00:00 +0200 + diff -urN newtree/fs/shfs/Makefile newtree.2/fs/shfs/Makefile --- newtree/fs/shfs/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/Makefile 2006-08-04 10:03:28.000000000 -0700 @@ -0,0 +1,60 @@ +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-m := shfs.o + +shfs-objs := dcache.o dir.o fcache.o file.o inode.o ioctl.o proc.o shell.o symlink.o + +else +# external module build + +ifndef KERNEL +KERNEL=$(shell uname -r) +endif + +ifndef MODULESDIR +MODULESDIR=${ROOT}/lib/modules/${KERNEL} +endif + +ifndef KERNEL_SOURCES +KERNEL_SOURCES=${MODULESDIR}/build +endif + +LINVER := linux-${KERNEL} +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNEL_SOURCES) SUBDIRS=$(PWD) modules + +clean: patch-clean + rm -f *.o *.ko *.mod.c .*o.cmd + +install: shfs.ko + rm -f ${MODULESDIR}/kernel/fs/shfs/shfs.ko + install -m644 -b -D shfs.ko ${MODULESDIR}/kernel/fs/shfs/shfs.ko + if [ -x /sbin/depmod -a "${ROOT}" = "/" ]; then /sbin/depmod -aq; fi + +uninstall: + rm -rf ${MODULESDIR}/kernel/fs/shfs + if [ -x /sbin/depmod -a "${ROOT}" = "/" ]; then /sbin/depmod -aq; fi + +patch: + rm -rf ${LINVER} ${LINVER}.orig; mkdir ${LINVER}; + for i in fs/shfs include/linux; do \ + mkdir -p ${LINVER}/$$i; \ + done + cp ${KERNEL_SOURCES}/fs/{Makefile,Kconfig} ${LINVER}/fs + cp -r ${LINVER} ${LINVER}.orig + cp ../../Changelog Makefile *.c shfs_debug.h proc.h ${LINVER}/fs/shfs/ + cp shfs.h shfs_fs* ${LINVER}/include/linux/ + (cd ${LINVER}; patch -p1 <../kernel-config.diff) + find . -type f -name "*.orig" -print | xargs rm -f + diff -urN ${LINVER}.orig ${LINVER} >${LINVER}.diff; true + +patch-clean: + rm -rf ${LINVER} ${LINVER}.orig; + rm -f ${LINVER}.diff + +endif + +.PHONY : all tidy clean install uninstall patch patch-clean diff -urN newtree/fs/shfs/dcache.c newtree.2/fs/shfs/dcache.c --- newtree/fs/shfs/dcache.c 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/dcache.c 2006-08-04 10:03:28.000000000 -0700 @@ -0,0 +1,210 @@ +/* + * cache.c + * + * Copyright (C) 1997 by Bill Hawes + * Copyright (C) 2004 Miroslav Spousta + * + * Routines to support directory cacheing using the page cache. + * This cache code is almost directly taken from smbfs/ncpfs. + * + */ + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "shfs_debug.h" + +/* + * Force the next attempt to use the cache to be a timeout. + * If we can't find the page that's fine, it will cause a refresh. + */ +void +shfs_invalid_dir_cache(struct inode * dir) +{ + struct shfs_sb_info *info = info_from_inode(dir); + union shfs_dir_cache *cache = NULL; + struct page *page = NULL; + + page = grab_cache_page(&dir->i_data, 0); + if (!page) + goto out; + + if (!PageUptodate(page)) + goto out_unlock; + + cache = kmap(page); + cache->head.time = jiffies - SHFS_MAX_AGE(info); + + kunmap(page); + SetPageUptodate(page); +out_unlock: + unlock_page(page); + page_cache_release(page); +out: + return; +} + +/* + * Mark all dentries for 'parent' as invalid, forcing them to be re-read + */ +void +shfs_invalidate_dircache_entries(struct dentry *parent) +{ + struct shfs_sb_info *info = info_from_dentry(parent); + struct list_head *next; + struct dentry *dentry; + + spin_lock(&dcache_lock); + next = parent->d_subdirs.next; + while (next != &parent->d_subdirs) { + dentry = list_entry(next, struct dentry, d_child); + dentry->d_fsdata = NULL; + shfs_age_dentry(info, dentry); + next = next->next; + } + spin_unlock(&dcache_lock); +} + +/* + * dget, but require that fpos and parent matches what the dentry contains. + * dentry is not known to be a valid pointer at entry. + */ +struct dentry * +shfs_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) +{ + struct dentry *dent = dentry; + struct list_head *next; + + if (d_validate(dent, parent)) { + if ((unsigned long)dent->d_fsdata == fpos) { + if (!dent->d_inode) { + dput(dent); + dent = NULL; + } + return dent; + } + dput(dent); + } + + /* If a pointer is invalid, we search the dentry. */ + spin_lock(&dcache_lock); + next = parent->d_subdirs.next; + while (next != &parent->d_subdirs) { + dent = list_entry(next, struct dentry, d_child); + if ((unsigned long)dent->d_fsdata == fpos) { + if (dent->d_inode) + dget_locked(dent); + else + dent = NULL; + goto out_unlock; + } + next = next->next; + } + dent = NULL; +out_unlock: + spin_unlock(&dcache_lock); + return dent; +} + +/* + * Create dentry/inode for this file and add it to the dircache. + */ +int +shfs_fill_cache(struct file *filp, void *dirent, filldir_t filldir, + struct shfs_cache_control *ctrl, struct qstr *qname, + struct shfs_fattr *entry) +{ + struct dentry *newdent, *dentry = filp->f_dentry; + struct inode *newino, *inode = dentry->d_inode; + struct shfs_cache_control ctl = *ctrl; + int valid = 0; + int hashed = 0; + ino_t ino = 0; + + qname->hash = full_name_hash(qname->name, qname->len); + + if (dentry->d_op && dentry->d_op->d_hash) + if (dentry->d_op->d_hash(dentry, qname) != 0) + goto end_advance; + + newdent = d_lookup(dentry, qname); + + if (!newdent) { + newdent = d_alloc(dentry, qname); + if (!newdent) + goto end_advance; + } else { + hashed = 1; + memcpy((char *) newdent->d_name.name, qname->name, + newdent->d_name.len); + } + + if (!newdent->d_inode) { + shfs_renew_times(newdent); + entry->f_ino = iunique(inode->i_sb, 2); + newino = shfs_iget(inode->i_sb, entry); + if (newino) { + shfs_new_dentry(newdent); + d_instantiate(newdent, newino); + if (!hashed) + d_rehash(newdent); + } + } else { + shfs_set_inode_attr(newdent->d_inode, entry); + } + + if (newdent->d_inode) { + ino = newdent->d_inode->i_ino; + newdent->d_fsdata = (void *) ctl.fpos; + shfs_new_dentry(newdent); + } + + if (ctl.idx >= SHFS_DIRCACHE_SIZE) { + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + } + ctl.cache = NULL; + ctl.idx -= SHFS_DIRCACHE_SIZE; + ctl.ofs += 1; + ctl.page = grab_cache_page(&inode->i_data, ctl.ofs); + if (ctl.page) + ctl.cache = kmap(ctl.page); + } + if (ctl.cache) { + ctl.cache->dentry[ctl.idx] = newdent; + valid = 1; + } + dput(newdent); + +end_advance: + if (!valid) + ctl.valid = 0; + if (!ctl.filled && (ctl.fpos == filp->f_pos)) { + if (!ino) + ino = find_inode_number(dentry, qname); + if (!ino) + ino = iunique(inode->i_sb, 2); + ctl.filled = filldir(dirent, qname->name, qname->len, + filp->f_pos, ino, DT_UNKNOWN); + if (!ctl.filled) + filp->f_pos += 1; + } + ctl.fpos += 1; + ctl.idx += 1; + *ctrl = ctl; + return (ctl.valid || !ctl.filled); +} diff -urN newtree/fs/shfs/dir.c newtree.2/fs/shfs/dir.c --- newtree/fs/shfs/dir.c 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/dir.c 2006-08-04 10:03:28.000000000 -0700 @@ -0,0 +1,515 @@ +/* + * dcache.c + * + * Directory & dentry operations, partialy inspired by smbfs/ncpfs. + */ + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include "shfs_debug.h" +#include "proc.h" + +static int +shfs_dir_open(struct inode *inode, struct file *filp) +{ + struct dentry *dentry = filp->f_dentry; + int result = 0; + + DEBUG("%s\n", dentry->d_name.name); + /* always open root dir (ioctl()) */ + if (IS_ROOT(dentry)) + return 0; + result = shfs_revalidate_inode(dentry); + if (result < 0) + VERBOSE("failed (%d)\n", result); + return result; +} + +/* + * Read a directory, using filldir to fill the dirent memory. + * info->fops.readdir does the actual reading from the shfs server. + * + * The cache code is almost directly taken from shfs/ncpfs + */ +static int +shfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct dentry *dentry = filp->f_dentry; + struct shfs_sb_info *info = info_from_dentry(dentry); + struct inode *dir = dentry->d_inode; + char name[SHFS_PATH_MAX]; + union shfs_dir_cache *cache = NULL; + struct shfs_cache_control ctl; + struct page *page = NULL; + int result; + + ctl.page = NULL; + ctl.cache = NULL; + + DEBUG("%s (%d)\n", dentry->d_name.name, (unsigned int)filp->f_pos); + + result = 0; + switch ((unsigned int) filp->f_pos) { + case 0: + DEBUG("f_pos=0\n"); + if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0) + goto out; + filp->f_pos = 1; + /* fallthrough */ + case 1: + DEBUG("f_pos=1\n"); + if (filldir(dirent, "..", 2, 1, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) + goto out; + filp->f_pos = 2; + } + + /* + * Make sure our inode is up-to-date. + */ + result = shfs_revalidate_inode(dentry); + if (result) + goto out; + + + page = grab_cache_page(&dir->i_data, 0); + if (!page) + goto read_really; + + ctl.cache = cache = kmap(page); + ctl.head = cache->head; + + if (!PageUptodate(page) || !ctl.head.eof) { + DEBUG("%s, page uptodate=%d, eof=%d\n", dentry->d_name.name, PageUptodate(page), ctl.head.eof); + goto init_cache; + } + + if (filp->f_pos == 2) { + if (jiffies - ctl.head.time >= SHFS_MAX_AGE(info)) + goto init_cache; + } + + if (filp->f_pos > ctl.head.end) + goto finished; + + ctl.fpos = filp->f_pos + (SHFS_DIRCACHE_START - 2); + ctl.ofs = ctl.fpos / SHFS_DIRCACHE_SIZE; + ctl.idx = ctl.fpos % SHFS_DIRCACHE_SIZE; + + for (;;) { + if (ctl.ofs != 0) { + ctl.page = find_lock_page(&dir->i_data, ctl.ofs); + if (!ctl.page) + goto invalid_cache; + ctl.cache = kmap(ctl.page); + if (!PageUptodate(ctl.page)) + goto invalid_cache; + } + while (ctl.idx < SHFS_DIRCACHE_SIZE) { + struct dentry *dent; + int res; + + dent = shfs_dget_fpos(ctl.cache->dentry[ctl.idx], + dentry, filp->f_pos); + if (!dent) + goto invalid_cache; + res = filldir(dirent, dent->d_name.name, + dent->d_name.len, filp->f_pos, + dent->d_inode->i_ino, DT_UNKNOWN); + dput(dent); + if (res) + goto finished; + filp->f_pos += 1; + ctl.idx += 1; + if (filp->f_pos > ctl.head.end) + goto finished; + } + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + ctl.page = NULL; + } + ctl.idx = 0; + ctl.ofs += 1; + } +invalid_cache: + if (ctl.page) { + kunmap(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + ctl.page = NULL; + } + ctl.cache = cache; +init_cache: + shfs_invalidate_dircache_entries(dentry); + ctl.head.time = jiffies; + ctl.head.eof = 0; + ctl.fpos = 2; + ctl.ofs = 0; + ctl.idx = SHFS_DIRCACHE_START; + ctl.filled = 0; + ctl.valid = 1; +read_really: + result = -ENAMETOOLONG; + if (get_name(dentry, name) < 0) + goto out; + result = info->fops.readdir(info, name, filp, dirent, filldir, &ctl); + if (ctl.idx == -1) + goto invalid_cache; /* retry */ + ctl.head.end = ctl.fpos - 1; + ctl.head.eof = ctl.valid; +finished: + if (page) { + cache->head = ctl.head; + kunmap(page); + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + } + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + } +out: + return result; +} + +/* + * shouldn't be called too often since we instantiate dentry + * in fill_cache() + */ +static struct dentry* +shfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *namei) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + char name[SHFS_PATH_MAX]; + struct shfs_fattr fattr; + struct inode *inode; + int result; + + DEBUG("%s\n", dentry->d_name.name); + if (get_name(dentry, name) < 0) + return ERR_PTR(-ENAMETOOLONG); + + result = info->fops.stat(info, name, &fattr); + if (result < 0) { + DEBUG("!%d\n", result); + dentry->d_op = &shfs_dentry_operations; + d_add(dentry, NULL); + shfs_renew_times(dentry); + if (result == -EINTR) + return ERR_PTR(result); + return NULL; + } + + fattr.f_ino = iunique(dentry->d_sb, 2); + inode = shfs_iget(dir->i_sb, &fattr); + if (inode) { + dentry->d_op = &shfs_dentry_operations; + d_add(dentry, inode); + shfs_renew_times(dentry); + } + + return NULL; +} + +static int +shfs_instantiate(struct dentry *dentry) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + struct shfs_fattr fattr; + struct inode *inode; + char name[SHFS_PATH_MAX]; + int result; + + DEBUG("%s\n", dentry->d_name.name); + if (get_name(dentry, name) < 0) + return -ENAMETOOLONG; + + result = info->fops.stat(info, name, &fattr); + if (result < 0) { + VERBOSE("!%d\n", result); + goto out; + } + + shfs_renew_times(dentry); + fattr.f_ino = iunique(dentry->d_sb, 2); + inode = shfs_iget(dentry->d_sb, &fattr); + result = -EACCES; + if (!inode) + goto out; + dentry->d_op = &shfs_dentry_operations; + d_instantiate(dentry, inode); + result = 0; +out: + return result; +} + +static int +shfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + char name[SHFS_PATH_MAX]; + int result; + + if (info->readonly) + return -EROFS; + if (get_name(dentry, name) < 0) + return -ENAMETOOLONG; + + result = info->fops.mkdir(info, name); + if (result < 0) + return result; + + shfs_invalid_dir_cache(dir); + return shfs_instantiate(dentry); +} + +static int +shfs_create(struct inode* dir, struct dentry *dentry, int mode, struct nameidata *namei) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + char name[SHFS_PATH_MAX]; + int result, forced_write = 0; + + if (info->readonly) + return -EROFS; + if (get_name(dentry, name) < 0) + return -ENAMETOOLONG; + + if (!(mode & S_IWUSR)) { + forced_write = 1; + mode |= S_IWUSR; + } + result = info->fops.create(info, name, mode); + if (result < 0) + return result; + + shfs_invalid_dir_cache(dir); + result = shfs_instantiate(dentry); + if (forced_write && dentry->d_inode && dentry->d_inode->u.generic_ip) + ((struct shfs_inode_info *)dentry->d_inode->u.generic_ip)->unset_write_on_close = 1; + return result; +} + +static int +shfs_rmdir(struct inode* dir, struct dentry *dentry) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + char name[SHFS_PATH_MAX]; + int result; + + if (info->readonly) + return -EROFS; + if (!d_unhashed(dentry)) + return -EBUSY; + if (get_name(dentry, name) < 0) + return -ENAMETOOLONG; + + result = info->fops.rmdir(info, name); + if (!result) + shfs_renew_times(dentry); + shfs_invalid_dir_cache(dir); + return result; +} + +static int +shfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) +{ + struct shfs_sb_info *info = info_from_dentry(old_dentry); + char old[SHFS_PATH_MAX], new[SHFS_PATH_MAX]; + int result; + + if (info->readonly) + return -EROFS; + + if (get_name(old_dentry, old) < 0) + return -ENAMETOOLONG; + if (get_name(new_dentry, new) < 0) + return -ENAMETOOLONG; + if (new_dentry->d_inode) + d_delete(new_dentry); + + result = info->fops.rename(info, old, new); + if (!result) { + shfs_renew_times(old_dentry); + shfs_renew_times(new_dentry); + } + shfs_invalid_dir_cache(old_dir); + shfs_invalid_dir_cache(new_dir); + return result; +} + +static int +shfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + char name[SHFS_PATH_MAX]; + int result; + + if (info->readonly) + return -EROFS; + if (get_name(dentry, name) < 0) + return -ENAMETOOLONG; + + result = info->fops.unlink(info, name); + if (!result) + shfs_renew_times(dentry); + shfs_invalid_dir_cache(dir); + return result; +} + +static int +shfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) +{ + struct shfs_sb_info *info = info_from_dentry(old_dentry); + char old[SHFS_PATH_MAX], new[SHFS_PATH_MAX]; + int result; + + if (info->readonly) + return -EROFS; + + if (get_name(old_dentry, old) < 0) + return -ENAMETOOLONG; + if (get_name(new_dentry, new) < 0) + return -ENAMETOOLONG; + + result = info->fops.link(info, old, new); + if (result < 0) + return result; + shfs_invalid_dir_cache(dir); + return shfs_instantiate(new_dentry); +} + +static int +shfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + char old[SHFS_PATH_MAX], new[SHFS_PATH_MAX]; + int result; + + if (info->readonly) + return -EROFS; + + if (get_name(dentry, new) < 0) + return -ENAMETOOLONG; + if (strlen(oldname) + 1 > SHFS_PATH_MAX) + return -ENAMETOOLONG; + strcpy(old, oldname); + + result = info->fops.symlink(info, old, new); + if (result < 0) + return result; + + shfs_invalid_dir_cache(dir); + return shfs_instantiate(dentry); +} + +/* + * Initialize a new dentry + */ +void +shfs_new_dentry(struct dentry *dentry) +{ + dentry->d_op = &shfs_dentry_operations; + dentry->d_time = jiffies; +} + +void +shfs_age_dentry(struct shfs_sb_info *info, struct dentry *dentry) +{ + dentry->d_time = jiffies - SHFS_MAX_AGE(info); +} + +/* + * Whenever a lookup succeeds, we know the parent directories + * are all valid, so we want to update the dentry timestamps. + * N.B. Move this to dcache? + */ +void +shfs_renew_times(struct dentry * dentry) +{ + for (;;) { + dentry->d_time = jiffies; + if (IS_ROOT(dentry)) + break; + dentry = dentry->d_parent; + } +} + +static int +shfs_d_revalidate(struct dentry *dentry, struct nameidata *namei) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + struct inode *inode = dentry->d_inode; + unsigned long age; + int result; + + DEBUG("%s\n", dentry->d_name.name); + age = jiffies - dentry->d_time; + + result = (age <= SHFS_MAX_AGE(info)); + DEBUG("valid: %d\n", result); + if (!inode) + return result; /* negative dentry */ + lock_kernel(); + if (is_bad_inode(inode)) + result = 0; + else if (!result) + result = (shfs_revalidate_inode(dentry) == 0); + unlock_kernel(); + return result; +} + +static int +shfs_d_delete(struct dentry *dentry) +{ + if (dentry->d_inode) { + if (is_bad_inode(dentry->d_inode)) { + DEBUG("bad inode, unhashing\n"); + return 1; + } + } else { + /* ??? */ + } + return 0; +} + +static struct dentry_operations shfs_dentry_operations = { + .d_revalidate = shfs_d_revalidate, + .d_delete = shfs_d_delete, +}; + +struct file_operations shfs_dir_operations = { + .read = generic_read_dir, + .readdir = shfs_readdir, + .ioctl = shfs_ioctl, + .open = shfs_dir_open, +}; + +struct inode_operations shfs_dir_inode_operations = { + .create = shfs_create, + .lookup = shfs_lookup, + .link = shfs_link, + .unlink = shfs_unlink, + .symlink = shfs_symlink, + .mkdir = shfs_mkdir, + .rmdir = shfs_rmdir, + .rename = shfs_rename, + .getattr = shfs_getattr, + .setattr = shfs_notify_change, +}; + diff -urN newtree/fs/shfs/fcache.c newtree.2/fs/shfs/fcache.c --- newtree/fs/shfs/fcache.c 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/fcache.c 2006-08-04 10:03:28.000000000 -0700 @@ -0,0 +1,393 @@ +/* + * fcache.c + * + * File cache: read/write operations are grouped together in chunks + * and done together. + */ + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include "shfs_debug.h" + +#define SHFS_FCACHE_READ 0 +#define SHFS_FCACHE_WRITE 1 + +struct shfs_file { + int type:2; /* why does gcc complain for 1-bit? */ + int new:1; + off_t offset; + unsigned long count; + char *data; +}; + +kmem_cache_t *file_cache = NULL; + +void +fcache_init(void) +{ + file_cache = kmem_cache_create("shfs_file", sizeof(struct shfs_file), 0, 0, NULL, NULL); + DEBUG("file_cache: %p\n", file_cache); +} + +void +fcache_finish(void) +{ + kmem_cache_destroy(file_cache); +} + +static struct shfs_file * +alloc_fcache(struct shfs_sb_info *info) +{ + struct shfs_file *cache; + + spin_lock(&info->fcache_lock); + if (info->fcache_free <= 0) { + spin_unlock(&info->fcache_lock); + return NULL; + } + info->fcache_free--; + spin_unlock(&info->fcache_lock); + + cache = (struct shfs_file *)KMEM_ALLOC("fcache", file_cache, GFP_KERNEL); + if (!cache) + return NULL; + cache->data = vmalloc(info->fcache_size); + if (!cache->data) { + KMEM_FREE("fcache", file_cache, cache); + return NULL; + } + cache->type = SHFS_FCACHE_READ; + cache->new = 1; + cache->offset = 0; + cache->count = 0; + + return cache; +} + +static void +free_fcache(struct shfs_sb_info *info, struct shfs_file *cache) +{ + DEBUG("release\n"); + vfree(cache->data); + KMEM_FREE("fcache", file_cache, cache); + + spin_lock(&info->fcache_lock); + info->fcache_free++; + spin_unlock(&info->fcache_lock); +} + +int +fcache_file_open(struct file *f) +{ + struct inode *inode; + struct shfs_inode_info *p; + + if (!f->f_dentry || !(inode = f->f_dentry->d_inode)) { + VERBOSE("invalid\n"); + return -EINVAL; + } + DEBUG("ino: %lu\n", inode->i_ino); + if (S_ISDIR(inode->i_mode)) { + VERBOSE("dir in file cache?\n"); + return -EINVAL; + } + p = (struct shfs_inode_info *)inode->u.generic_ip; + if (!p) { + VERBOSE("inode without info\n"); + return -EINVAL; + } + if (!p->cache) + p->cache = alloc_fcache(info_from_dentry(f->f_dentry)); + return 0; +} + +int +fcache_file_sync(struct file *f) +{ + struct inode *inode; + struct shfs_inode_info *p; + struct shfs_file *cache; + int result; + + if (!f->f_dentry || !(inode = f->f_dentry->d_inode)) { + VERBOSE("invalid\n"); + return -EINVAL; + } + DEBUG("ino: %lu\n", inode->i_ino); + if (S_ISDIR(inode->i_mode)) { + VERBOSE("dir in file cache?\n"); + return -EINVAL; + } + p = (struct shfs_inode_info *)inode->u.generic_ip; + if (!p) { + VERBOSE("inode without info\n"); + return -EINVAL; + } + if (!p->cache) + return 0; + + cache = p->cache; + result = 0; + if (cache->count && cache->type == SHFS_FCACHE_WRITE) { + char name[SHFS_PATH_MAX]; + struct shfs_sb_info *info = info_from_dentry(f->f_dentry); + + DEBUG("sync\n"); + if (get_name(f->f_dentry, name) < 0) + return -ENAMETOOLONG; + result = info->fops.write(info, name, cache->offset, cache->count, cache->data, inode->i_ino); + cache->type = SHFS_FCACHE_READ; + cache->count = 0; + } + return result < 0 ? result : 0; +} + +int +fcache_file_close(struct file *f) +{ + int result; + + result = fcache_file_sync(f); + if (result == 0) { + struct shfs_inode_info *p; + + p = (struct shfs_inode_info *)f->f_dentry->d_inode->u.generic_ip; + if (!p) { + VERBOSE("inode without info\n"); + return -EINVAL; + } + if (!p->cache) + return 0; + free_fcache(info_from_dentry(f->f_dentry), p->cache); + p->cache = NULL; + } + return result < 0 ? result : 0; +} + +int +fcache_file_clear(struct inode *inode) +{ + struct shfs_inode_info *p; + struct shfs_file *cache; + + if (!inode || S_ISDIR(inode->i_mode)) { + DEBUG("invalid\n"); + return -EINVAL; + } + DEBUG("ino: %lu\n", inode->i_ino); + p = (struct shfs_inode_info *)inode->u.generic_ip; + if (!p) { + VERBOSE("inode without info\n"); + return -EINVAL; + } + if (!p->cache) + return 0; + + cache = p->cache; + if (cache->count) { + if (cache->type == SHFS_FCACHE_WRITE) { + VERBOSE("Aieeee, out of sync\n"); + } else { + cache->count = 0; + } + } + return 0; +} + +int +fcache_file_read(struct file *f, unsigned offset, unsigned count, char *buffer) +{ + char name[SHFS_PATH_MAX]; + struct shfs_sb_info *info; + unsigned readahead, c, o, x, y, z, read = 0; + struct inode *inode; + struct shfs_inode_info *p; + struct shfs_file *cache; + int result = 0; + + DEBUG("[%u, %u]\n", offset, count); + if (!f->f_dentry || !(inode = f->f_dentry->d_inode)) { + VERBOSE("invalid\n"); + return -EINVAL; + } + if (get_name(f->f_dentry, name) < 0) + return -ENAMETOOLONG; + DEBUG("ino: %lu\n", inode->i_ino); + if (S_ISDIR(inode->i_mode)) { + VERBOSE("dir in file cache?\n"); + return -EINVAL; + } + p = (struct shfs_inode_info *)inode->u.generic_ip; + if (!p) { + VERBOSE("inode without info\n"); + return -EINVAL; + return result; + } + info = info_from_dentry(f->f_dentry); + if (!p->cache) { + p->cache = alloc_fcache(info); + if (!p->cache) + return info->fops.read(info, name, offset, count, buffer, 0); + } + + cache = p->cache; + /* hit? */ + if (offset >= cache->offset && offset < (cache->offset + cache->count)) { + o = offset - cache->offset; + c = count > cache->count - o ? cache->count - o : count; + memcpy(buffer, cache->data + o, c); + buffer += c; + offset += c; + count -= c; + read += c; + } + + if (count && cache->type == SHFS_FCACHE_WRITE) { + info->fops.write(info, name, cache->offset, cache->count, cache->data, inode->i_ino); + cache->type = SHFS_FCACHE_READ; + cache->offset = offset; + cache->count = 0; + } + + while (count) { + o = offset & PAGE_MASK; + x = offset - o; + if (cache->offset + cache->count == offset) + readahead = cache->count; + else { + cache->offset = o; + cache->count = 0; + readahead = 0; + } + if (readahead < (x + count)) + readahead = x + count; + readahead = (readahead+PAGE_SIZE-1) & PAGE_MASK; + if (readahead > info->fcache_size) + readahead = info->fcache_size; + if (o % readahead) { /* HD */ + y = o, z = readahead; + while (y && z) + if (y > z) y %= z; else z %= y; + readahead = y > z ? y : z; + } + if (cache->count + readahead > info->fcache_size) { + cache->offset = o; + cache->count = 0; + } + DEBUG("[%u, %u]\n", o, readahead); + result = info->fops.read(info, name, o, readahead, cache->data+cache->count, 0); + if (result < 0) { + cache->count = 0; + return result; + } + + c = result - x; + if (c > count) + c = count; + memcpy(buffer, cache->data + cache->count + x, c); + buffer += c; + offset += c; + count -= c; + read += c; + cache->count += result; + } + return read; +} + +int +fcache_file_write(struct file *f, unsigned offset, unsigned count, char *buffer) +{ + struct dentry *dentry = f->f_dentry; + char name[SHFS_PATH_MAX]; + struct shfs_sb_info *info; + unsigned o, c, wrote = 0; + struct inode *inode; + struct shfs_inode_info *p; + struct shfs_file *cache; + int result = 0; + + DEBUG("[%u, %u]\n", offset, count); + if (!(inode = dentry->d_inode)) { + VERBOSE("invalid\n"); + return -EINVAL; + } + if (get_name(dentry, name) < 0) + return -ENAMETOOLONG; + DEBUG("ino: %lu\n", inode->i_ino); + if (S_ISDIR(inode->i_mode)) { + VERBOSE("dir in file cache?\n"); + return -EINVAL; + } + p = (struct shfs_inode_info *)inode->u.generic_ip; + if (!p) { + VERBOSE("inode without info\n"); + return -EINVAL; + } + info = info_from_dentry(dentry); + if (!p->cache) { + p->cache = alloc_fcache(info); + if (!p->cache) + return info->fops.write(info, name, offset, count, buffer, inode->i_ino); + } + + cache = p->cache; + if (cache->type == SHFS_FCACHE_READ) { + cache->offset = offset; + cache->count = 0; + cache->type = SHFS_FCACHE_WRITE; + } + + DEBUG("1 [%lu, %lu]\n", cache->offset, cache->count); + if (offset >= cache->offset && offset < (cache->offset + cache->count)) { + o = offset - cache->offset; + c = count > info->fcache_size - o ? info->fcache_size - o : count; + memcpy(cache->data + o, buffer, c); + if (o + c > cache->count) + cache->count = o + c; + buffer += c; + offset += c; + count -= c; + wrote += c; + } + DEBUG("2 [%lu, %lu]\n", cache->offset, cache->count); + if (count && offset == cache->offset+cache->count) { + o = offset - cache->offset; + c = count > info->fcache_size - cache->count ? info->fcache_size - cache->count : count; + memcpy(cache->data + o, buffer, c); + cache->count += c; + buffer += c; + offset += c; + count -= c; + wrote += c; + } + DEBUG("3 [%lu, %lu]\n", cache->offset, cache->count); + while (count) { + result = info->fops.write(info, name, cache->offset, cache->count, cache->data, inode->i_ino); + if (result < 0) + break; + c = count > info->fcache_size ? info->fcache_size : count; + memcpy(cache->data, buffer, c); + cache->offset = offset; + cache->count = c; + buffer += c; + offset += c; + count -= c; + wrote += c; + } + DEBUG("4 [%lu, %lu]\n", cache->offset, cache->count); + if (cache->new && wrote) { + result = info->fops.write(info, name, cache->offset, 1, cache->data, inode->i_ino); + cache->new = 0; + } + + return result >= 0 ? wrote : result; +} diff -urN newtree/fs/shfs/file.c newtree.2/fs/shfs/file.c --- newtree/fs/shfs/file.c 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/file.c 2006-08-04 10:03:28.000000000 -0700 @@ -0,0 +1,357 @@ +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "shfs_debug.h" +#include "proc.h" + +static int +shfs_file_readpage(struct file *f, struct page *p) +{ + struct dentry *dentry = f->f_dentry; + struct shfs_sb_info *info = info_from_dentry(dentry); + char *buffer; + unsigned long offset, count; + int result; + + page_cache_get(p); + + buffer = kmap(p); + offset = p->index << PAGE_CACHE_SHIFT; + count = PAGE_SIZE; + + lock_kernel(); + do { + DEBUG("\n"); + if (info->fcache_size) { + result = fcache_file_read(f, offset, count, buffer); + } else { + char name[SHFS_PATH_MAX]; + if (get_name(f->f_dentry, name) < 0) { + unlock_kernel(); + result = -ENAMETOOLONG; + goto io_error; + } + result = info->fops.read(info, name, offset, count, buffer, 0); + } + if (result < 0) { + VERBOSE("!%d\n", result); + unlock_kernel(); + goto io_error; + } + count -= result; + offset += result; + buffer += result; + dentry->d_inode->i_atime = CURRENT_TIME; + ROUND_TO_MINS(dentry->d_inode->i_atime); + if (!result) + break; + } while (count); + + unlock_kernel(); + memset(buffer, 0, count); + flush_dcache_page(p); + SetPageUptodate(p); + result = 0; +io_error: + kunmap(p); + unlock_page(p); + page_cache_release(p); + return result; +} + +static int +shfs_file_writepage(struct page *p, struct writeback_control *wbc) +{ + return -EFAULT; +} + +static int +shfs_file_preparewrite(struct file *f, struct page *p, unsigned offset, unsigned to) +{ + DEBUG("[%p, %u, %u]\n", p, offset, to); + return 0; +} + +static int +shfs_file_commitwrite(struct file *f, struct page *p, unsigned offset, unsigned to) +{ + struct dentry *dentry = f->f_dentry; + struct shfs_sb_info *info = info_from_dentry(dentry); + struct inode *inode = p->mapping->host; + struct shfs_inode_info *i = (struct shfs_inode_info *)inode->u.generic_ip; + char *buffer = kmap(p) + offset; + int written = 0, result; + unsigned count = to - offset; + struct timespec time; + + DEBUG("[%p, %u, %u]\n", p, offset, to); + offset += p->index << PAGE_CACHE_SHIFT; + lock_kernel(); + if (info->readonly) { + result = -EROFS; + goto error; + } + + do { + if (info->fcache_size) { + result = fcache_file_write(f, offset, count, buffer); + } else { + char name[SHFS_PATH_MAX]; + if (get_name(dentry, name) < 0) { + result = -ENAMETOOLONG; + goto error; + } + result = info->fops.write(info, name, offset, count, buffer, dentry->d_inode->i_ino); + } + if (result < 0) { + VERBOSE("!%d\n", result); + goto error; + } + count -= result; + offset += result; + buffer += result; + written += result; + if (!result) + break; + + } while (count); + + memset(buffer, 0, count); + result = 0; +error: + unlock_kernel(); + kunmap(p); + + DEBUG("[%u, %lld]\n", offset, inode->i_size); + time = CURRENT_TIME; + ROUND_TO_MINS(time); + if (!timespec_equal(&time, &inode->i_mtime)) { + inode->i_atime = inode->i_mtime = time; + if (i) + i->oldmtime = 0; /* force inode reload */ + } + if (offset > inode->i_size) { + inode->i_size = offset; + if (i) + i->oldmtime = 0; /* force inode reload */ + } + return result; +} + +static int +shfs_file_permission(struct inode *inode, int mask, struct nameidata *namei) +{ + int mode = inode->i_mode; + + mode >>= 6; + if ((mode & 7 & mask) != mask) + return -EACCES; + return 0; +} + +static int +shfs_file_open(struct inode *inode, struct file *f) +{ + struct dentry *dentry = f->f_dentry; + struct shfs_sb_info *info = info_from_dentry(dentry); + int mode = f->f_flags & O_ACCMODE; + char name[SHFS_PATH_MAX]; + int result = 0; + + DEBUG("%s\n", dentry->d_name.name); + if ((mode == O_WRONLY || mode == O_RDWR) && info->readonly) + return -EROFS; + if (!get_name(dentry, name)) + return -ENAMETOOLONG; + + result = info->fops.open(info, name, mode); + + switch (result) { + case 1: /* install special read handler for this file */ + if (dentry->d_inode) + f->f_op = &shfs_slow_operations; + result = 0; + /* fallthrough */ + case 0: + if (info->fcache_size) + fcache_file_open(f); + break; + default: /* error */ + DEBUG("!%d\n", result); + break; + } + if (result >= 0) + shfs_renew_times(dentry); + + return result; +} + +static int +shfs_file_flush(struct file *f) +{ + struct dentry *dentry = f->f_dentry; + struct shfs_sb_info *info = info_from_dentry(dentry); + int result = 0; + + DEBUG("%s\n", dentry->d_name.name); + if (info->fcache_size) { + result = fcache_file_sync(f); + if (result < 0) + shfs_invalid_dir_cache(dentry->d_parent->d_inode); + if (!result) { + /* last reference */ + lock_kernel(); + filemap_fdatawrite(dentry->d_inode->i_mapping); + filemap_fdatawait(dentry->d_inode->i_mapping); + unlock_kernel(); + } + } + return result < 0 ? result : 0; +} + +static int +shfs_file_release(struct inode *inode, struct file *f) +{ + struct dentry *dentry = f->f_dentry; + struct shfs_sb_info *info = info_from_dentry(dentry); + int result; + + DEBUG("%s\n", dentry->d_name.name); + if (info->fcache_size) { + result = fcache_file_close(f); + if (result < 0) + shfs_invalid_dir_cache(dentry->d_parent->d_inode); + if (!result) { + /* last reference */ + lock_kernel(); + filemap_fdatawrite(dentry->d_inode->i_mapping); + filemap_fdatawait(dentry->d_inode->i_mapping); + unlock_kernel(); + } + } + /* if file was forced to be writeable, change attrs back on close */ + if (dentry->d_inode && dentry->d_inode->u.generic_ip) { + if (((struct shfs_inode_info *)dentry->d_inode->u.generic_ip)->unset_write_on_close) { + char name[SHFS_PATH_MAX]; + + if (get_name(dentry, name) < 0) + goto out; + result = info->fops.chmod(info, name, dentry->d_inode->i_mode & ~S_IWUSR); + if (result < 0) + goto out; + inode->i_mode &= ~S_IWUSR; + } + } +out: + return 0; /* ignored */ +} + +static int +shfs_file_sync(struct file *f, struct dentry *dentry, int datasync) +{ + return 0; +} + +static ssize_t +shfs_slow_read(struct file *f, char *buf, size_t count, loff_t *ppos) +{ + struct dentry *dentry = f->f_dentry; + struct shfs_sb_info *info = info_from_dentry(dentry); + char name[SHFS_PATH_MAX]; + unsigned long page; + int result; + + DEBUG("%s\n", dentry->d_name.name); + + lock_kernel(); + page = __get_free_page(GFP_KERNEL); + if (!page) { + result = -ENOMEM; + goto out; + } + if (get_name(dentry, name) < 0) { + result = -ENAMETOOLONG; + goto error; + } + count = count > PAGE_SIZE ? PAGE_SIZE : count; + result = info->fops.read(info, name, *ppos, count, (char *)page, dentry->d_inode->i_ino); + if (result < 0) { + VERBOSE("!%d\n", result); + goto error; + } + if (result != 0) { + copy_to_user(buf, (char *)page, result); + *ppos += result; + } +error: + free_page(page); +out: + unlock_kernel(); + return result; +} + +static ssize_t +shfs_slow_write(struct file *f, const char *buf, size_t count, loff_t *offset) +{ + int written = 0; + int result; + + DEBUG("\n"); + written = generic_file_write(f, buf, count, offset); + if (written > 0) { + result = shfs_file_flush(f); + written = result < 0 ? result: written; + } + + return written; +} + +struct file_operations shfs_file_operations = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .write = generic_file_write, + .ioctl = shfs_ioctl, + .mmap = generic_file_mmap, + .open = shfs_file_open, + .flush = shfs_file_flush, + .release = shfs_file_release, + .fsync = shfs_file_sync, +}; + +struct file_operations shfs_slow_operations = { + .llseek = generic_file_llseek, + .read = shfs_slow_read, + .write = shfs_slow_write, + .ioctl = shfs_ioctl, + .mmap = generic_file_mmap, + .open = shfs_file_open, + .flush = shfs_file_flush, + .release = shfs_file_release, + .fsync = shfs_file_sync, +}; + +struct inode_operations shfs_file_inode_operations = { + .permission = shfs_file_permission, + .getattr = shfs_getattr, + .setattr = shfs_notify_change, +}; + +struct address_space_operations shfs_file_aops = { + .readpage = shfs_file_readpage, + .writepage = shfs_file_writepage, + .prepare_write = shfs_file_preparewrite, + .commit_write = shfs_file_commitwrite, +}; + diff -urN newtree/fs/shfs/inode.c newtree.2/fs/shfs/inode.c --- newtree/fs/shfs/inode.c 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/inode.c 2006-08-04 10:03:28.000000000 -0700 @@ -0,0 +1,385 @@ +/* + * inode.c + * + * Inode/superblock operations, partialy inspired by smbfs/ncpfs. + */ + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "shfs_debug.h" +#include "proc.h" + +MODULE_AUTHOR("Zemljanka core team"); +MODULE_DESCRIPTION("SHell File System"); + +int debug_level; +#ifdef ENABLE_DEBUG + unsigned long alloc; +#endif + +kmem_cache_t *inode_cache = NULL; + +void +shfs_set_inode_attr(struct inode *inode, struct shfs_fattr *fattr) +{ + struct shfs_sb_info *info = info_from_inode(inode); + struct shfs_inode_info *i = inode->u.generic_ip; + struct timespec last_time = inode->i_mtime; + loff_t last_size = inode->i_size; + + inode->i_mode = fattr->f_mode; + inode->i_nlink = fattr->f_nlink; + if (info->preserve_own) { + inode->i_uid = fattr->f_uid; + inode->i_gid = fattr->f_gid; + } else { + inode->i_uid = info->uid; + inode->i_gid = info->gid; + } + inode->i_rdev = fattr->f_rdev; + inode->i_ctime = fattr->f_ctime; + inode->i_atime = fattr->f_atime; + inode->i_mtime = fattr->f_mtime; + inode->i_blksize= fattr->f_blksize; + inode->i_blocks = fattr->f_blocks; + inode->i_size = fattr->f_size; + + i->oldmtime = jiffies; + + if (!timespec_equal(&inode->i_mtime, &last_time) || inode->i_size != last_size) { + DEBUG("inode changed (%ld/%ld, %lu/%lu)\n", inode->i_mtime.tv_sec, last_time.tv_sec, (unsigned long)inode->i_size, (unsigned long)last_size); + invalidate_inode_pages(inode->i_mapping); + fcache_file_clear(inode); + } +} + +struct inode* +shfs_iget(struct super_block *sb, struct shfs_fattr *fattr) +{ + struct inode *inode; + struct shfs_inode_info *i; + + inode = new_inode(sb); + if (!inode) + return NULL; + inode->i_ino = fattr->f_ino; + i = inode->u.generic_ip = (struct shfs_inode_info *)KMEM_ALLOC("inode", inode_cache, GFP_KERNEL); + if (!i) + return NULL; + i->cache = NULL; + i->unset_write_on_close = 0; + shfs_set_inode_attr(inode, fattr); + + DEBUG("ino: %lu\n", inode->i_ino); + if (S_ISDIR(inode->i_mode)) { + DEBUG("dir\n"); + inode->i_op = &shfs_dir_inode_operations; + inode->i_fop = &shfs_dir_operations; + } else if (S_ISLNK(inode->i_mode)) { + DEBUG("link\n"); + inode->i_op = &shfs_symlink_inode_operations; + } else if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { + DEBUG("file/block/char\n"); + inode->i_op = &shfs_file_inode_operations; + inode->i_fop = &shfs_file_operations; + inode->i_data.a_ops = &shfs_file_aops; + } + + insert_inode_hash(inode); + return inode; +} + +static void +shfs_delete_inode(struct inode *inode) +{ + struct shfs_inode_info *i; + + DEBUG("ino: %lu\n", inode->i_ino); + i = (struct shfs_inode_info *)inode->u.generic_ip; + if (!i) { + VERBOSE("invalid inode\n"); + goto out; + } + if (i->cache) { + VERBOSE("file cache not free!\n"); + /* TODO: free it now? */ + } + KMEM_FREE("inode", inode_cache, i); +out: + clear_inode(inode); +} + +/* borrowed from smbfs */ +static int +shfs_refresh_inode(struct dentry *dentry) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + struct inode *inode = dentry->d_inode; + struct shfs_fattr fattr; + char name[SHFS_PATH_MAX]; + int result; + + if (inode->i_ino == 2) { + shfs_renew_times(dentry); + return 0; + } + if (get_name(dentry, name) < 0) + return -ENAMETOOLONG; + + result = info->fops.stat(info, name, &fattr); + if (result < 0) + goto out; + + shfs_renew_times(dentry); + if (S_ISLNK(inode->i_mode)) + goto out; + if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) { + shfs_set_inode_attr(inode, &fattr); + } else { + /* big touble */ + fattr.f_mode = inode->i_mode; /* save mode */ + make_bad_inode(inode); + inode->i_mode = fattr.f_mode; /* restore mode */ + /* + * No need to worry about unhashing the dentry: the + * lookup validation will see that the inode is bad. + * But we do want to invalidate the caches ... + */ + if (!S_ISDIR(inode->i_mode)) + invalidate_inode_pages(inode->i_mapping); + else + shfs_invalid_dir_cache(inode); + result = -EIO; + } +out: + return result; +} + +int +shfs_revalidate_inode(struct dentry *dentry) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + struct inode *inode = dentry->d_inode; + struct shfs_inode_info *i = (struct shfs_inode_info *)inode->u.generic_ip; + int result; + + DEBUG("%s\n", dentry->d_name.name); + result = 0; + + lock_kernel(); + if (is_bad_inode(inode)) + goto out; + if (inode->i_sb->s_magic != SHFS_SUPER_MAGIC) + goto out; + if (time_before(jiffies, i->oldmtime + SHFS_MAX_AGE(info))) + goto out; + + result = shfs_refresh_inode(dentry); +out: + unlock_kernel(); + DEBUG("%d\n", result); + return result; +} + +int +shfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + int result = shfs_revalidate_inode(dentry); + if (!result) + generic_fillattr(dentry->d_inode, stat); + return result; +} + +static void +shfs_put_super(struct super_block *sb) +{ + struct shfs_sb_info *info = info_from_sb(sb); + int result; + + result = info->fops.finish(info); + if (info->sock) + fput(info->sock); + if (!sock_lock(info)) { + VERBOSE("Cannot free caches!\n"); + return; + } + kfree(info->sockbuf); + kfree(info->readlnbuf); + kfree(info); + DEBUG("Super block discarded!\n"); +} + +struct super_operations shfs_sops = { + .drop_inode = generic_delete_inode, + .delete_inode = shfs_delete_inode, + .put_super = shfs_put_super, + .statfs = shfs_statfs, +}; + +static void +init_root_dirent(struct shfs_sb_info *server, struct shfs_fattr *fattr) +{ + memset(fattr, 0, sizeof(*fattr)); + fattr->f_nlink = 1; + fattr->f_uid = server->uid; + fattr->f_gid = server->gid; + fattr->f_blksize = 512; + + fattr->f_ino = 2; + fattr->f_mtime = CURRENT_TIME; + fattr->f_mode = server->root_mode; + fattr->f_size = 512; + fattr->f_blocks = 0; +} + +static int +shfs_read_super(struct super_block *sb, void *opts, int silent) +{ + struct shfs_sb_info *info; + struct shfs_fattr root; + struct inode *root_inode; + int result; + + info = (struct shfs_sb_info*)kmalloc(sizeof(struct shfs_sb_info), GFP_KERNEL); + if (!info) { + printk(KERN_NOTICE "Not enough kmem!\n"); + goto out; + } + + memset(info, 0, sizeof(struct shfs_sb_info)); + + sb->s_fs_info = info; + sb->s_blocksize = 4096; + sb->s_blocksize_bits = 12; + sb->s_magic = SHFS_SUPER_MAGIC; + sb->s_op = &shfs_sops; + sb->s_flags = 0; + + /* fill-in default values */ + info->fops = shell_fops; + info->version = 0; + info->ttl = SHFS_DEFAULT_TTL; + info->uid = current->uid; + info->gid = current->gid; + info->root_mode = (S_IRUSR | S_IWUSR | S_IXUSR | S_IFDIR); + info->fmask = 00177777; + info->mount_point[0] = 0; + init_MUTEX(&info->sock_sem); + info->sock = NULL; + info->sockbuf = (char *)kmalloc(SOCKBUF_SIZE, GFP_KERNEL); + if (!info->sockbuf) { + printk(KERN_NOTICE "Not enough kmem!\n"); + goto out_no_mem; + } + info->readlnbuf_len = 0; + info->readlnbuf = (char *)kmalloc(READLNBUF_SIZE, GFP_KERNEL); + if (!info->readlnbuf) { + printk(KERN_NOTICE "Not enough kmem!\n"); + kfree(info->sockbuf); + goto out_no_mem; + } + spin_lock_init(&info->fcache_lock); + info->fcache_free = SHFS_FCACHE_MAX; + info->fcache_size = SHFS_FCACHE_PAGES * PAGE_SIZE; + info->garbage_read = 0; + info->garbage_write = 0; + info->garbage = 0; + info->readonly = 0; + info->preserve_own = 0; + info->stable_symlinks = 0; + + debug_level = 0; + result = parse_options(info, (char *)opts); + if (result < 0) + goto out_no_opts; + if (!info->sock) { + VERBOSE("Socket not specified\n"); + goto out_no_opts; + } + if (info->version != PROTO_VERSION) { + printk(KERN_NOTICE "shfs: version mismatch (module: %d, mount: %d)\n", PROTO_VERSION, info->version); + goto out_no_opts; + } + + init_root_dirent(info, &root); + root_inode = shfs_iget(sb, &root); + if (!root_inode) + goto out_no_root; + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) + goto out_no_root; + shfs_new_dentry(sb->s_root); + + DEBUG("ok\n"); + return 0; + +out_no_root: + iput(root_inode); +out_no_opts: + kfree(info->sockbuf); + kfree(info->readlnbuf); +out_no_mem: + kfree(info); +out: + DEBUG("failed\n"); + return -EINVAL; +} + +static struct super_block * +shfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_nodev(fs_type, flags, data, shfs_read_super); +} + +static struct file_system_type sh_fs_type = { + .owner = THIS_MODULE, + .name = "shfs", + .get_sb = shfs_get_sb, + .kill_sb = kill_anon_super, +}; + +static int __init +init_shfs(void) +{ + printk(KERN_NOTICE "SHell File System, (c) 2002-2004 Miroslav Spousta\n"); + fcache_init(); + inode_cache = kmem_cache_create("shfs_inode", sizeof(struct shfs_inode_info), 0, 0, NULL, NULL); + + debug_level = 0; +#ifdef ENABLE_DEBUG + alloc = 0; +#endif + return register_filesystem(&sh_fs_type); +} + +static void __exit +exit_shfs(void) +{ + VERBOSE("Unregistering shfs.\n"); +#ifdef ENABLE_DEBUG + if (alloc) + VERBOSE("Memory leak (%lu)!\n", alloc); +#endif + unregister_filesystem(&sh_fs_type); + kmem_cache_destroy(inode_cache); + fcache_finish(); +} + +module_init(init_shfs); +module_exit(exit_shfs); + +MODULE_LICENSE("GPL"); + diff -urN newtree/fs/shfs/ioctl.c newtree.2/fs/shfs/ioctl.c --- newtree/fs/shfs/ioctl.c 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/ioctl.c 2006-08-04 10:03:28.000000000 -0700 @@ -0,0 +1,49 @@ +/* + * ioctl.c + * + * ioctl() call for persistent connection support. + */ + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "shfs_debug.h" + +int +shfs_ioctl(struct inode *inode, struct file *f, + unsigned int cmd, unsigned long arg) +{ + struct shfs_sb_info *info = info_from_inode(inode); + int result = -EINVAL; + + switch (cmd) { + case SHFS_IOC_NEWCONN: + VERBOSE("Reconnect (%ld)\n", arg); + if (info->sock) + fput(info->sock); + info->sock = fget(arg); + info->garbage = 0; + info->garbage_read = 0; + info->garbage_write = 0; + VERBOSE(">%p\n", info->sock); + result = 0; + break; + default: + break; + } + + return result; +} diff -urN newtree/fs/shfs/proc.c newtree.2/fs/shfs/proc.c --- newtree/fs/shfs/proc.c 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/proc.c 2006-08-04 10:03:28.000000000 -0700 @@ -0,0 +1,581 @@ +/* + * proc.c + * + * Miscellaneous routines, socket read/write. + */ + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "shfs_debug.h" +#include "proc.h" + +/* our life would be too simple without RH */ +#ifdef INIT_SIGHAND +#define SIGLOCK(flags) do { spin_lock_irqsave(¤t->sighand->siglock, (flags)); } while (0) +#define SIGRECALC do { recalc_sigpending(); } while (0) +#define SIGUNLOCK(flags) do { spin_unlock_irqrestore(¤t->sighand->siglock, (flags)); } while (0) +#else +#define SIGLOCK(flags) do { spin_lock_irqsave(¤t->sigmask_lock, (flags)); } while (0) +#define SIGRECALC do { recalc_sigpending(current); } while (0) +#define SIGUNLOCK(flags) do { spin_unlock_irqrestore(¤t->sigmask_lock, (flags)); } while (0) +#endif + +static int +parse_mode(char *mode) +{ + return ((mode[0]-'0')<<6) + ((mode[1]-'0')<<3) + (mode[2]-'0'); +} + +int +parse_options(struct shfs_sb_info *info, char *opts) +{ + char *p, *q; + int i, j; + + if (!opts) + return -1; + + while ((p = strsep(&opts, ","))) { + if (strncmp(p, "mnt=", 4) == 0) { + if (strlen(p+4) + 1 > SHFS_PATH_MAX) + goto ugly_opts; + strcpy(info->mount_point, p+4); + } else if (strncmp(p, "ro", 2) == 0) { + info->readonly = 1; + } else if (strncmp(p, "rw", 2) == 0) { + info->readonly = 0; + } else if (strncmp(p, "suid", 4) == 0) { + info->fmask |= S_ISUID|S_ISGID; + } else if (strncmp(p, "nosuid", 6) == 0) { + info->fmask &= ~(S_ISUID|S_ISGID); + } else if (strncmp(p, "dev", 3) == 0) { + info->fmask |= S_IFCHR|S_IFBLK; + } else if (strncmp(p, "nodev", 5) == 0) { + info->fmask &= ~(S_IFCHR|S_IFBLK); + } else if (strncmp(p, "exec", 4) == 0) { + info->fmask |= S_IXUSR|S_IXGRP|S_IXOTH; + } else if (strncmp(p, "noexec", 6) == 0) { + info->fmask &= ~(S_IXUSR|S_IXGRP|S_IXOTH); + } else if (strncmp(p, "preserve", 8) == 0) { + info->preserve_own = 1; + } else if (strncmp(p, "cachesize=", 10) == 0) { + if (strlen(p+10) > 5) + goto ugly_opts; + q = p+10; + i = simple_strtoul(q, &q, 10); + i--; + for (j = 0; i; j++) + i >>= 1; + info->fcache_size = (1 << j) * PAGE_SIZE; + } else if (strncmp(p, "cachemax=", 9) == 0) { + if (strlen(p+9) > 5) + goto ugly_opts; + q = p+9; + i = simple_strtoul(q, &q, 10); + info->fcache_free = i; + } else if (strncmp(p, "debug=", 6) == 0) { + if (strlen(p+6) > 5) + goto ugly_opts; + q = p+6; + i = simple_strtoul(q, &q, 10); + debug_level = i; + } else if (strncmp(p, "ttl=", 4) == 0) { + if (strlen(p+4) > 10) + goto ugly_opts; + q = p+4; + i = simple_strtoul(q, &q, 10); + info->ttl = i * 1000; + } else if (strncmp(p, "uid=", 4) == 0) { + if (strlen(p+4) > 10) + goto ugly_opts; + q = p+4; + i = simple_strtoul(q, &q, 10); + info->uid = i; + } else if (strncmp(p, "gid=", 4) == 0) { + if (strlen(p+4) > 10) + goto ugly_opts; + q = p+4; + i = simple_strtoul(q, &q, 10); + info->gid = i; + } else if (strncmp(p, "rmode=", 6) == 0) { + if (strlen(p+6) > 3) + goto ugly_opts; + info->root_mode = S_IFDIR | (parse_mode(p+6) & (S_IRWXU | S_IRWXG | S_IRWXO)); + } else if (strncmp(p, "fd=", 3) == 0) { + if (strlen(p+3) > 5) + goto ugly_opts; + q = p+3; + i = simple_strtoul(q, &q, 10); + info->sock = fget(i); + } else if (strncmp(p, "version=", 8) == 0) { + if (strlen(p+8) > 5) + goto ugly_opts; + q = p+8; + i = simple_strtoul(q, &q, 10); + info->version = i; + } + } + return 0; + +ugly_opts: + VERBOSE("Invalid option: %s\n", p); + return -1; +} + +static int clear_garbage(struct shfs_sb_info *); + +/* sock_lock held */ +int +sock_write(struct shfs_sb_info *info, const void *buffer, int count) +{ + struct file *f = info->sock; + mm_segment_t fs; + int c, result = 0; + unsigned long flags, sigpipe; + sigset_t old_set; + + if (!f) + return -EIO; + if (info->garbage) { + result = clear_garbage(info); + if (result < 0) + return result; + } + + c = count; + + fs = get_fs(); + set_fs(get_ds()); + + SIGLOCK(flags); + sigpipe = sigismember(¤t->pending.signal, SIGPIPE); + old_set = current->blocked; + siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); + SIGRECALC; + SIGUNLOCK(flags); + + do { + struct iovec vec[1]; + + vec[0].iov_base = (void *)buffer; + vec[0].iov_len = c; + result = f->f_op->writev(f, (const struct iovec *) &vec, 1, &f->f_pos); + if (result < 0) { + DEBUG("error: %d\n", result); + if (result == -EAGAIN) + continue; + fput(f); + info->sock = NULL; + break; + } + buffer += result; + c -= result; + } while (c > 0); + + SIGLOCK(flags); + if (result == -EPIPE && !sigpipe) { + sigdelset(¤t->pending.signal, SIGPIPE); + result = -EIO; + } + current->blocked = old_set; + SIGRECALC; + SIGUNLOCK(flags); + + set_fs(fs); + + DEBUG(">%d\n", result); + if (result < 0) + set_garbage(info, 1, c); + else + result = count; + return result; +} + +#define BUFFER info->readlnbuf +#define LEN info->readlnbuf_len + +/* sock_lock held */ +int +sock_read(struct shfs_sb_info *info, void *buffer, int count) +{ + struct file *f = info->sock; + mm_segment_t fs; + int c, result = 0; + unsigned long flags, sigpipe; + sigset_t old_set; + + if (!f) + return -EIO; + if (info->garbage) { + result = clear_garbage(info); + if (result < 0) + return result; + } + c = count; + if (LEN > 0) { + if (count > LEN) + c = LEN; + memcpy(buffer, BUFFER, c); + buffer += c; + LEN -= c; + if (LEN > 0) + memmove(BUFFER, BUFFER+c, LEN); + c = count - c; + } + + /* don't block reading 0 bytes */ + if (c == 0) + return count; + + SIGLOCK(flags); + sigpipe = sigismember(¤t->pending.signal, SIGPIPE); + old_set = current->blocked; + siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); + SIGRECALC; + SIGUNLOCK(flags); + + fs = get_fs(); + set_fs(get_ds()); + + do { + struct iovec vec[1]; + + vec[0].iov_base = buffer; + vec[0].iov_len = c; + result = f->f_op->readv(f, (const struct iovec *)&vec, 1, &f->f_pos); + if (!result) { + /* peer has closed socket */ + result = -EIO; + } + if (result < 0) { + DEBUG("error: %d\n", result); + if (result == -EAGAIN) + continue; + fput(f); + info->sock = NULL; + break; + } + buffer += result; + c -= result; + } while (c > 0); + + SIGLOCK(flags); + if (result == -EPIPE && !sigpipe) { + sigdelset(¤t->pending.signal, SIGPIPE); + result = -EIO; + } + current->blocked = old_set; + SIGRECALC; + SIGUNLOCK(flags); + + set_fs(fs); + + DEBUG("<%d\n", result); + if (result < 0) + set_garbage(info, 0, c); + else + result = count; + return result; +} + +/* sock_lock held */ +int +sock_readln(struct shfs_sb_info *info, char *buffer, int count) +{ + struct file *f = info->sock; + mm_segment_t fs; + int c, l = 0, result; + char *nl; + unsigned long flags, sigpipe; + sigset_t old_set; + + if (!f) + return -EIO; + if (info->garbage) { + result = clear_garbage(info); + if (result < 0) + return result; + } + while (1) { + struct iovec vec[1]; + + nl = memchr(BUFFER, '\n', LEN); + if (nl) { + *nl = '\0'; + strncpy(buffer, BUFFER, count-1); + buffer[count-1] = '\0'; + c = LEN-(nl-BUFFER+1); + if (c > 0) + memmove(BUFFER, nl+1, c); + LEN = c; + + DEBUG("<%s\n", buffer); + return strlen(buffer); + } + DEBUG("miss(%d)\n", LEN); + c = READLNBUF_SIZE - LEN; + if (c == 0) { + BUFFER[READLNBUF_SIZE-1] = '\n'; + continue; + } + + SIGLOCK(flags); + sigpipe = sigismember(¤t->pending.signal, SIGPIPE); + old_set = current->blocked; + siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); + SIGRECALC; + SIGUNLOCK(flags); + + fs = get_fs(); + set_fs(get_ds()); + + vec[0].iov_base = BUFFER+LEN; + vec[0].iov_len = c; + result = f->f_op->readv(f, (const struct iovec *)&vec, 1, &f->f_pos); + SIGLOCK(flags); + if (result == -EPIPE && !sigpipe) { + sigdelset(¤t->pending.signal, SIGPIPE); + result = -EIO; + } + current->blocked = old_set; + SIGRECALC; + SIGUNLOCK(flags); + + set_fs(fs); + + DEBUG("<%d\n", result); + if (!result) { + /* peer has closed socket */ + result = -EIO; + } + if (result < 0) { + DEBUG("error: %d\n", result); + if (result == -EAGAIN) + continue; + fput(f); + info->sock = NULL; + set_garbage(info, 0, c); + return result; + } + LEN += result; + + /* do not lock while reading 0 bytes */ + if (l++ > READLNBUF_SIZE && LEN == 0) + return -EINVAL; + } +} + +int +reply(char *s) +{ + if (strncmp(s, "### ", 4)) + return 0; + return simple_strtoul(s+4, NULL, 10); +} + +static int +clear_garbage(struct shfs_sb_info *info) +{ + static unsigned long seq = 12345; + char buffer[256]; + int i, c, state, garbage; + int result; + + garbage = info->garbage_write; + if (garbage) + memset(buffer, ' ', sizeof(buffer)); + DEBUG(">%d\n", garbage); + while (garbage > 0) { + c = garbage < sizeof(buffer) ? garbage : sizeof(buffer); + info->garbage = 0; + result = sock_write(info, buffer, c); + if (result < 0) { + info->garbage_write = garbage; + goto error; + } + garbage -= result; + } + info->garbage_write = 0; + garbage = info->garbage_read; + DEBUG("<%d\n", garbage); + while (garbage > 0) { + c = garbage < sizeof(buffer) ? garbage : sizeof(buffer); + info->garbage = 0; + result = sock_read(info, buffer, c); + if (result < 0) { + info->garbage_read = garbage; + goto error; + } + garbage -= result; + } + info->garbage_read = 0; + + info->garbage = 0; + sprintf(buffer, "\n\ns_ping %lu", seq); + result = sock_write(info, (void *)buffer, strlen(buffer)); + if (result < 0) + goto error; + DEBUG("reading..\n"); + state = 0; + for (i = 0; i < 100000; i++) { + info->garbage = 0; + result = sock_readln(info, buffer, sizeof(buffer)); + if (result < 0) + goto error; + if (((state == 0) || (state == 1)) && reply(buffer) == REP_PRELIM) { + state = 1; + } else if (state == 1 && simple_strtoul(buffer, NULL, 10) == (seq-1)) { + state = 2; + } else if (state == 2 && reply(buffer) == REP_NOP) { + DEBUG("cleared\n"); + return 0; + } else { + state = 0; + } + } +error: + info->garbage = 1; + DEBUG("failed\n"); + return result; +} + +void +set_garbage(struct shfs_sb_info *info, int write, int count) +{ + info->garbage = 1; + if (write) + info->garbage_write = count; + else + info->garbage_read = count; +} + +int +get_name(struct dentry *d, char *name) +{ + int len = 0; + struct dentry *p; + + for (p = d; !IS_ROOT(p); p = p->d_parent) + len += p->d_name.len + 1; + + if (len + 1 > SHFS_PATH_MAX) + return 0; + if (len == 0) { + name[0] = '/'; + name[1] = '\0'; + return 1; + } + + name[len] = '\0'; + for (p = d; !IS_ROOT(p); p = p->d_parent) { + len -= p->d_name.len; + strncpy(&(name[len]), p->d_name.name, p->d_name.len); + len--; + name[len] = '/'; + } + + return 1; +} + +int +shfs_notify_change(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + struct shfs_sb_info *info = info_from_inode(inode); + char file[SHFS_PATH_MAX]; + int result; + + DEBUG("\n"); + if (get_name(dentry, file) < 0) + return -ENAMETOOLONG; + result = inode_change_ok(inode, attr); + if (result < 0) + return result; + if (info->readonly) + return -EROFS; + + if (attr->ia_valid & ATTR_MODE) { + result = info->fops.chmod(info, file, attr->ia_mode); + if (result < 0) + goto error; + inode->i_mode = attr->ia_mode; + } + if (attr->ia_valid & ATTR_UID) { + result = info->fops.chown(info, file, attr->ia_uid); + if (result < 0) + goto error; + inode->i_uid = info->preserve_own ? info->uid : attr->ia_uid; + } + if (attr->ia_valid & ATTR_GID) { + result = info->fops.chgrp(info, file, attr->ia_gid); + if (result < 0) + goto error; + inode->i_gid = info->preserve_own ? info->gid : attr->ia_gid; + } + if (attr->ia_valid & ATTR_SIZE) { + filemap_fdatawrite(inode->i_mapping); + filemap_fdatawait(inode->i_mapping); + result = info->fops.trunc(info, file, attr->ia_size); + if (result < 0) + goto error; + result = vmtruncate(inode, attr->ia_size); + if (result != 0) + goto error; + inode->i_size = attr->ia_size; + mark_inode_dirty(inode); + } + /* optimisation: call settime once when setting both atime and mtime */ + if (attr->ia_valid & ATTR_ATIME && attr->ia_valid & ATTR_MTIME + && attr->ia_atime.tv_sec == attr->ia_mtime.tv_sec + && attr->ia_atime.tv_nsec == attr->ia_mtime.tv_nsec) { + result = info->fops.settime(info, file, 1, 1, &attr->ia_atime); + if (result < 0) + goto error; + inode->i_atime = attr->ia_atime; + inode->i_mtime = attr->ia_mtime; + } else { + if (attr->ia_valid & ATTR_ATIME) { + result = info->fops.settime(info, file, 1, 0, &attr->ia_atime); + if (result < 0) + goto error; + inode->i_atime = attr->ia_atime; + } + if (attr->ia_valid & ATTR_MTIME) { + result = info->fops.settime(info, file, 0, 1, &attr->ia_mtime); + if (result < 0) + goto error; + inode->i_mtime = attr->ia_mtime; + } + } +error: + return result; +} + +int +shfs_statfs(struct super_block *sb, struct kstatfs *attr) +{ + struct shfs_sb_info *info = info_from_sb(sb); + + DEBUG("\n"); + return info->fops.statfs(info, attr); +} + diff -urN newtree/fs/shfs/proc.h newtree.2/fs/shfs/proc.h --- newtree/fs/shfs/proc.h 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/proc.h 2006-08-04 10:03:29.000000000 -0700 @@ -0,0 +1,23 @@ +#ifndef _PROC_H +#define _PROC_H + +#include + +static inline int +sock_lock(struct shfs_sb_info *info) +{ + int result; + DEBUG("?\n"); + result = down_interruptible(&(info->sock_sem)); + DEBUG("!\n"); + return (result != -EINTR); +} + +static inline void +sock_unlock(struct shfs_sb_info *info) +{ + up(&(info->sock_sem)); + DEBUG("\n"); +} + +#endif /* _PROC_H */ diff -urN newtree/fs/shfs/shell.c newtree.2/fs/shfs/shell.c --- newtree/fs/shfs/shell.c 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/shell.c 2006-08-04 10:03:29.000000000 -0700 @@ -0,0 +1,1027 @@ +/* + * shell.c + * + * "Shell" client implementation. + */ + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "shfs_debug.h" +#include "proc.h" + +/* directory list fields (ls -lan) */ +#define DIR_COLS 9 +#define DIR_PERM 0 +#define DIR_NLINK 1 +#define DIR_UID 2 +#define DIR_GID 3 +#define DIR_SIZE 4 +#define DIR_MONTH 5 +#define DIR_DAY 6 +#define DIR_YEAR 7 +#define DIR_NAME 8 + +/* aaa'aaa -> aaa'\''aaa */ +static int +replace_quote(char *name) +{ + char *s; + char *r = name; + int q = 0; + + while (*r) { + if (*r == '\'') + q++; + r++; + } + s = r+(3*q); + if ((s-name+1) > SHFS_PATH_MAX) + return -1; + + *(s--) = '\0'; + r--; + while (q) { + if (*r == '\'') { + s -= 3; + s[0] = '\''; + s[1] = '\\'; + s[2] = '\''; + s[3] = '\''; + q--; + } else { + *s = *r; + } + s--; + r--; + } + return 0; +} + +static int +check_path(char *path) +{ + if (replace_quote(path) < 0) + return 0; + return 1; +} + +/* returns NULL if not enough space */ +static char * +get_ugid(struct shfs_sb_info *info, char *str, int max) +{ + char buffer[1024]; + char *s = str; + + if (max < 2) + return NULL; + strcpy(s, " "); s++; + if (info->preserve_own) { + int i; + snprintf(buffer, sizeof(buffer), "%d '", current->uid); + if (s - str + strlen(buffer)+1 > max) + return NULL; + strcpy(s, buffer); s += strlen(buffer); +#ifdef get_group_info + /* 2.6.4rc1+ */ + get_group_info(current->group_info); + for (i = 0; i < current->group_info->ngroups; i++) { + snprintf(buffer, sizeof(buffer), "%d ", GROUP_AT(current->group_info, i)); + if (s - str + strlen(buffer)+2 > max) + return NULL; + strcpy(s, buffer), s += strlen(buffer); + } + put_group_info(current->group_info); +#else + for (i = 0; i < current->ngroups; i++) { + snprintf(buffer, sizeof(buffer), "%d ", current->groups[i]); + if (s - str + strlen(buffer)+2 > max) + return NULL; + strcpy(s, buffer), s += strlen(buffer); + } +#endif + strcpy(s-1, "' "); s++; + } + return s; +} + +static int +do_command(struct shfs_sb_info *info, char *cmd, char *args, ...) +{ + va_list ap; + int result; + char *s; + + if (!sock_lock(info)) + return -EINTR; + + s = info->sockbuf; + strcpy(s, cmd); s += strlen(cmd); + s = get_ugid(info, s, SOCKBUF_SIZE - strlen(cmd)); + if (!s) { + result = -ENAMETOOLONG; + goto error; + } + va_start(ap, args); + result = vsnprintf(s, SOCKBUF_SIZE - (s - info->sockbuf), args, ap); + va_end(ap); + if (result < 0 || strlen(info->sockbuf) + 2 > SOCKBUF_SIZE) { + result = -ENAMETOOLONG; + goto error; + } + s += result; + strcpy(s, "\n"); + + DEBUG("#%s", info->sockbuf); + result = sock_write(info, (void *)info->sockbuf, strlen(info->sockbuf)); + if (result < 0) + goto error; + sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + switch (reply(info->sockbuf)) { + case REP_COMPLETE: + result = 0; + break; + case REP_EPERM: + result = -EPERM; + break; + case REP_ENOENT: + result = -ENOENT; + break; + case REP_NOTEMPTY: + result = 1; + break; + default: + result = -EIO; + break; + } + +error: + sock_unlock(info); + return result; +} + +static unsigned int +get_this_year(void) +{ + unsigned long s_per_year = 365*24*3600; + unsigned long delta_s = 24*3600; + + unsigned int year = CURRENT_TIME.tv_sec / (s_per_year + delta_s/4); + return 1970 + year; +} + +static unsigned int +get_this_month(void) +{ + int month = -1; + long secs = CURRENT_TIME.tv_sec % (365*24*3600+24*3600/4); + static long days_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + if (get_this_year() % 4) { + days_month[1] = 28; + } else { + days_month[1] = 29; + } + while (month < 11 && secs >= 0) { + month++; + secs-=24*3600*days_month[month]; + } + return (unsigned int)month; +} + +static int +get_month(char *mon) +{ + static char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + unsigned int i; + + for (i = 0; i<12; i++) + if (strcmp(mon, months[i]) == 0) { + DEBUG("%s: %u\n", mon, i); + return i; + } + + return -1; +} + +static int +parse_dir(char *s, char **col) +{ + int c = 0; + int is_space = 1; + int device = 0; + + while (*s) { + if (c == DIR_COLS) + break; + + if (*s == ' ') { + if (!is_space) { + if ((c-1 == DIR_SIZE) && device) { + while (*s && (*s == ' ')) + s++; + while (*s && (*s != ' ')) + s++; + } + *s = '\0'; + is_space = 1; + } + } else { + if (is_space) { + /* (b)lock/(c)haracter device hack */ + col[c++] = s; + is_space = 0; + if ((c-1 == DIR_PERM) && ((*s == 'b')||(*s == 'c'))) { + device = 1; + } + + } + } + s++; + } + return c; +} + +static int +do_ls(struct shfs_sb_info *info, char *file, struct shfs_fattr *entry, + struct file *filp, void *dirent, filldir_t filldir, struct shfs_cache_control *ctl) +{ + char *col[DIR_COLS]; + struct shfs_fattr fattr; + struct qstr name; + unsigned int year, mon, day, hour, min; + unsigned int this_year = get_this_year(); + unsigned int this_month = get_this_month(); + int device, month; + umode_t mode; + char *b, *s, *command = entry ? "s_stat" : "s_lsdir"; + int result; + + if (!check_path(file)) + return -ENAMETOOLONG; + if (!sock_lock(info)) + return -EINTR; + + s = info->sockbuf; + strcpy(s, command); s += strlen(command); + s = get_ugid(info, s, SOCKBUF_SIZE - strlen(command)); + if (!s) { + result = -ENAMETOOLONG; + goto out; + } + if (s - info->sockbuf + strlen(file) + 4 > SOCKBUF_SIZE) { + result = -ENAMETOOLONG; + goto out; + } + strcpy(s, "'"); s++; + strcpy(s, file); s += strlen(file); + strcpy(s, "'"); s++; + strcpy(s, "\n"); + + DEBUG(">%s\n", info->sockbuf); + result = sock_write(info, (void *)info->sockbuf, strlen(info->sockbuf)); + if (result < 0) + goto out; + + while ((result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE)) > 0) { + switch (reply(info->sockbuf)) { + case REP_COMPLETE: + result = 0; + goto out; + case REP_EPERM: + result = -EPERM; + goto out; + case REP_ENOENT: + result = -ENOENT; + goto out; + case REP_ERROR: + result = -EIO; + goto out; + } + + result = parse_dir(info->sockbuf, col); + if (result != DIR_COLS) + continue; /* skip `total xx' line */ + + memset(&fattr, 0, sizeof(fattr)); + s = col[DIR_NAME]; + if (!strcmp(s, ".") || !strcmp(s, "..")) + continue; + name.name = s; + /* name.len is assigned later */ + + s = col[DIR_PERM]; + mode = 0; device = 0; + switch (s[0]) { + case 'b': + device = 1; + if ((info->fmask & S_IFMT) & S_IFBLK) + mode = S_IFBLK; + else + mode = S_IFREG; + break; + case 'c': + device = 1; + if ((info->fmask & S_IFMT) & S_IFCHR) + mode = S_IFCHR; + else + mode = S_IFREG; + break; + case 's': + case 'S': /* IRIX64 socket */ + mode = S_IFSOCK; + break; + case 'd': + mode = S_IFDIR; + break; + case 'l': + mode = S_IFLNK; + break; + case '-': + mode = S_IFREG; + break; + case 'p': + mode = S_IFIFO; + break; + } + if (s[1] == 'r') mode |= S_IRUSR; + if (s[2] == 'w') mode |= S_IWUSR; + if (s[3] == 'x') mode |= S_IXUSR; + if (s[3] == 's') mode |= S_IXUSR | S_ISUID; + if (s[3] == 'S') mode |= S_ISUID; + if (s[4] == 'r') mode |= S_IRGRP; + if (s[5] == 'w') mode |= S_IWGRP; + if (s[6] == 'x') mode |= S_IXGRP; + if (s[6] == 's') mode |= S_IXGRP | S_ISGID; + if (s[6] == 'S') mode |= S_ISGID; + if (s[7] == 'r') mode |= S_IROTH; + if (s[8] == 'w') mode |= S_IWOTH; + if (s[9] == 'x') mode |= S_IXOTH; + if (s[9] == 't') mode |= S_ISVTX | S_IXOTH; + if (s[9] == 'T') mode |= S_ISVTX; + fattr.f_mode = S_ISREG(mode) ? mode & info->fmask : mode; + + fattr.f_uid = simple_strtoul(col[DIR_UID], NULL, 10); + fattr.f_gid = simple_strtoul(col[DIR_GID], NULL, 10); + + if (!device) { + fattr.f_size = simple_strtoull(col[DIR_SIZE], NULL, 10); + } else { + unsigned short major, minor; + fattr.f_size = 0; + major = (unsigned short) simple_strtoul(col[DIR_SIZE], &s, 10); + while (*s && (!isdigit(*s))) + s++; + minor = (unsigned short) simple_strtoul(s, NULL, 10); + fattr.f_rdev = MKDEV(major, minor); + } + fattr.f_nlink = simple_strtoul(col[DIR_NLINK], NULL, 10); + fattr.f_blksize = 4096; + fattr.f_blocks = (fattr.f_size + 511) >> 9; + + month = get_month(col[DIR_MONTH]); + /* some systems have month/day swapped (MacOS X) */ + if (month < 0) { + day = simple_strtoul(col[DIR_MONTH], NULL, 10); + mon = get_month(col[DIR_DAY]); + } else { + mon = (unsigned) month; + day = simple_strtoul(col[DIR_DAY], NULL, 10); + } + + s = col[DIR_YEAR]; + if (!strchr(s, ':')) { + year = simple_strtoul(s, NULL, 10); + hour = 12; + min = 0; + } else { + year = this_year; + if (mon > this_month) + year--; + b = strchr(s, ':'); + *b = 0; + hour = simple_strtoul(s, NULL, 10); + min = simple_strtoul(++b, NULL, 10); + } + fattr.f_atime.tv_sec = fattr.f_mtime.tv_sec = fattr.f_ctime.tv_sec = mktime(year, mon + 1, day, hour, min, 0); + fattr.f_atime.tv_nsec = fattr.f_mtime.tv_nsec = fattr.f_ctime.tv_nsec = 0; + + if (S_ISLNK(mode) && ((s = strstr(name.name, " -> ")))) + *s = '\0'; + name.len = strlen(name.name); + DEBUG("Name: %s, mode: %o, size: %llu, nlink: %d, month: %d, day: %d, year: %d, hour: %d, min: %d (time: %lu)\n", name.name, fattr.f_mode, fattr.f_size, fattr.f_nlink, mon, day, year, hour, min, fattr.f_atime.tv_sec); + + if (entry) { + *entry = fattr; + } else { + result = shfs_fill_cache(filp, dirent, filldir, ctl, &name, &fattr); + if (!result) + break; + } + } +out: + sock_unlock(info); + return result; +} + +static int +shell_readdir(struct shfs_sb_info *info, char *dir, + struct file *filp, void *dirent, filldir_t filldir, struct shfs_cache_control *ctl) +{ + return do_ls(info, dir, NULL, filp, dirent, filldir, ctl); +} + +static int +shell_stat(struct shfs_sb_info *info, char *file, struct shfs_fattr *fattr) +{ + return do_ls(info, file, fattr, NULL, NULL, NULL, NULL); +} + +/* returns 1 if file is "non-empty" but has zero size */ +static int +shell_open(struct shfs_sb_info *info, char *file, int mode) +{ + char bufmode[3] = ""; + + if (!check_path(file)) + return -ENAMETOOLONG; + + if (mode == O_RDONLY) + strcpy(bufmode, "R"); + else if (mode == O_WRONLY) + strcpy(bufmode, "W"); + else + strcpy(bufmode, "RW"); + + DEBUG("Open: %s (%s)\n", file, bufmode); + return do_command(info, "s_open", "'%s' %s", file, bufmode); +} + +/* data should be aligned (offset % count == 0), ino == 0 => normal read, != 0 => slow read */ +static int +shell_read(struct shfs_sb_info *info, char *file, unsigned offset, + unsigned count, char *buffer, unsigned long ino) +{ + unsigned bs = 1, offset2 = offset, count2 = count; + int result; + char *s; + + DEBUG("<%s[%u, %u]\n", file, (unsigned)offset, (unsigned)count); + + if (!check_path(file)) + return -ENAMETOOLONG; + /* read speedup if possible */ + if (count && !(offset % count)) { + bs = count; + offset2 = offset / count; + count2 = 1; + } + + if (!sock_lock(info)) + return -EINTR; + + s = info->sockbuf; + if (ino) { + strcpy(s, "s_sread"); s += strlen("s_sread"); + } else { + strcpy(s, "s_read"); s += strlen("s_read"); + } + s = get_ugid(info, s, SOCKBUF_SIZE - strlen(info->sockbuf)); + if (!s) { + result = -ENAMETOOLONG; + goto error; + } + if (ino) { + result = snprintf(s, SOCKBUF_SIZE - (s - info->sockbuf), + "'%s' %u %u %u %u %u %lu\n", file, offset, count, bs, offset2, count2, ino); + } else { + result = snprintf(s, SOCKBUF_SIZE - (s - info->sockbuf), + "'%s' %u %u %u %u %u\n", file, offset, count, bs, offset2, count2); + } + if (result < 0) { + result = -ENAMETOOLONG; + goto error; + } + + DEBUG("<%s", info->sockbuf); + result = sock_write(info, (void *)info->sockbuf, strlen(info->sockbuf)); + if (result < 0) + goto error; + + result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + if (result < 0) { + set_garbage(info, 0, count); + goto error; + } + switch (reply(info->sockbuf)) { + case REP_PRELIM: + break; + case REP_EPERM: + result = -EPERM; + goto error; + case REP_ENOENT: + result = -ENOENT; + goto error; + default: + set_garbage(info, 0, count); + result = -EIO; + goto error; + } + if (ino) { + result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + if (result < 0) + goto error; + count = simple_strtoul(info->sockbuf, NULL, 10); + } + + result = sock_read(info, buffer, count); + if (result < 0) + goto error; + result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + if (result < 0) + goto error; + switch (reply(info->sockbuf)) { + case REP_COMPLETE: + break; + case REP_EPERM: + result = -EPERM; + goto error; + case REP_ENOENT: + result = -ENOENT; + goto error; + default: + result = -EIO; + goto error; + } + + result = count; +error: + sock_unlock(info); + DEBUG("<%d\n", result); + return result; +} + +static int +shell_write(struct shfs_sb_info *info, char *file, unsigned offset, + unsigned count, char *buffer, unsigned long ino) +{ + unsigned offset2 = offset, bs = 1; + int result; + char *s; + + if (!check_path(file)) + return -ENAMETOOLONG; +#if 0 + if (!count) + return 0; +#endif + DEBUG(">%s[%u, %u]\n", file, (unsigned)offset, (unsigned)count); + if (offset % info->fcache_size == 0) { + offset2 = offset / info->fcache_size; + bs = info->fcache_size; + } + + if (!sock_lock(info)) + return -EINTR; + + s = info->sockbuf; + strcpy(s, "s_write"); s += strlen("s_write"); + s = get_ugid(info, s, SOCKBUF_SIZE - strlen(info->sockbuf)); + if (!s) { + result = -ENAMETOOLONG; + goto error; + } + + result = snprintf(s, SOCKBUF_SIZE - (s - info->sockbuf), + "'%s' %u %u %u %u %lu\n", file, offset, count, bs, offset2, ino); + if (result < 0) { + result = -ENAMETOOLONG; + goto error; + } + + DEBUG(">%s", info->sockbuf); + result = sock_write(info, (void *)info->sockbuf, strlen(info->sockbuf)); + if (result < 0) + goto error; + result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + if (result < 0) { + set_garbage(info, 1, count); + goto error; + } + switch (reply(info->sockbuf)) { + case REP_PRELIM: + break; + case REP_EPERM: + result = -EPERM; + goto error; + case REP_ENOENT: + result = -ENOENT; + goto error; + default: + result = -EIO; + goto error; + } + + result = sock_write(info, buffer, count); + if (result < 0) + goto error; + result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + if (result < 0) + goto error; + switch (reply(info->sockbuf)) { + case REP_COMPLETE: + break; + case REP_EPERM: + result = -EPERM; + goto error; + case REP_ENOENT: + result = -ENOENT; + goto error; + case REP_ENOSPC: + result = -ENOSPC; + set_garbage(info, 1, count); + goto error; + default: + result = -EIO; + goto error; + } + + result = count; +error: + sock_unlock(info); + DEBUG(">%d\n", result); + return result; +} + +static int +shell_mkdir(struct shfs_sb_info *info, char *dir) +{ + if (!check_path(dir)) + return -ENAMETOOLONG; + + DEBUG("Mkdir %s\n", dir); + return do_command(info, "s_mkdir", "'%s'", dir); +} + +static int +shell_rmdir(struct shfs_sb_info *info, char *dir) +{ + if (!check_path(dir)) + return -ENAMETOOLONG; + + DEBUG("Rmdir %s\n", dir); + return do_command(info, "s_rmdir", "'%s'", dir); +} + +static int +shell_rename(struct shfs_sb_info *info, char *old, char *new) +{ + if (!check_path(old) || !check_path(new)) + return -ENAMETOOLONG; + + DEBUG("Rename %s -> %s\n", old, new); + return do_command(info, "s_mv", "'%s' '%s'", old, new); +} + +static int +shell_unlink(struct shfs_sb_info *info, char *file) +{ + if (!check_path(file)) + return -ENAMETOOLONG; + + DEBUG("Remove %s\n", file); + return do_command(info, "s_rm", "'%s'", file); +} + +static int +shell_create(struct shfs_sb_info *info, char *file, int mode) +{ + if (!check_path(file)) + return -ENAMETOOLONG; + + DEBUG("Create %s (%o)\n", file, mode); + return do_command(info, "s_creat", "'%s' %o", file, mode & S_IALLUGO); +} + +static int +shell_link(struct shfs_sb_info *info, char *old, char *new) +{ + if (!check_path(old) || !check_path(new)) + return -ENAMETOOLONG; + + DEBUG("Link %s -> %s\n", old, new); + return do_command(info, "s_ln", "'%s' '%s'", old, new); +} + +static int +shell_symlink(struct shfs_sb_info *info, char *old, char *new) +{ + if (!check_path(old) || !check_path(new)) + return -ENAMETOOLONG; + + DEBUG("Symlink %s -> %s\n", old, new); + return do_command(info, "s_sln", "'%s' '%s'", old, new); +} + +static int +shell_readlink(struct shfs_sb_info *info, char *name, char *real_name) +{ + char *s; + int result = 0; + + if (!check_path(name) || !check_path(real_name)) + return -ENAMETOOLONG; + + if (!sock_lock(info)) + return -EINTR; + + s = info->sockbuf; + strcpy(s, "s_readlink"); s += strlen("s_readlink"); + s = get_ugid(info, s, SOCKBUF_SIZE - strlen(info->sockbuf)); + if (!s) { + result = -ENAMETOOLONG; + goto error; + } + if (s - info->sockbuf + strlen(name) + 4 > SOCKBUF_SIZE) { + result = -ENAMETOOLONG; + goto error; + } + strcpy(s, "'"); s++; + strcpy(s, name); s += strlen(name); + strcpy(s, "'"); s++; + strcpy(s, "\n"); + + DEBUG("Readlink %s\n", info->sockbuf); + result = sock_write(info, (void *)info->sockbuf, strlen(info->sockbuf)); + if (result < 0) + goto error; + result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + if (result < 0) + goto error; + + switch (reply(info->sockbuf)) { + case REP_COMPLETE: + result = -EIO; + goto error; + case REP_EPERM: + result = -EPERM; + goto error; + } + + strncpy(real_name, info->sockbuf, SHFS_PATH_MAX-1); + real_name[SHFS_PATH_MAX-1] = '\0'; + + result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + if (result < 0) + goto error; + switch (reply(info->sockbuf)) { + case REP_COMPLETE: + result = 0; + break; + case REP_EPERM: + result = -EPERM; + goto error; + default: + info->garbage = 1; + result = -EIO; + goto error; + } +error: + sock_unlock(info); + return result; +} + +static int +shell_chmod(struct shfs_sb_info *info, char *file, umode_t mode) +{ + if (!check_path(file)) + return -ENAMETOOLONG; + + DEBUG("Chmod %o %s\n", mode, file); + return do_command(info, "s_chmod", "'%s' %o", file, mode); +} + +static int +shell_chown(struct shfs_sb_info *info, char *file, uid_t user) +{ + if (!check_path(file)) + return -ENAMETOOLONG; + + DEBUG("Chown %u %s\n", user, file); + return do_command(info, "s_chown", "'%s' %u", file, user); +} + +static int +shell_chgrp(struct shfs_sb_info *info, char *file, gid_t group) +{ + if (!check_path(file)) + return -ENAMETOOLONG; + + DEBUG("Chgrp %u %s\n", group, file); + return do_command(info, "s_chgrp", "'%s' %u", file, group); +} + +static int +shell_trunc(struct shfs_sb_info *info, char *file, loff_t size) +{ + unsigned seek = 1; + + if (!check_path(file)) + return -ENAMETOOLONG; + + DEBUG("Truncate %s %u\n", file, (unsigned)size); + /* dd doesn't like bs=0 */ + if (size == 0) { + seek = 0; + size = 1; + } + return do_command(info, "s_trunc", "'%s' %u %u", file, (unsigned) size, seek); +} + +/* this code is borrowed from dietlibc */ + +static int +__isleap(int year) { + /* every fourth year is a leap year except for century years that are + * not divisible by 400. */ +/* return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); */ + return (!(year%4) && ((year%100) || !(year%400))); +} + +/* days per month -- nonleap! */ +const short __spm[12] = + { 0, + (31), + (31+28), + (31+28+31), + (31+28+31+30), + (31+28+31+30+31), + (31+28+31+30+31+30), + (31+28+31+30+31+30+31), + (31+28+31+30+31+30+31+31), + (31+28+31+30+31+30+31+31+30), + (31+28+31+30+31+30+31+31+30+31), + (31+28+31+30+31+30+31+31+30+31+30), + }; + +/* seconds per day */ +#define SPD 24*60*60 + +static char * +strctime(const struct timespec *timep, char *s) +{ + time_t i; + int sec, min, hour; + int day, month, year; + time_t work = timep->tv_sec % (SPD); + + sec = work % 60; work /= 60; + min = work % 60; hour = work / 60; + work = timep->tv_sec / (SPD); + + for (i = 1970; ; i++) { + time_t k = __isleap(i) ? 366 : 365; + if (work >= k) + work -= k; + else + break; + } + year = i; + + day=1; + if (__isleap(i) && (work > 58)) { + if (work == 59) + day = 2; /* 29.2. */ + work -= 1; + } + + for (i = 11; i && (__spm[i] > work); i--) + ; + month = i; + day += work - __spm[i]; + + sprintf(s, "%.4d%.2d%.2d%.2d%.2d.%.2d", year, month+1, day, hour, min, sec); + + return s; +} + +static int +shell_settime(struct shfs_sb_info *info, char *file, int atime, int mtime, struct timespec *time) +{ + char str[20]; + + if (!check_path(file)) + return -ENAMETOOLONG; + + strctime(time, str); + DEBUG("Settime %s (%s%s) %s\n", str, atime ? "a" : "", mtime ? "m" : "", file); + return do_command(info, "s_settime", "'%s' %s%s %s", file, atime ? "a" : "", mtime ? "m" : "", str); +} + +static int +shell_statfs(struct shfs_sb_info *info, struct kstatfs *attr) +{ + char *s, *p; + int result = 0; + + attr->f_type = SHFS_SUPER_MAGIC; + attr->f_bsize = 4096; + attr->f_blocks = 0; + attr->f_bfree = 0; + attr->f_bavail = 0; + attr->f_files = 1; + attr->f_bavail = 1; + attr->f_namelen = SHFS_PATH_MAX; + + if (!sock_lock(info)) + return -EINTR; + + s = info->sockbuf; + strcpy(s, "s_statfs"); s += strlen("s_statfs"); + s = get_ugid(info, s, SOCKBUF_SIZE - strlen(info->sockbuf)); + if (!s) { + result = -ENAMETOOLONG; + goto error; + } + strcpy(s, "\n"); + + DEBUG("Statfs %s\n", info->sockbuf); + result = sock_write(info, (void *)info->sockbuf, strlen(info->sockbuf)); + if (result < 0) + goto error; + result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + if (result < 0) + goto error; + + s = info->sockbuf; + if ((p = strsep(&s, " "))) + attr->f_blocks = simple_strtoull(p, NULL, 10); + if ((p = strsep(&s, " "))) + attr->f_bfree = attr->f_blocks - simple_strtoull(p, NULL, 10); + if ((p = strsep(&s, " "))) + attr->f_bavail = simple_strtoull(p, NULL, 10); + + result = sock_readln(info, info->sockbuf, SOCKBUF_SIZE); + if (result < 0) + goto error; + switch (reply(info->sockbuf)) { + case REP_COMPLETE: + result = 0; + break; + case REP_EPERM: + result = -EPERM; + goto error; + default: + info->garbage = 1; + result = -EIO; + goto error; + } +error: + sock_unlock(info); + return result; +} + +static int +shell_finish(struct shfs_sb_info *info) +{ + DEBUG("Finish\n"); + return do_command(info, "s_finish", ""); +} + +struct shfs_fileops shell_fops = { + readdir: shell_readdir, + stat: shell_stat, + open: shell_open, + read: shell_read, + write: shell_write, + mkdir: shell_mkdir, + rmdir: shell_rmdir, + rename: shell_rename, + unlink: shell_unlink, + create: shell_create, + link: shell_link, + symlink: shell_symlink, + readlink: shell_readlink, + chmod: shell_chmod, + chown: shell_chown, + chgrp: shell_chgrp, + trunc: shell_trunc, + settime: shell_settime, + statfs: shell_statfs, + finish: shell_finish, +}; diff -urN newtree/fs/shfs/shfs_debug.h newtree.2/fs/shfs/shfs_debug.h --- newtree/fs/shfs/shfs_debug.h 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/shfs_debug.h 2006-08-04 10:03:29.000000000 -0700 @@ -0,0 +1,57 @@ +#ifndef _SHFS_DEBUG_H +#define _SHFS_DEBUG_H + +#define ENABLE_DEBUG + +#define SHFS_VERBOSE 1 +#define SHFS_ALLOC 2 +#define SHFS_DEBUG 3 + +extern int debug_level; + +#ifdef ENABLE_DEBUG + +#define DEBUG(x...) do { if (debug_level >= SHFS_DEBUG) { printk(KERN_DEBUG "%s: ", __FUNCTION__); printk(x); } } while (0) + +#define VERBOSE(x...) do { if (debug_level >= SHFS_VERBOSE) { printk(KERN_NOTICE "%s: ", __FUNCTION__); printk(x); } } while (0) + +#include +extern unsigned long alloc; + +static inline void * +__kmem_malloc_debug(char *s, kmem_cache_t *cache, int flags) +{ + if (debug_level >= SHFS_ALLOC) { + void *x = kmem_cache_alloc(cache, flags); + alloc += (unsigned long) x; + VERBOSE("alloc (%s): %p\n", s, x); + return x; + } else { + return kmem_cache_alloc(cache, flags); + } +} + +static inline void +__kmem_free_debug(char *s, kmem_cache_t *cache, void *p) +{ + if (debug_level >= SHFS_ALLOC) { + VERBOSE("free (%s): %p\n", s, p); + alloc -= (unsigned long) p; + } + kmem_cache_free(cache, p); +} + +#define KMEM_ALLOC(s, cache, flags) __kmem_malloc_debug(s, cache, flags); +#define KMEM_FREE(s, cache, p) __kmem_free_debug(s, cache, p); + +#else + +#define DEBUG(x...) ; +#define VERBOSE(x...) ; + +#define KMEM_ALLOC(s, cache, flags) kmem_cache_alloc(cache, flags) +#define KMEM_FREE(s, cache, p) kmem_cache_free(cache, p) + +#endif + +#endif /* _SHFS_DEBUG_H */ diff -urN newtree/fs/shfs/symlink.c newtree.2/fs/shfs/symlink.c --- newtree/fs/shfs/symlink.c 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/fs/shfs/symlink.c 2006-08-04 10:03:29.000000000 -0700 @@ -0,0 +1,72 @@ +/* + * symlink.c + * + * Symlink resolving implementation. + */ + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include "shfs_debug.h" +#include "proc.h" + +static int +shfs_readlink(struct dentry *dentry, char *buffer, int bufflen) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + char name[SHFS_PATH_MAX]; + char real_name[SHFS_PATH_MAX]; + int result; + + DEBUG("%s\n", dentry->d_name.name); + + result = -ENAMETOOLONG; + if (get_name(dentry, name) < 0) + goto error; + + result = info->fops.readlink(info, name, real_name); + if (result < 0) + goto error; + DEBUG("%s\n", real_name); + result = vfs_readlink(dentry, buffer, bufflen, real_name); +error: + return result; +} + +static int +shfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct shfs_sb_info *info = info_from_dentry(dentry); + char name[SHFS_PATH_MAX]; + char real_name[SHFS_PATH_MAX]; + int result; + + DEBUG("%s\n", dentry->d_name.name); + + result = -ENAMETOOLONG; + if (get_name(dentry, name) < 0) + goto error; + + result = info->fops.readlink(info, name, real_name); + if (result < 0) + goto error; + DEBUG("%s\n", real_name); + result = vfs_follow_link(nd, real_name); +error: + return result; +} + +struct inode_operations shfs_symlink_inode_operations = { + .readlink = shfs_readlink, + .follow_link = shfs_follow_link, + .getattr = shfs_getattr, + .setattr = shfs_notify_change, +}; diff -urN newtree/include/linux/shfs.h newtree.2/include/linux/shfs.h --- newtree/include/linux/shfs.h 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/include/linux/shfs.h 2006-08-04 10:03:29.000000000 -0700 @@ -0,0 +1,44 @@ +#ifndef _SHFS_H +#define _SHFS_H + +#define PROTO_VERSION 2 + +/* response code */ +#define REP_PRELIM 100 +#define REP_COMPLETE 200 +#define REP_NOP 201 +#define REP_NOTEMPTY 202 /* file with zero size but not empty */ +#define REP_CONTINUE 300 +#define REP_TRANSIENT 400 +#define REP_ERROR 500 +#define REP_EPERM 501 +#define REP_ENOSPC 502 +#define REP_ENOENT 503 + +#define SHFS_SUPER_MAGIC 0xD0D0 + +#ifdef __KERNEL__ + +#define SHFS_DEFAULT_TTL 20000 +#define SHFS_PATH_MAX 512 + +#include + +struct shfs_fattr { + unsigned long f_ino; + umode_t f_mode; + nlink_t f_nlink; + uid_t f_uid; + gid_t f_gid; + dev_t f_rdev; + loff_t f_size; + struct timespec f_atime; + struct timespec f_mtime; + struct timespec f_ctime; + unsigned long f_blksize; + unsigned long f_blocks; +}; + +#endif /* __KERNEL__ */ + +#endif /* _SHFS_H */ diff -urN newtree/include/linux/shfs_fs.h newtree.2/include/linux/shfs_fs.h --- newtree/include/linux/shfs_fs.h 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/include/linux/shfs_fs.h 2006-08-04 10:03:29.000000000 -0700 @@ -0,0 +1,117 @@ +#ifndef _SHFS_FS_H +#define _SHFS_FS_H + +#include + +#define SHFS_IOC_NEWCONN _IOW('s', 2, int) + +#ifdef __KERNEL__ + +#include +#include + +#define SHFS_MAX_AGE(info) (((info)->ttl * HZ) / 1000) +#define SOCKBUF_SIZE (SHFS_PATH_MAX * 10) +#define READLNBUF_SIZE (SHFS_PATH_MAX * 10) + +#define SHFS_FCACHE_MAX 10 /* max number of files cached */ +#define SHFS_FCACHE_PAGES 32 /* should be 2^x */ + +struct shfs_sb_info; + +struct shfs_cache_head { +// time_t mtime; /* unused */ + unsigned long time; /* cache age */ + unsigned long end; /* last valid fpos in cache */ + int eof; +}; + +#define SHFS_DIRCACHE_SIZE ((int)(PAGE_CACHE_SIZE/sizeof(struct dentry *))) +union shfs_dir_cache { + struct shfs_cache_head head; + struct dentry *dentry[SHFS_DIRCACHE_SIZE]; +}; + +#define SHFS_FIRSTCACHE_SIZE ((int)((SHFS_DIRCACHE_SIZE * \ + sizeof(struct dentry *) - sizeof(struct shfs_cache_head)) / \ + sizeof(struct dentry *))) + +#define SHFS_DIRCACHE_START (SHFS_DIRCACHE_SIZE - SHFS_FIRSTCACHE_SIZE) + +struct shfs_cache_control { + struct shfs_cache_head head; + struct page *page; + union shfs_dir_cache *cache; + unsigned long fpos, ofs; + int filled, valid, idx; +}; + +/* use instead of CURRENT_TIME since precision is minutes, not seconds */ +#define ROUND_TO_MINS(x) do { (x).tv_sec = ((x).tv_sec / 60) * 60; (x).tv_nsec = 0; } while (0) + +/* shfs/dir.c */ +extern struct dentry_operations shfs_dentry_operations; +extern struct file_operations shfs_dir_operations; +extern struct inode_operations shfs_dir_inode_operations; +extern void shfs_new_dentry(struct dentry *dentry); +extern void shfs_age_dentry(struct shfs_sb_info *info, struct dentry *dentry); +extern void shfs_renew_times(struct dentry * dentry); + +/* shfs/file.c */ +extern struct file_operations shfs_file_operations; +extern struct file_operations shfs_slow_operations; +extern struct inode_operations shfs_file_inode_operations; +extern struct address_space_operations shfs_file_aops; + +/* shfs/symlink.c */ +extern struct inode_operations shfs_symlink_inode_operations; + +/* shfs/dcache.c */ +void shfs_invalid_dir_cache(struct inode * dir); +void shfs_invalidate_dircache_entries(struct dentry *parent); +struct dentry *shfs_dget_fpos(struct dentry*, struct dentry*, unsigned long); +int shfs_fill_cache(struct file*, void*, filldir_t, struct shfs_cache_control*, struct qstr*, struct shfs_fattr*); + +/* shfs/fcache.c */ +#include +extern kmem_cache_t *file_cache; +extern kmem_cache_t *dir_head_cache; +extern kmem_cache_t *dir_entry_cache; +extern kmem_cache_t *dir_name_cache; +void fcache_init(void); +void fcache_finish(void); +int fcache_file_open(struct file*); +int fcache_file_sync(struct file*); +int fcache_file_close(struct file*); +int fcache_file_clear(struct inode*); +int fcache_file_read(struct file*, unsigned, unsigned, char*); +int fcache_file_write(struct file*, unsigned, unsigned, char*); + +/* shfs/ioctl.c */ +int shfs_ioctl(struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg); + +#include + +/* shfs/proc.c */ +int parse_options(struct shfs_sb_info *info, char *opts); +int sock_write(struct shfs_sb_info *info, const void *buf, int count); +int sock_read(struct shfs_sb_info *info, void *buffer, int count); +int sock_readln(struct shfs_sb_info *info, char *buffer, int count); +int reply(char *s); +void set_garbage(struct shfs_sb_info *info, int write, int count); +int get_name(struct dentry *d, char *name); +int shfs_notify_change(struct dentry *dentry, struct iattr *attr); +int shfs_statfs(struct super_block *sb, struct kstatfs *attr); + +/* shfs/inode.c */ +void shfs_set_inode_attr(struct inode *inode, struct shfs_fattr *fattr); +struct inode *shfs_iget(struct super_block*, struct shfs_fattr*); +int shfs_revalidate_inode(struct dentry*); +int shfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); + +/* shfs/shell.c */ +extern struct shfs_fileops shell_fops; + +#endif /* __KERNEL__ */ + +#endif /* _SHFS_FS_H */ diff -urN newtree/include/linux/shfs_fs_i.h newtree.2/include/linux/shfs_fs_i.h --- newtree/include/linux/shfs_fs_i.h 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/include/linux/shfs_fs_i.h 2006-08-04 10:03:29.000000000 -0700 @@ -0,0 +1,21 @@ +/* + * shfs_fs_i.h + */ + +#ifndef _SHFS_FS_I_H +#define _SHFS_FS_I_H + +#ifdef __KERNEL__ +#include + +struct shfs_file; + +struct shfs_inode_info { + unsigned long oldmtime; /* last time refreshed */ + int unset_write_on_close; /* created ro, opened for write */ + struct shfs_file *cache; /* readahead cache */ +}; + +#endif + +#endif diff -urN newtree/include/linux/shfs_fs_sb.h newtree.2/include/linux/shfs_fs_sb.h --- newtree/include/linux/shfs_fs_sb.h 1969-12-31 16:00:00.000000000 -0800 +++ newtree.2/include/linux/shfs_fs_sb.h 2006-08-04 10:03:29.000000000 -0700 @@ -0,0 +1,64 @@ +#ifndef _SHFS_FS_SB_H +#define _SHFS_FS_SB_H + +#include + +#ifdef __KERNEL__ + +struct shfs_fileops { + int (*readdir)(struct shfs_sb_info *info, char *dir, struct file *filp, void *dirent, filldir_t filldir, struct shfs_cache_control *ctl); + int (*stat)(struct shfs_sb_info *info, char *file, struct shfs_fattr *fattr); + int (*open)(struct shfs_sb_info *info, char *file, int mode); + int (*read)(struct shfs_sb_info *info, char *file, unsigned offset, + unsigned count, char *buffer, unsigned long ino); + int (*write)(struct shfs_sb_info *info, char *file, unsigned offset, + unsigned count, char *buffer, unsigned long ino); + int (*mkdir)(struct shfs_sb_info *info, char *dir); + int (*rmdir)(struct shfs_sb_info *info, char *dir); + int (*rename)(struct shfs_sb_info *info, char *old, char *new); + int (*unlink)(struct shfs_sb_info *info, char *file); + int (*create)(struct shfs_sb_info *info, char *file, int mode); + int (*link)(struct shfs_sb_info *info, char *old, char *new); + int (*symlink)(struct shfs_sb_info *info, char *old, char *new); + int (*readlink)(struct shfs_sb_info *info, char *name, char *real_name); + int (*chmod)(struct shfs_sb_info *info, char *file, umode_t mode); + int (*chown)(struct shfs_sb_info *info, char *file, uid_t user); + int (*chgrp)(struct shfs_sb_info *info, char *file, gid_t group); + int (*trunc)(struct shfs_sb_info *info, char *file, loff_t size); + int (*settime)(struct shfs_sb_info *info, char *file, int atime, int mtime, struct timespec *time); + int (*statfs)(struct shfs_sb_info *info, struct kstatfs *attr); + int (*finish)(struct shfs_sb_info *info); +}; + +#define info_from_inode(inode) ((struct shfs_sb_info *)(inode)->i_sb->s_fs_info) +#define info_from_dentry(dentry) ((struct shfs_sb_info *)(dentry)->d_sb->s_fs_info) +#define info_from_sb(sb) ((struct shfs_sb_info *)(sb)->s_fs_info) + +struct shfs_sb_info { + struct shfs_fileops fops; + int version; + int ttl; + __kernel_uid_t uid; + __kernel_gid_t gid; + __kernel_mode_t root_mode; + __kernel_mode_t fmask; + char mount_point[SHFS_PATH_MAX]; + struct semaphore sock_sem; /* next 4 vars are guarded */ + struct file *sock; + char *sockbuf; + char *readlnbuf; + int readlnbuf_len; + spinlock_t fcache_lock; /* fcache_free is guarded */ + int fcache_free; + int fcache_size; + int garbage_read; + int garbage_write; + int garbage:1; + int readonly:1; + int preserve_own:1; + int stable_symlinks:1; +}; + +#endif /* __KERNEL__ */ + +#endif /* _SHFS_FS_SB_H */