N: Nick Holloway
E: Nick.Holloway@alfie.demon.co.uk
E: Nick.Holloway@parallax.co.uk
-D: Small patches for kernel, libc
-D: MAKEDEV
+W: http://www.alfie.demon.co.uk/
+P: 1024/75C49395 3A F0 E3 4E B7 9F E0 7E 47 A3 B0 D5 68 6A C2 FB
+D: Occasional Linux hacker...
S: 15 Duke Street
S: Chapelfields
S: Coventry
VERSION = 2
PATCHLEVEL = 1
-SUBLEVEL = 57
+SUBLEVEL = 58
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
/* Are we prepared to handle this kernel fault? */
if ((fixup = search_exception_table(regs->eip)) != 0) {
- printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n",
+ printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n",
tsk->comm,
regs->eip,
+ address,
fixup);
regs->eip = fixup;
goto out;
return ERR_PTR(-EIO);
}
-static int return_EIO()
+static int return_EIO(void)
{
return -EIO;
}
inode->i_op = &bad_inode_ops;
}
+/*
+ * This tests whether an inode has been flagged as bad. The test uses
+ * &bad_inode_ops to cover the case of invalidated inodes as well as
+ * those created by make_bad_inode() above.
+ */
+int is_bad_inode(struct inode * inode)
+{
+ return (inode->i_op == &bad_inode_ops);
+}
const char *sp;
char del, *dp;
struct binfmt_entry *e;
- int memsize, cnt = count - 1, err = 0;
+ int memsize, cnt = count - 1, err;
- MOD_INC_USE_COUNT;
/* some sanity checks */
- if ((count < 11) || (count > 256)) {
- err = -EINVAL;
+ err = -EINVAL;
+ if ((count < 11) || (count > 256))
goto _err;
- }
+ err = -ENOMEM;
memsize = sizeof(struct binfmt_entry) + count;
- if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER))) {
- err = -ENOMEM;
+ if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER)))
goto _err;
- }
+ err = 0;
sp = buffer + 1;
del = buffer[0];
dp = (char *)e + sizeof(struct binfmt_entry);
/* more sanity checks */
if (err || !(!cnt || (!(--cnt) && (*sp == '\n'))) ||
(e->size < 1) || ((e->size + e->offset) > 127) ||
- !(e->proc_name) || !(e->interpreter) ||
- entry_proc_setup(e)) {
- kfree(e);
- err = -EINVAL;
- goto _err;
- }
+ !(e->proc_name) || !(e->interpreter) || entry_proc_setup(e))
+ goto free_err;
write_lock(&entries_lock);
e->next = entries;
err = count;
_err:
- MOD_DEC_USE_COUNT;
return err;
+free_err:
+ kfree(e);
+ err = -EINVAL;
+ goto _err;
}
/*
char *dp;
int elen, i, err;
- MOD_INC_USE_COUNT;
#ifndef VERBOSE_STATUS
if (data) {
if (!(e = get_entry((int) data))) {
err = elen;
_err:
- MOD_DEC_USE_COUNT;
return err;
}
struct binfmt_entry *e;
int res = count;
- MOD_INC_USE_COUNT;
if (buffer[count-1] == '\n')
count--;
if ((count == 1) && !(buffer[0] & ~('0' | '1'))) {
} else {
res = -EINVAL;
}
- MOD_DEC_USE_COUNT;
return res;
}
return 0;
}
+#ifdef MODULE
+/*
+ * This is called as the fill_inode function when an inode
+ * is going into (fill = 1) or out of service (fill = 0).
+ * We use it here to manage the module use counts.
+ *
+ * Note: only the top-level directory needs to do this; if
+ * a lower level is referenced, the parent will be as well.
+ */
+static void bm_modcount(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+#endif
__initfunc(int init_misc_binfmt(void))
{
struct proc_dir_entry *status = NULL, *reg;
+ int error = -ENOMEM;
- if (!(bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR,
- NULL)) ||
- !(status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR,
- bm_dir)) ||
- !(reg = create_proc_entry("register", S_IFREG | S_IWUSR,
- bm_dir))) {
- if (status)
- remove_proc_entry("status", bm_dir);
- if (bm_dir)
- remove_proc_entry("sys/fs/binfmt_misc", NULL);
- return -ENOMEM;
- }
+ bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR, NULL);
+ if (!bm_dir)
+ goto out;
+#ifdef MODULE
+ bm_dir->fill_inode = bm_modcount;
+#endif
+
+ status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR,
+ bm_dir);
+ if (!status)
+ goto cleanup_bm;
status->read_proc = proc_read_status;
status->write_proc = proc_write_status;
+ reg = create_proc_entry("register", S_IFREG | S_IWUSR, bm_dir);
+ if (!reg)
+ goto cleanup_status;
reg->write_proc = proc_write_register;
- return register_binfmt(&misc_format);
+ error = register_binfmt(&misc_format);
+out:
+ return error;
+
+cleanup_status:
+ remove_proc_entry("status", bm_dir);
+cleanup_bm:
+ remove_proc_entry("sys/fs/binfmt_misc", NULL);
+ goto out;
}
#ifdef MODULE
*/
void dput(struct dentry *dentry)
{
- if (dentry) {
- int count;
+ int count;
+
+ if (!dentry)
+ return;
+
repeat:
- count = dentry->d_count-1;
- if (count < 0) {
- printk("Negative d_count (%d) for %s/%s\n",
- count,
- dentry->d_parent->d_name.name,
- dentry->d_name.name);
- *(int *)0 = 0;
- }
+ count = dentry->d_count - 1;
+ if (count != 0)
+ goto out;
+
+ /*
+ * Note that if d_op->d_delete blocks,
+ * the dentry could go back in use.
+ * Each fs will have to watch for this.
+ */
+ if (dentry->d_op && dentry->d_op->d_delete) {
+ dentry->d_op->d_delete(dentry);
+
+ count = dentry->d_count - 1;
+ if (count != 0)
+ goto out;
+ }
+
+ list_del(&dentry->d_lru);
+ if (list_empty(&dentry->d_hash)) {
+ struct inode *inode = dentry->d_inode;
+ struct dentry * parent;
+ if (inode)
+ iput(inode);
+ parent = dentry->d_parent;
+ d_free(dentry);
+ if (dentry == parent)
+ return;
+ dentry = parent;
+ goto repeat;
+ }
+ list_add(&dentry->d_lru, &dentry_unused);
+out:
+ if (count >= 0) {
dentry->d_count = count;
- if (!count) {
- list_del(&dentry->d_lru);
- if (dentry->d_op && dentry->d_op->d_delete)
- dentry->d_op->d_delete(dentry);
- if (list_empty(&dentry->d_hash)) {
- struct inode *inode = dentry->d_inode;
- struct dentry * parent;
- if (inode)
- iput(inode);
- parent = dentry->d_parent;
- d_free(dentry);
- if (dentry == parent)
- return;
- dentry = parent;
- goto repeat;
- }
- list_add(&dentry->d_lru, &dentry_unused);
- }
+ return;
}
+
+ printk("Negative d_count (%d) for %s/%s\n",
+ count,
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ *(int *)0 = 0;
}
/*
* possible. If there are other users of the dentry we
* can't invalidate it.
*
- * This is currently incorrect. We should try to see if
- * we can invalidate any unused children - right now we
- * refuse to invalidate way too much.
+ * We should probably try to see if we can invalidate
+ * any unused children - right now we refuse to invalidate
+ * too much. That would require a better child list
+ * data structure, though.
*/
int d_invalidate(struct dentry * dentry)
{
- /* We should do a partial shrink_dcache here */
+ /* We might want to do a partial shrink_dcache here */
if (dentry->d_count != 1)
return -EBUSY;
return 0;
}
+/*
+ * Throw away a dentry - free the inode, dput the parent.
+ * This requires that the LRU list has already been
+ * removed.
+ */
+static inline void prune_one_dentry(struct dentry * dentry)
+{
+ struct dentry * parent;
+
+ list_del(&dentry->d_hash);
+ if (dentry->d_inode) {
+ struct inode * inode = dentry->d_inode;
+
+ dentry->d_inode = NULL;
+ iput(inode);
+ }
+ parent = dentry->d_parent;
+ d_free(dentry);
+ dput(parent);
+}
+
/*
* Shrink the dcache. This is done when we need
* more memory, or simply when we need to unmount
INIT_LIST_HEAD(tmp);
dentry = list_entry(tmp, struct dentry, d_lru);
if (!dentry->d_count) {
- struct dentry * parent;
-
- list_del(&dentry->d_hash);
- if (dentry->d_inode) {
- struct inode * inode = dentry->d_inode;
-
- dentry->d_inode = NULL;
- iput(inode);
- }
- parent = dentry->d_parent;
- d_free(dentry);
- dput(parent);
+ prune_one_dentry(dentry);
if (!--count)
break;
}
}
}
+/*
+ * Shrink the dcache for the specified super block.
+ * This allows us to unmount a device without disturbing
+ * the dcache for the other devices.
+ *
+ * This implementation makes just two traversals of the
+ * unused list. On the first pass we move the selected
+ * dentries to the most recent end, and on the second
+ * pass we free them. The second pass must restart after
+ * each dput(), but since the target dentries are all at
+ * the end, it's really just a single traversal.
+ */
+void shrink_dcache_sb(struct super_block * sb)
+{
+ struct list_head *tmp, *next;
+ struct dentry *dentry;
+
+ /*
+ * Pass one ... move the dentries for the specified
+ * superblock to the most recent end of the unused list.
+ */
+ next = dentry_unused.next;
+ while (next != &dentry_unused) {
+ tmp = next;
+ next = tmp->next;
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ if (dentry->d_sb != sb)
+ continue;
+ list_del(tmp);
+ list_add(tmp, &dentry_unused);
+ }
+
+ /*
+ * Pass two ... free the dentries for this superblock.
+ */
+repeat:
+ next = dentry_unused.next;
+ while (next != &dentry_unused) {
+ tmp = next;
+ next = tmp->next;
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ if (dentry->d_sb != sb)
+ continue;
+ if (dentry->d_count)
+ continue;
+ list_del(tmp);
+ INIT_LIST_HEAD(tmp);
+ prune_one_dentry(dentry);
+ goto repeat;
+ }
+}
+
#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
static kmem_cache_t *filp_cache;
/* sysctl tunables... */
-int nr_files = 0;
-int max_files = NR_FILE;
+int nr_files = 0; /* read only */
+int nr_free_files = 0; /* read only */
+int max_files = NR_FILE;/* tunable */
/* Free list management, if you are here you must have f_count == 0 */
static struct file * free_filps = NULL;
free_filps->f_pprev = &file->f_next;
free_filps = file;
file->f_pprev = &free_filps;
+ nr_free_files++;
}
/* The list of in-use filp's must be exported (ugh...) */
file->f_pprev = &inuse_filps;
}
+/* N.B. This should be an __initfunc ... */
void file_table_init(void)
{
filp_cache = kmem_cache_create("filp", sizeof(struct file),
SLAB_HWCACHE_ALIGN, NULL, NULL);
if(!filp_cache)
panic("VFS: Cannot alloc filp SLAB cache.");
+ /*
+ * We could allocate the reserved files here, but really
+ * shouldn't need to: the normal boot process will create
+ * plenty of free files.
+ */
}
/* Find an unused file structure and return a pointer to it.
static int old_max = 0;
struct file * f;
- f = free_filps;
- if (!f)
- goto get_more;
- remove_filp(f);
-got_one:
- memset(f, 0, sizeof(*f));
- f->f_count = 1;
- f->f_version = ++event;
- put_inuse(f);
- return f;
-
-get_more:
- /* Reserve a few files for the super-user.. */
- if (nr_files < (current->euid ? max_files - 10 : max_files)) {
+ if (nr_free_files > NR_RESERVED_FILES) {
+ used_one:
+ f = free_filps;
+ remove_filp(f);
+ nr_free_files--;
+ new_one:
+ memset(f, 0, sizeof(*f));
+ f->f_count = 1;
+ f->f_version = ++event;
+ put_inuse(f);
+ return f;
+ }
+ /*
+ * Use a reserved one if we're the superuser
+ */
+ if (nr_free_files && !current->euid)
+ goto used_one;
+ /*
+ * Allocate a new one if we're below the limit.
+ */
+ if (nr_files < max_files) {
f = kmem_cache_alloc(filp_cache, SLAB_KERNEL);
if (f) {
nr_files++;
- goto got_one;
+ goto new_one;
}
/* Big problems... */
printk("VFS: filp allocation failed\n");
nlmclnt_grace_wait(struct nlm_host *host)
{
if (!host->h_reclaiming)
- current->timeout = 10 * HZ;
+ current->timeout = jiffies + 10 * HZ;
interruptible_sleep_on(&host->h_gracewait);
+ current->timeout = 0;
return signalled()? -ERESTARTSYS : 0;
}
sizeof(struct nlm_rqst));
if (call)
return call;
- current->timeout = 5 * HZ;
+ printk("nlmclnt_alloc_call: failed, waiting for memory\n");
+ current->timeout = jiffies + 5 * HZ;
current->state = TASK_INTERRUPTIBLE;
schedule();
+ current->timeout = 0;
}
return NULL;
}
/* Back off a little and try again */
current->timeout = jiffies + 15 * HZ;
interruptible_sleep_on(&host->h_gracewait);
+ current->timeout = 0;
} while (!signalled());
return -ERESTARTSYS;
extern struct svc_program nlmsvc_program;
struct nlmsvc_binding * nlmsvc_ops = NULL;
-static int nlmsvc_sema = 0;
-static int nlmsvc_pid = 0;
+static struct semaphore nlmsvc_sema = MUTEX;
+static unsigned int nlmsvc_users = 0;
+static pid_t nlmsvc_pid = 0;
unsigned long nlmsvc_grace_period = 0;
unsigned long nlmsvc_timeout = 0;
+static struct wait_queue * lockd_start = NULL;
+static struct wait_queue * lockd_exit = NULL;
+
/*
* Currently the following can be set only at insmod time.
* Ideally, they would be accessible through the sysctl interface.
sigset_t oldsigmask;
int err = 0;
- lock_kernel();
/* Lock module and set up kernel thread */
MOD_INC_USE_COUNT;
- /* exit_files(current); */
+ lock_kernel();
+
+ /*
+ * Let our maker know we're running.
+ */
+ nlmsvc_pid = current->pid;
+ wake_up(&lockd_start);
+
exit_mm(current);
current->session = 1;
current->pgrp = 1;
/* kick rpciod */
rpciod_up();
+ /*
+ * N.B. current do_fork() doesn't like NULL task->files,
+ * so we defer closing files until forking rpciod.
+ */
+ exit_files(current);
+
dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
if (!nlm_timeout)
nlmsvc_grace_period += jiffies;
nlmsvc_timeout = nlm_timeout * HZ;
- nlmsvc_pid = current->pid;
/*
* The main request loop. We don't terminate until the last
* NFS mount or NFS daemon has gone away, and we've been sent a
- * signal.
+ * signal, or else another process has taken over our job.
*/
- while (nlmsvc_sema || !signalled()) {
+ while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid)
+ {
if (signalled())
current->signal = 0;
nlmsvc_ops->exp_unlock();
}
- nlm_shutdown_hosts();
-
+ /*
+ * Check whether there's a new lockd process before
+ * shutting down the hosts and clearing the slot.
+ */
+ if (!nlmsvc_pid || current->pid == nlmsvc_pid) {
+ nlm_shutdown_hosts();
+ nlmsvc_pid = 0;
+ } else
+ printk("lockd: new process, skipping host shutdown\n");
+ wake_up(&lockd_exit);
+
/* Exit the RPC thread */
svc_exit_thread(rqstp);
/* Release module */
MOD_DEC_USE_COUNT;
- nlmsvc_pid = 0;
}
/*
return svc_create_socket(serv, protocol, &sin);
}
+/*
+ * Bring up the lockd process if it's not already up.
+ */
int
lockd_up(void)
{
struct svc_serv * serv;
- int error;
+ int error = 0;
- if (nlmsvc_pid || nlmsvc_sema++)
- return 0;
+ down(&nlmsvc_sema);
+ /*
+ * Unconditionally increment the user count ... this is
+ * the number of clients who _want_ a lockd process.
+ */
+ nlmsvc_users++;
+ /*
+ * Check whether we're already up and running.
+ */
+ if (nlmsvc_pid)
+ goto out;
- dprintk("lockd: creating service\n");
- if ((serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE)) == NULL)
- return -ENOMEM;
+ /*
+ * Sanity check: if there's no pid,
+ * we should be the first user ...
+ */
+ if (nlmsvc_users > 1)
+ printk("lockd_up: no pid, %d users??\n", nlmsvc_users);
+
+ error = -ENOMEM;
+ serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE);
+ if (!serv) {
+ printk("lockd_up: create service failed\n");
+ goto out;
+ }
- if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0
+ if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0
|| (error = lockd_makesock(serv, IPPROTO_TCP, 0)) < 0) {
- svc_destroy(serv);
- return error;
+ printk("lockd_up: makesock failed, error=%d\n", error);
+ goto destroy_and_out;
}
- if ((error = svc_create_thread(lockd, serv)) < 0)
- nlmsvc_sema--;
+ /*
+ * Create the kernel thread and wait for it to start.
+ */
+ error = svc_create_thread(lockd, serv);
+ if (error) {
+ printk("lockd_up: create thread failed, error=%d\n", error);
+ goto destroy_and_out;
+ }
+ sleep_on(&lockd_start);
- /* Release server */
+ /*
+ * Note: svc_serv structures have an initial use count of 1,
+ * so we exit through here on both success and failure.
+ */
+destroy_and_out:
svc_destroy(serv);
- return 0;
+out:
+ up(&nlmsvc_sema);
+ return error;
}
+/*
+ * Decrement the user count and bring down lockd if we're the last.
+ */
void
lockd_down(void)
{
- if (!nlmsvc_pid || --nlmsvc_sema > 0)
- return;
+ down(&nlmsvc_sema);
+ if (nlmsvc_users) {
+ if (--nlmsvc_users)
+ goto out;
+ } else
+ printk("lockd_down: no users! pid=%d\n", nlmsvc_pid);
+
+ if (!nlmsvc_pid) {
+ printk("lockd_down: nothing to do!\n");
+ goto out;
+ }
kill_proc(nlmsvc_pid, SIGKILL, 1);
- nlmsvc_sema = 0;
- nlmsvc_pid = 0;
+ /*
+ * Wait for the lockd process to exit, but since we're holding
+ * the lockd semaphore, we can't wait around forever ...
+ */
+ current->timeout = jiffies + 5 * HZ;
+ interruptible_sleep_on(&lockd_exit);
+ current->timeout = 0;
+ if (nlmsvc_pid) {
+ printk("lockd_down: lockd failed to exit, clearing pid\n");
+ nlmsvc_pid = 0;
+ }
+out:
+ up(&nlmsvc_sema);
}
#ifdef MODULE
int
init_module(void)
{
+ /* Init the static variables */
+ nlmsvc_sema = MUTEX;
+ nlmsvc_users = 0;
+ nlmsvc_pid = 0;
+ lockd_exit = NULL;
nlmxdr_init();
return 0;
}
if (caller->fl_type != F_UNLCK) {
repeat:
- error = -ERESTARTSYS;
- if (signal_pending(current))
- goto out;
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX))
continue;
error = -EDEADLK;
if (posix_locks_deadlock(caller, fl))
goto out;
+ error = -ERESTARTSYS;
+ if (signal_pending(current))
+ goto out;
locks_insert_block(fl, caller);
interruptible_sleep_on(&caller->fl_wait);
locks_delete_block(fl, caller);
result = d_lookup(parent, name);
if (!result) {
struct dentry * dentry = d_alloc(parent, name);
- int error = dir->i_op->lookup(dir, dentry);
- result = ERR_PTR(error);
- if (!error)
- result = dget(dentry->d_mounts);
- dput(dentry);
+ result = ERR_PTR(-ENOMEM);
+ if (dentry) {
+ int error = dir->i_op->lookup(dir, dentry);
+ result = ERR_PTR(error);
+ if (!error)
+ result = dget(dentry->d_mounts);
+ dput(dentry);
+ }
}
up(&dir->i_sem);
return result;
if (new_dir->d_inode->i_sb && new_dir->d_inode->i_sb->dq_op)
new_dir->d_inode->i_sb->dq_op->initialize(new_dir->d_inode, -1);
- error = old_dir->d_inode->i_op->rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry);
+ error = old_dir->d_inode->i_op->rename(old_dir->d_inode, old_dentry,
+ new_dir->d_inode, new_dentry);
exit_lock:
double_unlock(new_dir, old_dir);
#include <asm/segment.h> /* for fs functions */
+/* needed by smbfs as well ... move to dcache? */
+extern void nfs_renew_times(struct dentry *);
+extern void nfs_invalidate_dircache_sb(struct super_block *);
+#define NFS_PARANOIA 1
+
/*
* Head for a dircache entry. Currently still very simple; when
* the cache grows larger, we will need a LRU list.
struct nfs_dirent {
dev_t dev; /* device number */
ino_t ino; /* inode number */
- u32 cookie; /* cooke of first entry */
+ u32 cookie; /* cookie of first entry */
unsigned short valid : 1, /* data is valid */
locked : 1; /* entry locked */
unsigned int size; /* # of entries */
unsigned long age; /* last used */
unsigned long mtime; /* last attr stamp */
struct wait_queue * wait;
- struct nfs_entry * entry;
+ __u32 * entry; /* three __u32's per entry */
};
static int nfs_dir_open(struct inode * inode, struct file * file);
static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
+ struct inode *inode = filp->f_dentry->d_inode;
static struct wait_queue *readdir_wait = NULL;
struct wait_queue **waitp = NULL;
struct nfs_dirent *cache, *free;
- struct nfs_entry *entry;
unsigned long age, dead;
u32 cookie;
int ismydir, result;
int i, j, index = 0;
- struct inode *inode = filp->f_dentry->d_inode;
+ __u32 *entry;
+ char *name, *start;
dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino);
if (!inode || !S_ISDIR(inode->i_mode)) {
break;
}
for (j = 0; j < cache->size; j++) {
- /*
- dprintk("NFS: examing entry %.*s @%d\n",
- (int) cache->entry[j].length,
- cache->entry[j].name,
- cache->entry[j].cookie);
- */
- if (cache->entry[j].cookie != cookie)
+ __u32 *this_ent = cache->entry + j*3;
+
+ if (*(this_ent+1) != cookie)
continue;
if (j < cache->size - 1) {
- entry = cache->entry + (index = j + 1);
- } else if (cache->entry[j].eof) {
+ index = j + 1;
+ entry = this_ent + 3;
+ } else if (*(this_ent+2) >> 16) {
+ /* eof */
return 0;
}
break;
cache->dev = inode->i_dev;
cache->ino = inode->i_ino;
if (!cache->entry) {
- cache->entry = (struct nfs_entry *)
- get_free_page(GFP_KERNEL);
- if (!cache->entry) {
- result = -ENOMEM;
+ result = -ENOMEM;
+ cache->entry = (__u32 *) get_free_page(GFP_KERNEL);
+ if (!cache->entry)
goto done;
- }
}
result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
/*
* Yowza! We have a cache entry...
*/
+ start = (char *) cache->entry;
while (index < cache->size) {
- int nextpos = entry->cookie;
+ __u32 fileid = *entry++;
+ __u32 nextpos = *entry++; /* cookie */
+ __u32 length = *entry++;
+ /*
+ * Unpack the eof flag, offset, and length
+ */
+ result = length & (1 << 15); /* eof flag */
+ name = start + ((length >> 16) & 0xFFFF);
+ length &= 0x7FFF;
/*
dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry,
- (int) entry->length, entry->name, entry->length,
+ (int) length, name, length,
(unsigned int) filp->f_pos,
- entry->fileid, entry->eof);
+ fileid, result);
*/
- if (filldir(dirent, entry->name, entry->length, cookie, entry->fileid) < 0)
+ if (filldir(dirent, name, length, cookie, fileid) < 0)
break;
cookie = nextpos;
- if (nextpos != entry->cookie) {
- printk("nfs_readdir: shouldn't happen!\n");
- break;
- }
index++;
- entry++;
}
filp->f_pos = cookie;
result = 0;
}
/*
- * Invalidate dircache entries for inode
+ * Invalidate dircache entries for an inode.
*/
void
nfs_invalidate_dircache(struct inode *inode)
{
- struct nfs_dirent *cache;
+ struct nfs_dirent *cache = dircache;
dev_t dev = inode->i_dev;
ino_t ino = inode->i_ino;
int i;
dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino);
- for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
- if (!cache->locked && cache->dev == dev && cache->ino == ino)
- cache->valid = 0; /* brute force */
+ for (i = NFS_MAX_DIRCACHE; i--; cache++) {
+ if (cache->ino != ino)
+ continue;
+ if (cache->dev != dev)
+ continue;
+ if (cache->locked) {
+ printk("NFS: cache locked for %s/%ld\n",
+ kdevname(dev), ino);
+ continue;
+ }
+ cache->valid = 0; /* brute force */
}
}
/*
- * Free directory cache memory
- * Called from cleanup_module
+ * Invalidate the dircache for a super block (or all caches),
+ * and release the cache memory.
*/
void
-nfs_free_dircache(void)
+nfs_invalidate_dircache_sb(struct super_block *sb)
{
- struct nfs_dirent *cache;
+ struct nfs_dirent *cache = dircache;
int i;
+ int freed = 0;
- dfprintk(DIRCACHE, "NFS: freeing dircache\n");
- for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
- cache->valid = 0;
+ for (i = NFS_MAX_DIRCACHE; i--; cache++) {
+ if (sb && sb->s_dev != cache->dev)
+ continue;
if (cache->locked) {
- printk("nfs_kfree_cache: locked entry in dircache!\n");
+ printk("NFS: cache locked at umount %s\n",
+ (cache->entry ? "(lost a page!)" : ""));
continue;
}
- if (cache->entry)
+ cache->valid = 0; /* brute force */
+ if (cache->entry) {
free_page((unsigned long) cache->entry);
- cache->entry = NULL;
+ cache->entry = NULL;
+ freed++;
+ }
}
+#ifdef NFS_PARANOIA
+if (freed)
+printk("nfs_invalidate_dircache_sb: freed %d pages from %s\n",
+freed, kdevname(sb->s_dev));
+#endif
+}
+
+/*
+ * Free directory cache memory
+ * Called from cleanup_module
+ */
+void
+nfs_free_dircache(void)
+{
+ dfprintk(DIRCACHE, "NFS: freeing dircache\n");
+ nfs_invalidate_dircache_sb(NULL);
}
/*
unsigned long time = jiffies - dentry->d_time;
unsigned long max = 5*HZ;
- if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode))
- max = 10*HZ;
- return time < max;
+ if (dentry->d_inode) {
+ if (is_bad_inode(dentry->d_inode)) {
+#ifdef NFS_PARANOIA
+printk("nfs_lookup_validate: %s/%s has dud inode\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ goto bad;
+ }
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ max = 10*HZ;
+ }
+
+ return (time < max) || IS_ROOT(dentry);
+bad:
+ return 0;
}
static void nfs_silly_delete(struct dentry *);
static struct dentry_operations nfs_dentry_operations = {
- nfs_lookup_revalidate,
+ nfs_lookup_revalidate, /* d_validate(struct dentry *) */
0, /* d_hash */
0, /* d_compare */
- nfs_silly_delete,
+ nfs_silly_delete, /* d_delete(struct dentry *) */
};
+/*
+ * Whenever a lookup succeeds, we know the parent directories
+ * are all valid, so we want to update the dentry timestamps.
+ */
+void nfs_renew_times(struct dentry * dentry)
+{
+ for (;;) {
+ dentry->d_time = jiffies;
+ if (dentry == dentry->d_parent)
+ break;
+ dentry = dentry->d_parent;
+ }
+}
+
static int nfs_lookup(struct inode *dir, struct dentry * dentry)
{
struct inode *inode;
if (len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &fhandle, &fattr);
-
+ error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
+ dentry->d_name.name, &fhandle, &fattr);
inode = NULL;
+ if (error == -ENOENT)
+ goto no_entry;
if (!error) {
+ error = -EACCES;
inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
- if (!inode)
- return -EACCES;
- } else if (error != -ENOENT)
- return error;
-
- dentry->d_time = jiffies;
- dentry->d_op = &nfs_dentry_operations;
- d_add(dentry, inode);
- return 0;
+ if (inode) {
+ no_entry:
+ dentry->d_op = &nfs_dentry_operations;
+ d_add(dentry, inode);
+ nfs_renew_times(dentry);
+ error = 0;
+ }
+ }
+ return error;
+}
+
+/*
+ * Code common to create, mkdir, and mknod.
+ */
+static int nfs_instantiate(struct inode *dir, struct dentry *dentry,
+ struct nfs_fattr *fattr, struct nfs_fh *fhandle)
+{
+ struct inode *inode;
+ int error = -EACCES;
+
+ inode = nfs_fhget(dir->i_sb, fhandle, fattr);
+ if (inode) {
+ nfs_invalidate_dircache(dir);
+ d_instantiate(dentry, inode);
+ nfs_renew_times(dentry);
+ error = 0;
+ }
+ return error;
}
static int nfs_create(struct inode *dir, struct dentry * dentry, int mode)
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
- struct inode *inode;
int error;
dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
- dentry->d_name.name, &sattr, &fhandle, &fattr);
-
- if (error)
- return error;
-
- inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
- if (!inode)
- return -EACCES;
-
- nfs_invalidate_dircache(dir);
- d_instantiate(dentry, inode);
- return 0;
+ dentry->d_name.name, &sattr, &fhandle, &fattr);
+ if (!error)
+ error = nfs_instantiate(dir, dentry, &fattr, &fhandle);
+ return error;
}
static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
- struct inode *inode;
int error;
dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
- dentry->d_name.name, &sattr, &fhandle, &fattr);
-
- if (error)
- return error;
-
- inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
- if (!inode)
- return -EACCES;
-
- nfs_invalidate_dircache(dir);
- d_instantiate(dentry, inode);
- return 0;
+ dentry->d_name.name, &sattr, &fhandle, &fattr);
+ if (!error)
+ error = nfs_instantiate(dir, dentry, &fattr, &fhandle);
+ return error;
}
static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
- struct inode * inode;
int error;
dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
- dentry->d_name.name, &sattr, &fhandle, &fattr);
-
- if (error)
- return error;
-
- inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
- if (!inode)
- return -EACCES;
-
- nfs_invalidate_dircache(dir);
- d_instantiate(dentry, inode);
- return 0;
+ dentry->d_name.name, &sattr, &fhandle, &fattr);
+ if (!error)
+ error = nfs_instantiate(dir, dentry, &fattr, &fhandle);
+ return error;
}
+/*
+ * Update inode->i_nlink immediately after a successful operation.
+ * (See comments for nfs_unlink.)
+ */
static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
{
int error;
return -ENAMETOOLONG;
error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name);
- if (error)
- return error;
-
- nfs_invalidate_dircache(dir);
- d_delete(dentry);
- return 0;
+ if (!error) {
+ if (dentry->d_inode->i_nlink)
+ dentry->d_inode->i_nlink --;
+ nfs_invalidate_dircache(dir);
+ nfs_renew_times(dentry);
+ d_delete(dentry);
+ }
+ return error;
}
error = nfs_proc_rename(NFS_SERVER(dir),
NFS_FH(dir), dentry->d_name.name,
NFS_FH(dir), silly);
- if (error) {
- dput(sdentry);
- return error;
+ if (!error) {
+ nfs_invalidate_dircache(dir);
+ nfs_renew_times(dentry);
+ d_move(dentry, sdentry);
+ dentry->d_flags |= DCACHE_NFSFS_RENAMED;
+ /* If we return 0 we don't unlink */
}
- nfs_invalidate_dircache(dir);
- d_move(dentry, sdentry);
dput(sdentry);
- dentry->d_flags |= DCACHE_NFSFS_RENAMED;
-
- return 0; /* don't unlink */
+ return error;
}
static void nfs_silly_delete(struct dentry *dentry)
if (error < 0)
printk("NFS " __FUNCTION__ " failed (err = %d)\n",
-error);
- dentry->d_inode->i_nlink --;
+ if (dentry->d_inode) {
+ if (dentry->d_inode->i_nlink)
+ dentry->d_inode->i_nlink --;
+ } else
+ printk("nfs_silly_delete: negative dentry %s/%s\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
nfs_invalidate_dircache(dir);
+ /*
+ * The dentry is unhashed, but we want to make it negative.
+ */
+ d_delete(dentry);
}
}
*
* If sillyrename() returns 0, we do nothing, otherwise we unlink.
*
- * inode->i_nlink is updated here rather than waiting for the next
- * nfs_refresh_inode() for cosmetic reasons only.
+ * Updating inode->i_nlink here rather than waiting for the next
+ * nfs_refresh_inode() is not merely cosmetic; once an object has
+ * been deleted, we want to get rid of the inode locally. The NFS
+ * server may reuse the fileid for a new inode, and we don't want
+ * that to be confused with this inode.
*/
static int nfs_unlink(struct inode *dir, struct dentry *dentry)
{
error = nfs_sillyrename(dir, dentry);
- if (error == -EBUSY) {
- return -EBUSY;
- } else if (error < 0) {
+ if (error && error != -EBUSY) {
error = nfs_proc_remove(NFS_SERVER(dir),
NFS_FH(dir), dentry->d_name.name);
- if (error < 0)
- return error;
-
- dentry->d_inode->i_nlink --;
- nfs_invalidate_dircache(dir);
- d_delete(dentry);
+ if (!error) {
+ if (dentry->d_inode->i_nlink)
+ dentry->d_inode->i_nlink --;
+ nfs_invalidate_dircache(dir);
+ nfs_renew_times(dentry);
+ d_delete(dentry);
+ }
}
- return 0;
+ return error;
}
static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
- dentry->d_name.name, symname, &sattr);
-
- if (error)
- return error;
-
- nfs_invalidate_dircache(dir);
- /* this looks _funny_ doesn't it? But: nfs_proc_symlink()
- * only fills in sattr, not fattr. Thus nfs_fhget() cannot be
- * called, it would be pointless, without a valid fattr
- * argument. Other possibility: call nfs_proc_lookup()
- * HERE. But why? If somebody wants to reference this
- * symlink, the cached_lookup() will fail, and
- * nfs_proc_symlink() will be called anyway.
- */
- d_drop(dentry);
- return 0;
+ dentry->d_name.name, symname, &sattr);
+ if (!error) {
+ nfs_invalidate_dircache(dir);
+ nfs_renew_times(dentry->d_parent);
+ /* this looks _funny_ doesn't it? But: nfs_proc_symlink()
+ * only fills in sattr, not fattr. Thus nfs_fhget() cannot be
+ * called, it would be pointless, without a valid fattr
+ * argument. Other possibility: call nfs_proc_lookup()
+ * HERE. But why? If somebody wants to reference this
+ * symlink, the cached_lookup() will fail, and
+ * nfs_proc_symlink() will be called anyway.
+ */
+ d_drop(dentry);
+ }
+ return error;
}
static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode),
- NFS_FH(dir), dentry->d_name.name);
-
- if (error)
- return error;
-
- nfs_invalidate_dircache(dir);
- inode->i_count ++;
- inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */
- d_instantiate(dentry, inode);
- return 0;
+ error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), NFS_FH(dir),
+ dentry->d_name.name);
+ if (!error) {
+ nfs_invalidate_dircache(dir);
+ inode->i_count ++;
+ inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */
+ d_instantiate(dentry, inode);
+ error = 0;
+ }
+ return error;
}
/*
error = nfs_proc_rename(NFS_SERVER(old_dir),
NFS_FH(old_dir), old_dentry->d_name.name,
NFS_FH(new_dir), new_dentry->d_name.name);
- if (error)
- return error;
-
- nfs_invalidate_dircache(old_dir);
- nfs_invalidate_dircache(new_dir);
+ if (!error) {
+ nfs_invalidate_dircache(old_dir);
+ nfs_invalidate_dircache(new_dir);
+ /*
+ * We know these paths are still valid ...
+ */
+ nfs_renew_times(old_dentry);
+ nfs_renew_times(new_dentry->d_parent);
- /* Update the dcache */
- d_move(old_dentry, new_dentry);
- return 0;
+ /* Update the dcache */
+ d_move(old_dentry, new_dentry);
+ }
+ return error;
}
/*
* of the server's inode.
*/
-void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
- int was_empty;
+ int error = -EIO;
dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
inode->i_dev, inode->i_ino, inode->i_count);
if (!inode || !fattr) {
printk("nfs_refresh_inode: inode or fattr is NULL\n");
- return;
+ goto out;
}
if (inode->i_ino != fattr->fileid) {
printk("nfs_refresh_inode: inode number mismatch\n");
- return;
+ goto out;
}
- was_empty = (inode->i_mode == 0);
- inode->i_mode = fattr->mode;
+ /*
+ * Check whether the mode has been set, as we only want to
+ * do this once. (We don't allow inodes to change types.)
+ */
+ if (inode->i_mode == 0) {
+ inode->i_mode = fattr->mode;
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &nfs_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &nfs_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &nfs_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode)) {
+ inode->i_op = &chrdev_inode_operations;
+ inode->i_rdev = to_kdev_t(fattr->rdev);
+ } else if (S_ISBLK(inode->i_mode)) {
+ inode->i_op = &blkdev_inode_operations;
+ inode->i_rdev = to_kdev_t(fattr->rdev);
+ } else if (S_ISFIFO(inode->i_mode))
+ init_fifo(inode);
+ else
+ inode->i_op = NULL;
+ } else if ((inode->i_mode & S_IFMT) == (fattr->mode & S_IFMT)) {
+ inode->i_mode = fattr->mode;
+ } else {
+ mode_t old_mode;
+ /*
+ * Big trouble! The inode has become a different object.
+ */
+#if 1
+printk("nfs_refresh_inode: mode changed, %07o to %07o\n",
+inode->i_mode, fattr->mode);
+#endif
+ old_mode = inode->i_mode; /* save mode */
+ make_bad_inode(inode);
+ inode->i_mode = old_mode; /* restore mode */
+ inode->i_nlink = 0;
+ /*
+ * No need to worry about unhashing the dentry, as the
+ * lookup validation will know that the inode is bad.
+ * (But we do want to invalidate the caches.)
+ */
+ invalidate_inode_pages(inode);
+ nfs_invalidate_dircache(inode);
+ goto out;
+ }
+
inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid;
inode->i_gid = fattr->gid;
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
}
inode->i_size = fattr->size;
- if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = to_kdev_t(fattr->rdev);
- else
- inode->i_rdev = 0;
inode->i_blocks = fattr->blocks;
inode->i_atime = fattr->atime.seconds;
inode->i_mtime = fattr->mtime.seconds;
inode->i_ctime = fattr->ctime.seconds;
- if (S_ISREG(inode->i_mode))
- inode->i_op = &nfs_file_inode_operations;
- else if (S_ISDIR(inode->i_mode))
- inode->i_op = &nfs_dir_inode_operations;
- else if (S_ISLNK(inode->i_mode))
- inode->i_op = &nfs_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode)) {
- if (was_empty)
- init_fifo(inode);
- } else
- inode->i_op = NULL;
+ error = 0;
+out:
+ return error;
}
/*
#define NFSDBG_FACILITY NFSDBG_VFS
+extern void nfs_invalidate_dircache_sb(struct super_block *);
+
static int nfs_notify_change(struct inode *, struct iattr *);
static void nfs_put_inode(struct inode *);
static void nfs_delete_inode(struct inode *);
{
inode->i_blksize = inode->i_sb->s_blocksize;
inode->i_mode = 0;
+ inode->i_rdev = 0;
inode->i_op = NULL;
NFS_CACHEINV(inode);
}
nfs_put_inode(struct inode * inode)
{
dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ /*
+ * We want to get rid of unused inodes ...
+ */
+ if (inode->i_count == 1)
+ inode->i_nlink = 0;
}
static void
struct nfs_server *server = &sb->u.nfs_sb.s_server;
struct rpc_clnt *rpc;
+ /*
+ * Lock the super block while we bring down the daemons.
+ */
+ lock_super(sb);
if ((rpc = server->client) != NULL)
rpc_shutdown_client(rpc);
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */
rpciod_down(); /* release rpciod */
- lock_super(sb);
+ /*
+ * Invalidate the dircache for this superblock.
+ */
+ nfs_invalidate_dircache_sb(sb);
+
sb->s_dev = 0;
unlock_super(sb);
MOD_DEC_USE_COUNT;
unsigned int authflavor;
int tcp;
kdev_t dev = sb->s_dev;
+ struct inode *root_inode;
MOD_INC_USE_COUNT;
- if (!data) {
- printk("nfs_read_super: missing data argument\n");
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ if (!data)
+ goto out_miss_args;
+
if (data->version != NFS_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n",
data->version < NFS_MOUNT_VERSION ? "older" : "newer");
data->bsize = 0;
}
+ /* We now require that the mount process passes the remote address */
+ memcpy(&srvaddr, &data->addr, sizeof(srvaddr));
+ if (srvaddr.sin_addr.s_addr == INADDR_ANY)
+ goto out_no_remote;
+
lock_super(sb);
- server = &sb->u.nfs_sb.s_server;
sb->s_magic = NFS_SUPER_MAGIC;
sb->s_dev = dev;
sb->s_op = &nfs_sops;
sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
+ sb->u.nfs_sb.s_root = data->root;
+ server = &sb->u.nfs_sb.s_server;
server->rsize = nfs_block_size(data->rsize, NULL);
server->wsize = nfs_block_size(data->wsize, NULL);
server->flags = data->flags;
server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ;
strcpy(server->hostname, data->hostname);
- sb->u.nfs_sb.s_root = data->root;
-
- /* We now require that the mount process passes the remote address */
- memcpy(&srvaddr, &data->addr, sizeof(srvaddr));
- if (srvaddr.sin_addr.s_addr == INADDR_ANY) {
- printk("NFS: mount program didn't pass remote address!\n");
- MOD_DEC_USE_COUNT;
- return NULL;
- }
/* Which protocol do we use? */
tcp = (data->flags & NFS_MOUNT_TCP);
/* Now create transport and client */
xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
&srvaddr, &timeparms);
- if (xprt == NULL) {
- printk("NFS: cannot create RPC transport.\n");
- goto failure;
- }
+ if (xprt == NULL)
+ goto out_no_xprt;
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
NFS_VERSION, authflavor);
- if (clnt == NULL) {
- printk("NFS: cannot create RPC client.\n");
- xprt_destroy(xprt);
- goto failure;
- }
+ if (clnt == NULL)
+ goto out_no_client;
clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0;
clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0;
server->client = clnt;
/* Fire up rpciod if not yet running */
+#ifdef RPCIOD_RESULT
+ if (rpciod_up())
+ goto out_no_iod;
+#else
rpciod_up();
+#endif
- /* Unlock super block and try to get root fh attributes */
+ /*
+ * Keep the super block locked while we try to get
+ * the root fh attributes.
+ */
+ root_inode = nfs_fhget(sb, &data->root, NULL);
+ if (!root_inode)
+ goto out_no_root;
+ sb->s_root = d_alloc_root(root_inode, NULL);
+ if (!sb->s_root)
+ goto out_no_root;
+ /* We're airborne */
unlock_super(sb);
- sb->s_root = d_alloc_root(nfs_fhget(sb, &data->root, NULL), NULL);
- if (sb->s_root != NULL) {
- /* We're airborne */
- if (!(server->flags & NFS_MOUNT_NONLM))
- lockd_up();
- return sb;
- }
+ /* Check whether to start the lockd process */
+ if (!(server->flags & NFS_MOUNT_NONLM))
+ lockd_up();
+ return sb;
/* Yargs. It didn't work out. */
+out_no_root:
printk("nfs_read_super: get root inode failed\n");
- rpc_shutdown_client(server->client);
+ iput(root_inode);
rpciod_down();
+#ifdef RPCIOD_RESULT
+ goto out_shutdown;
-failure:
- MOD_DEC_USE_COUNT;
- if (sb->s_lock)
- unlock_super(sb);
+out_no_iod:
+ printk("nfs_read_super: couldn't start rpciod!\n");
+out_shutdown:
+#endif
+ rpc_shutdown_client(server->client);
+ goto out_unlock;
+
+out_no_client:
+ printk("NFS: cannot create RPC client.\n");
+ xprt_destroy(xprt);
+ goto out_unlock;
+
+out_no_xprt:
+ printk("NFS: cannot create RPC transport.\n");
+out_unlock:
+ unlock_super(sb);
+ goto out_fail;
+
+out_no_remote:
+ printk("NFS: mount program didn't pass remote address!\n");
+ goto out_fail;
+
+out_miss_args:
+ printk("nfs_read_super: missing data argument\n");
+
+out_fail:
sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
return NULL;
}
#include <linux/nfs_fs.h>
#define NFSDBG_FACILITY NFSDBG_XDR
+/* #define NFS_PARANOIA 1 */
#define QUADLEN(len) (((len) + 3) >> 2)
static int nfs_stat_to_errno(int stat);
* to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
* After decoding, the layout in memory looks like this:
* entry1 entry2 ... entryN <space> stringN ... string2 string1
+ * Each entry consists of three __u32 values, the same space as NFS uses.
* Note that the strings are not null-terminated so that the entire number
* of entries returned by the server should fit into the buffer.
*/
static int
nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
{
- struct nfs_entry *entry;
struct iovec *iov = req->rq_rvec;
int status, nr, len;
- char *string;
+ char *string, *start;
u32 *end;
+ __u32 fileid, cookie, *entry;
if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status);
end = (u32 *) ((u8 *) p + iov[1].iov_len);
/* Get start and end of dirent buffer */
- entry = (struct nfs_entry *) res->buffer;
+ entry = (__u32 *) res->buffer;
+ start = (char *) res->buffer;
string = (char *) res->buffer + res->bufsiz;
- for (nr = 0; *p++; nr++, entry++) {
- entry->fileid = ntohl(*p++);
+ for (nr = 0; *p++; nr++) {
+ fileid = ntohl(*p++);
len = ntohl(*p++);
if ((p + QUADLEN(len) + 3) > end) {
return -errno_NFSERR_IO;
}
string -= len;
- if ((void *) (entry+1) > (void *) string) {
- /* This may actually happen because an nfs_entry
- * will take up more space than the XDR data. On
- * 32bit machines that's due to 8byte alignment,
- * on 64bit machines that's because the char * takes
- * up 2 longs.
- *
- * THIS IS BAD!
+ if ((void *) (entry+3) > (void *) string) {
+ /*
+ * This error is impossible as long as the temp
+ * buffer is no larger than the user buffer. The
+ * current packing algorithm uses the same amount
+ * of space in the user buffer as in the XDR data,
+ * so it's guaranteed to fit.
*/
- printk(KERN_NOTICE "NFS: should not happen in %s!\n",
+ printk("NFS: incorrect buffer size in %s!\n",
__FUNCTION__);
break;
}
- entry->name = string;
- entry->length = len;
memmove(string, p, len);
p += QUADLEN(len);
- entry->cookie = ntohl(*p++);
- entry->eof = !p[0] && p[1];
+ cookie = ntohl(*p++);
+ /*
+ * To make everything fit, we encode the length, offset,
+ * and eof flag into 32 bits. This works for filenames
+ * up to 32K and PAGE_SIZE up to 64K.
+ */
+ status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
+ *entry++ = fileid;
+ *entry++ = cookie;
+ *entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
}
+#ifdef NFS_PARANOIA
+printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
+nr, ((char *) entry - start), (start + res->bufsiz - string));
+#endif
return nr;
}
* to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
* After decoding, the layout in memory looks like this:
* entry1 entry2 ... entryN <space> stringN ... string2 string1
+ * Each entry consists of three __u32 values, the same space as NFS uses.
* Note that the strings are not null-terminated so that the entire number
* of entries returned by the server should fit into the buffer.
*/
static int
nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
{
- struct nfs_entry *entry;
struct iovec *iov = req->rq_rvec;
int status, nr, len;
- char *string;
+ char *string, *start;
u32 *end;
+ __u32 fileid, cookie, *entry;
if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status);
return -errno_NFSERR_IO;
}
- string = (char *) res->buffer + res->bufsiz;
- entry = (struct nfs_entry *) res->buffer;
- for (nr = 0; *p++; nr++, entry++) {
- entry->fileid = ntohl(*p++);
+ entry = (__u32 *) res->buffer;
+ start = (char *) res->buffer;
+ string = start + res->bufsiz;
+ for (nr = 0; *p++; nr++) {
+ fileid = ntohl(*p++);
len = ntohl(*p++);
if ((p + QUADLEN(len) + 3) > end) {
return -errno_NFSERR_IO;
}
string -= len;
- if ((void *) (entry+1) > (void *) string) {
- dprintk("NFS: shouldnothappen in readdirres_decode!\n");
- break; /* should not happen */
+ if ((void *) (entry+3) > (void *) string) {
+ /*
+ * This error is impossible as long as the temp
+ * buffer is no larger than the user buffer. The
+ * current packing algorithm uses the same amount
+ * of space in the user buffer as in the XDR data,
+ * so it's guaranteed to fit.
+ */
+ printk("NFS: incorrect buffer size in %s!\n",
+ __FUNCTION__);
+ break;
}
- entry->name = string;
- entry->length = len;
memmove(string, p, len);
p += QUADLEN(len);
- entry->cookie = ntohl(*p++);
- entry->eof = !p[0] && p[1];
+ cookie = ntohl(*p++);
+ /*
+ * To make everything fit, we encode the length, offset,
+ * and eof flag into 32 bits. This works for filenames
+ * up to 32K and PAGE_SIZE up to 64K.
+ */
+ status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
+ *entry++ = fileid;
+ *entry++ = cookie;
+ *entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
/*
dprintk("NFS: decoded dirent %.*s cookie %d eof %d\n",
- len, string, entry->cookie, entry->eof);
+ len, string, cookie, status);
*/
}
+#ifdef NFS_PARANOIA
+printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
+nr, ((char *) entry - start), (start + res->bufsiz - string));
+#endif
return nr;
}
*/
int
nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
- u32 cookie, unsigned int size, struct nfs_entry *entry)
+ u32 cookie, unsigned int size, __u32 *entry)
{
struct nfs_readdirargs arg;
struct nfs_readdirres res;
void * buffer;
+ unsigned int buf_size = PAGE_SIZE;
int status;
/* First get a temp buffer for the readdir reply */
- while (!(buffer = (void *) get_free_page(GFP_USER))) {
- need_resched = 1;
- schedule();
- if (signalled())
- return -ERESTARTSYS;
- }
+ /* N.B. does this really need to be cleared? */
+ status = -ENOMEM;
+ buffer = (void *) get_free_page(GFP_KERNEL);
+ if (!buffer)
+ goto out;
+
+ /*
+ * Calculate the effective size the buffer. To make sure
+ * that the returned data will fit into the user's buffer,
+ * we decrease the buffer size as necessary.
+ *
+ * Note: NFS returns three __u32 values for each entry,
+ * and we assume that the data is packed into the user
+ * buffer with the same efficiency.
+ */
+ if (size < buf_size)
+ buf_size = size;
+ if (server->rsize < buf_size)
+ buf_size = server->rsize;
+#if 0
+printk("nfs_proc_readdir: user size=%d, rsize=%d, buf_size=%d\n",
+size, server->rsize, buf_size);
+#endif
arg.fh = fhandle;
arg.cookie = cookie;
arg.buffer = buffer;
- arg.bufsiz = server->rsize < PAGE_SIZE? server->rsize : PAGE_SIZE;
+ arg.bufsiz = buf_size;
res.buffer = entry;
res.bufsiz = size;
status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0);
dprintk("NFS reply readdir: %d\n", status);
free_page((unsigned long) buffer);
+out:
return status;
}
if (!p || !p->mm || ptr >= TASK_SIZE)
return 0;
+ /* Check for NULL pgd .. shouldn't happen! */
+ if (!p->mm->pgd) {
+ printk("get_phys_addr: pid %d has NULL pgd!\n", p->pid);
+ return 0;
+ }
+
page_dir = pgd_offset(p->mm,ptr);
if (pgd_none(*page_dir))
return 0;
#define MAPS_LINE_MAX MAPS_LINE_MAX8
-static long read_maps (int pid, struct file * file,
- char * buf, unsigned long count)
+static long read_maps (int pid, struct file * file, char * buf,
+ unsigned long count)
{
- struct task_struct *p = find_task_by_pid(pid);
- char * destptr;
+ struct task_struct *p;
+ struct vm_area_struct * map, * next;
+ char * destptr = buf, * buffer;
loff_t lineno;
- int column;
- struct vm_area_struct * map;
- int i;
- char * buffer;
+ int column, i, volatile_task;
+ long retval;
+
+ /*
+ * We might sleep getting the page, so get it first.
+ */
+ retval = -ENOMEM;
+ buffer = (char*)__get_free_page(GFP_KERNEL);
+ if (!buffer)
+ goto out;
+ retval = -EINVAL;
+ p = find_task_by_pid(pid);
if (!p)
- return -EINVAL;
+ goto freepage_out;
if (!p->mm || p->mm == &init_mm || count == 0)
- return 0;
+ goto getlen_out;
- buffer = (char*)__get_free_page(GFP_KERNEL);
+ /* Check whether the mmaps could change if we sleep */
+ volatile_task = (p != current || p->mm->count > 1);
/* decode f_pos */
lineno = file->f_pos >> MAPS_LINE_SHIFT;
for (map = p->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++)
continue;
- destptr = buf;
-
- for ( ; map ; ) {
+ for ( ; map ; map = next ) {
/* produce the next line */
char *line;
char str[5], *cp = str;
MAPS_LINE_MAX4 : MAPS_LINE_MAX8;
int len;
+ /*
+ * Get the next vma now (but it won't be used if we sleep).
+ */
+ next = map->vm_next;
flags = map->vm_flags;
*cp++ = flags & VM_READ ? 'r' : '-';
if (column >= len) {
column = 0; /* continue with next line at column 0 */
lineno++;
- map = map->vm_next;
- continue;
+ continue; /* we haven't slept */
}
i = len-column;
if (i > count)
i = count;
- copy_to_user(destptr, line+column, i);
- destptr += i; count -= i;
- column += i;
+ copy_to_user(destptr, line+column, i); /* may have slept */
+ destptr += i;
+ count -= i;
+ column += i;
if (column >= len) {
column = 0; /* next time: next line at column 0 */
lineno++;
- map = map->vm_next;
}
/* done? */
/* By writing to user space, we might have slept.
* Stop the loop, to avoid a race condition.
*/
- if (p != current)
+ if (volatile_task)
break;
}
/* encode f_pos */
file->f_pos = (lineno << MAPS_LINE_SHIFT) + column;
+getlen_out:
+ retval = destptr - buf;
+
+freepage_out:
free_page((unsigned long)buffer);
- return destptr-buf;
+out:
+ return retval;
}
#ifdef CONFIG_MODULES
NULL /* permission */
};
-static void proc_pid_fill_inode(struct inode * inode)
+/*
+ * The fill argument is non-zero when the inode is being filled ...
+ * we don't need to do anything when it's being deleted.
+ */
+static void proc_pid_fill_inode(struct inode * inode, int fill)
{
struct task_struct *p;
int pid = inode->i_ino >> 16;
int ino = inode->i_ino & 0xffff;
- if ((p = find_task_by_pid(pid)) != NULL) {
+ if (fill && (p = find_task_by_pid(pid)) != NULL) {
if (p->dumpable || ino == PROC_PID_INO) {
inode->i_uid = p->euid;
inode->i_gid = p->gid;
#include <linux/stat.h>
#include <asm/bitops.h>
+extern struct inode_operations proc_dyna_dir_inode_operations;
+
static long proc_file_read(struct inode * inode, struct file * file,
char * buf, unsigned long nbytes);
static long proc_file_write(struct inode * inode, struct file * file,
const char * buffer, unsigned long count);
-static long long proc_file_lseek(struct file * file, long long offset, int orig);
+static long long proc_file_lseek(struct file *, long long, int);
int proc_match(int len, const char *name,struct proc_dir_entry * de)
{
NULL /* can't fsync */
};
-/*
- * proc files can do almost nothing..
- */
struct inode_operations proc_file_inode_operations = {
- &proc_file_operations, /* default proc file-ops */
- NULL, /* create */
- NULL, /* lookup */
- NULL, /* link */
- NULL, /* unlink */
- NULL, /* symlink */
- NULL, /* mkdir */
+ &proc_file_operations, /* default proc file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
- struct proc_dir_entry *ent;
- const char *fn;
-
- if (parent)
- fn = name;
- else {
- if (xlate_proc_name(name, &parent, &fn))
- return NULL;
- }
+ struct proc_dir_entry *ent = NULL;
+ const char *fn = name;
+ int len;
+
+ if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+ goto out;
+ len = strlen(fn);
- ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+ ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
if (!ent)
- return NULL;
+ goto out;
memset(ent, 0, sizeof(struct proc_dir_entry));
+ memcpy(((char *) ent) + sizeof(*ent), fn, len + 1);
+ ent->name = ((char *) ent) + sizeof(*ent);
+ ent->namelen = len;
- if (mode == S_IFDIR)
+ if (mode == S_IFDIR) {
mode |= S_IRUGO | S_IXUGO;
- else if (mode == 0)
- mode = S_IFREG | S_IRUGO;
-
- ent->name = fn;
- ent->namelen = strlen(fn);
- ent->mode = mode;
- if (S_ISDIR(mode))
+ ent->ops = &proc_dyna_dir_inode_operations;
ent->nlink = 2;
- else
+ }
+ else if (mode == 0) {
+ mode = S_IFREG | S_IRUGO;
ent->nlink = 1;
+ }
+ ent->mode = mode;
proc_register(parent, ent);
+out:
return ent;
}
+extern void free_proc_entry(struct proc_dir_entry *);
+void free_proc_entry(struct proc_dir_entry *de)
+{
+ kfree(de);
+}
+
+/*
+ * Remove a /proc entry and free it if it's not currently in use.
+ * If it is in use, we set the 'deleted' flag.
+ */
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
{
struct proc_dir_entry *de;
- const char *fn;
+ const char *fn = name;
int len;
- if (parent)
- fn = name;
- else
- if (xlate_proc_name(name, &parent, &fn))
- return;
+ if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+ goto out;
len = strlen(fn);
for (de = parent->subdir; de ; de = de->next) {
if (proc_match(len, fn, de))
break;
}
- if (de)
+
+ if (de) {
+printk("remove_proc_entry: parent nlink=%d, file nlink=%d\n",
+parent->nlink, de->nlink);
proc_unregister(parent, de->low_ino);
- kfree(de);
+ de->nlink = 0;
+ de->deleted = 1;
+ if (!de->count)
+ free_proc_entry(de);
+ else {
+ printk("remove_proc_entry: %s/%s busy, count=%d\n",
+ parent->name, de->name, de->count);
+ }
+ }
+out:
+ return;
}
#include <asm/system.h>
#include <asm/uaccess.h>
+extern void free_proc_entry(struct proc_dir_entry *);
+
+struct proc_dir_entry * de_get(struct proc_dir_entry *de)
+{
+ if (de)
+ de->count++;
+ return de;
+}
+
+/*
+ * Decrements the use count and checks for deferred deletion.
+ */
+void de_put(struct proc_dir_entry *de)
+{
+ if (de) {
+ if (!de->count) {
+ printk("de_put: entry %s already free!\n", de->name);
+ return;
+ }
+
+ if (!--de->count) {
+ if (de->deleted) {
+ printk("de_put: deferred delete of %s\n",
+ de->name);
+ free_proc_entry(de);
+ }
+ }
+ }
+}
+
static void proc_put_inode(struct inode *inode)
{
#ifdef CONFIG_SUN_OPENPROMFS_MODULE
- if ((inode->i_ino >= PROC_OPENPROM_FIRST)
- && (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM)
- && proc_openprom_use)
+ if ((inode->i_ino >= PROC_OPENPROM_FIRST) &&
+ (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) &&
+ proc_openprom_use)
(*proc_openprom_use)(inode, 0);
#endif
+ /*
+ * Kill off unused inodes ... VFS will unhash and
+ * delete the inode if we set i_nlink to zero.
+ */
+ if (inode->i_count == 1)
+ inode->i_nlink = 0;
}
/*
- * Does this ever happen?
+ * Decrement the use count of the proc_dir_entry.
*/
static void proc_delete_inode(struct inode *inode)
{
- printk("proc_delete_inode()?\n");
- inode->i_size = 0;
+ struct proc_dir_entry *de = inode->u.generic_ip;
+ if (de) {
+ /*
+ * Call the fill_inode hook to release module counts.
+ */
+ if (de->fill_inode)
+ de->fill_inode(inode, 0);
+ de_put(de);
+ }
}
static void proc_put_super(struct super_block *sb)
proc_read_inode,
proc_write_inode,
proc_put_inode,
- proc_delete_inode,
+ proc_delete_inode, /* delete_inode(struct inode *) */
NULL,
proc_put_super,
NULL,
return 1;
}
-struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_entry * de)
+struct inode * proc_get_inode(struct super_block * sb, int ino,
+ struct proc_dir_entry * de)
{
- struct inode * inode = iget(s, ino);
+ struct inode * inode;
+
+ /*
+ * Increment the use count so the dir entry can't disappear.
+ */
+ de_get(de);
+#if 1
+/* shouldn't ever happen */
+if (de && de->deleted)
+printk("proc_iget: using deleted entry %s, count=%d\n", de->name, de->count);
+#endif
+
+ inode = iget(sb, ino);
+ if (!inode)
+ goto out_fail;
#ifdef CONFIG_SUN_OPENPROMFS_MODULE
if ((inode->i_ino >= PROC_OPENPROM_FIRST)
&& proc_openprom_use)
(*proc_openprom_use)(inode, 1);
#endif
- if (inode && inode->i_sb == s) {
- inode->u.generic_ip = (void *) de;
- if (de) {
- if (de->mode) {
- inode->i_mode = de->mode;
- inode->i_uid = de->uid;
- inode->i_gid = de->gid;
- }
- if (de->size)
- inode->i_size = de->size;
- if (de->ops)
- inode->i_op = de->ops;
- if (de->nlink)
- inode->i_nlink = de->nlink;
- if (de->fill_inode)
- de->fill_inode(inode);
+ /* N.B. How can this test ever fail?? */
+ if (inode->i_sb != sb)
+ printk("proc_get_inode: inode fubar\n");
+
+ inode->u.generic_ip = (void *) de;
+ if (de) {
+ if (de->mode) {
+ inode->i_mode = de->mode;
+ inode->i_uid = de->uid;
+ inode->i_gid = de->gid;
}
+ if (de->size)
+ inode->i_size = de->size;
+ if (de->ops)
+ inode->i_op = de->ops;
+ if (de->nlink)
+ inode->i_nlink = de->nlink;
+ /*
+ * The fill_inode routine should use this call
+ * to increment module counts, if necessary.
+ */
+ if (de->fill_inode)
+ de->fill_inode(inode, 1);
}
/*
* Fixup the root inode's nlink value
}
read_unlock(&tasklist_lock);
}
+out:
return inode;
+
+out_fail:
+ de_put(de);
+ goto out;
}
struct super_block *proc_read_super(struct super_block *s,void *data,
int silent)
{
+ struct inode * root_inode;
+
lock_super(s);
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = PROC_SUPER_MAGIC;
s->s_op = &proc_sops;
+ root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
+ if (!root_inode)
+ goto out_no_root;
+ s->s_root = d_alloc_root(root_inode, NULL);
+ if (!s->s_root)
+ goto out_no_root;
+ parse_options(data, &root_inode->i_uid, &root_inode->i_gid);
unlock_super(s);
- s->s_root = d_alloc_root(proc_get_inode(s, PROC_ROOT_INO, &proc_root), NULL);
- if (!s->s_root) {
- s->s_dev = 0;
- printk("get root inode failed\n");
- return NULL;
- }
- parse_options(data, &s->s_root->d_inode->i_uid, &s->s_root->d_inode->i_gid);
return s;
+
+out_no_root:
+ printk("proc_read_super: get root inode failed\n");
+ iput(root_inode);
+ s->s_dev = 0;
+ unlock_super(s);
+ return NULL;
}
int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
static int proc_root_readdir(struct file *, void *, filldir_t);
static int proc_root_lookup(struct inode *,struct dentry *);
+static int proc_unlink(struct inode *, struct dentry *);
static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};
NULL /* permission */
};
+/*
+ * /proc dynamic directories now support unlinking
+ */
+struct inode_operations proc_dyna_dir_inode_operations = {
+ &proc_dir_operations, /* default proc dir ops */
+ NULL, /* create */
+ proc_lookup, /* lookup */
+ NULL, /* link */
+ proc_unlink, /* unlink(struct inode *, struct dentry *) */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
/*
* The root /proc directory is special, as it has the
* <pid> directories. Thus we don't use the generic
int proc_openprom_regdev(struct openpromfs_dev *d)
{
- if (proc_openpromdev_ino == PROC_OPENPROMD_FIRST + PROC_NOPENPROMD) return -1;
+ if (proc_openpromdev_ino == PROC_OPENPROMD_FIRST + PROC_NOPENPROMD)
+ return -1;
d->next = proc_openprom_devices;
d->inode = proc_openpromdev_ino++;
proc_openprom_devices = d;
(inode, filp, dirent, filldir);
return -EINVAL;
}
+#define OPENPROM_DEFREADDIR proc_openprom_defreaddir
static int
proc_openprom_deflookup(struct inode * dir, struct dentry *dentry)
(dir, dentry);
return -ENOENT;
}
+#define OPENPROM_DEFLOOKUP proc_openprom_deflookup
+#else
+#define OPENPROM_DEFREADDIR NULL
+#define OPENPROM_DEFLOOKUP NULL
#endif
static struct file_operations proc_openprom_operations = {
NULL, /* lseek - default */
NULL, /* read - bad */
NULL, /* write - bad */
-#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD)
- proc_openprom_defreaddir,/* readdir */
-#else
- NULL, /* readdir */
-#endif
+ OPENPROM_DEFREADDIR, /* readdir */
NULL, /* poll - default */
NULL, /* ioctl - default */
NULL, /* mmap */
struct inode_operations proc_openprom_inode_operations = {
&proc_openprom_operations,/* default net directory file-ops */
NULL, /* create */
-#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD)
- proc_openprom_deflookup,/* lookup */
-#else
- NULL, /* lookup */
-#endif
+ OPENPROM_DEFLOOKUP, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
#endif
}
+/*
+ * As some entries in /proc are volatile, we want to
+ * get rid of unused dentries. This could be made
+ * smarter: we could keep a "volatile" flag in the
+ * inode to indicate which ones to keep.
+ */
+static void
+proc_delete_dentry(struct dentry * dentry)
+{
+ d_drop(dentry);
+}
+
+static struct dentry_operations proc_dentry_operations =
+{
+ NULL, /* revalidate */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ proc_delete_dentry /* d_delete(struct dentry *) */
+};
+
/*
* Don't create negative dentries here, return -ENOENT by hand
* instead.
{
struct inode *inode;
struct proc_dir_entry * de;
+ int error;
+ error = -ENOTDIR;
if (!dir || !S_ISDIR(dir->i_mode))
- return -ENOTDIR;
+ goto out;
- de = (struct proc_dir_entry *) dir->u.generic_ip;
+ error = -ENOENT;
inode = NULL;
+ de = (struct proc_dir_entry *) dir->u.generic_ip;
if (de) {
for (de = de->subdir; de ; de = de->next) {
if (!de || !de->low_ino)
continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
int ino = de->low_ino | (dir->i_ino & ~(0xffff));
+ error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de);
- if (!inode)
- return -EINVAL;
break;
}
}
}
- if (!inode)
- return -ENOENT;
- d_add(dentry, inode);
- return 0;
+ if (inode) {
+ dentry->d_op = &proc_dentry_operations;
+ d_add(dentry, inode);
+ error = 0;
+ }
+out:
+ return error;
}
static int proc_root_lookup(struct inode * dir, struct dentry * dentry)
if (!inode)
return -EINVAL;
}
+
+ dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
return 0;
}
read_unlock(&tasklist_lock);
return 0;
}
+
+static int proc_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry * dp = dir->u.generic_ip;
+
+printk("proc_file_unlink: deleting %s/%s\n", dp->name, dentry->d_name.name);
+
+ remove_proc_entry(dentry->d_name.name, dp);
+ dentry->d_inode->i_nlink = 0;
+ d_delete(dentry);
+ return 0;
+}
# Note 2! The CFLAGS definitions are now in the main makefile...
O_TARGET := smbfs.o
-O_OBJS := proc.o dir.o sock.o inode.o file.o ioctl.o
+O_OBJS := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o
M_OBJS := $(O_TARGET)
# If you want debugging output, please uncomment the following line
--- /dev/null
+/*
+ * cache.c
+ *
+ * Copyright (C) 1997 by Bill Hawes
+ *
+ * Routines to support directory cacheing using the page cache.
+ * Right now this only works for smbfs, but will be generalized
+ * for use with other filesystems.
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/dirent.h>
+#include <linux/smb_fs.h>
+
+#include <asm/page.h>
+
+#define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_VERBOSE 1 */
+
+static inline struct inode *
+get_cache_inode(struct cache_head *cachep)
+{
+ return (mem_map + MAP_NR((unsigned long) cachep))->inode;
+}
+
+/*
+ * Get a pointer to the cache_head structure,
+ * mapped as the page at offset 0. The page is
+ * kept locked while we're using the cache.
+ */
+struct cache_head *
+smb_get_dircache(struct dentry * dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ struct cache_head * cachep;
+
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_get_dircache: finding cache for %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ cachep = (struct cache_head *) get_cached_page(inode, 0, 1);
+ if (!cachep)
+ goto out;
+ if (cachep->valid)
+ {
+ struct cache_index * index = cachep->index;
+ struct cache_block * block;
+ unsigned long offset;
+ int i;
+
+ cachep->valid = 0;
+ /*
+ * Here we only want to find existing cache blocks,
+ * not add new ones.
+ */
+ for (i = 0; i < cachep->pages; i++, index++) {
+#ifdef SMBFS_PARANOIA
+if (index->block)
+printk("smb_get_dircache: cache %s/%s has existing block!\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ offset = PAGE_SIZE + (i << PAGE_SHIFT);
+ block = (struct cache_block *) get_cached_page(inode,
+ offset, 0);
+ if (!block)
+ goto out;
+ index->block = block;
+ }
+ cachep->valid = 1;
+ }
+out:
+ return cachep;
+}
+
+/*
+ * Unlock and release the data blocks.
+ */
+static void
+smb_free_cache_blocks(struct cache_head * cachep)
+{
+ struct cache_index * index = cachep->index;
+ int i;
+
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_free_cache_blocks: freeing %d blocks\n", cachep->pages);
+#endif
+ for (i = 0; i < cachep->pages; i++, index++)
+ {
+ if (index->block)
+ {
+ put_cached_page((unsigned long) index->block);
+ index->block = NULL;
+ }
+ }
+}
+
+/*
+ * Unlocks and releases the dircache.
+ */
+void
+smb_free_dircache(struct cache_head * cachep)
+{
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_free_dircache: freeing cache\n");
+#endif
+ smb_free_cache_blocks(cachep);
+ put_cached_page((unsigned long) cachep);
+}
+
+/*
+ * Initializes the dircache. We release any existing data blocks,
+ * and then clear the cache_head structure.
+ */
+void
+smb_init_dircache(struct cache_head * cachep)
+{
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_init_dircache: initializing cache, %d blocks\n", cachep->pages);
+#endif
+ smb_free_cache_blocks(cachep);
+ memset(cachep, 0, sizeof(struct cache_head));
+}
+
+/*
+ * Add a new entry to the cache. This assumes that the
+ * entries are coming in order and are added to the end.
+ */
+void
+smb_add_to_cache(struct cache_head * cachep, struct dirent *entry, off_t fpos)
+{
+ struct inode * inode = get_cache_inode(cachep);
+ struct cache_index * index;
+ struct cache_block * block;
+ unsigned long page_off;
+ unsigned int nent, offset, len = entry->d_reclen;
+ unsigned int needed = len + sizeof(struct cache_entry);
+
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_add_to_cache: cache inode %p, status %d, adding %s at %ld\n",
+inode, cachep->status, entry->d_name, fpos);
+#endif
+ /*
+ * Don't do anything if we've had an error ...
+ */
+ if (cachep->status)
+ goto out;
+
+ index = &cachep->index[cachep->idx];
+ if (!index->block)
+ goto get_block;
+
+ /* space available? */
+ if (needed < index->space)
+ {
+ add_entry:
+ nent = index->num_entries;
+ index->num_entries++;
+ index->space -= needed;
+ offset = index->space +
+ index->num_entries * sizeof(struct cache_entry);
+ block = index->block;
+ memcpy(&block->cb_data.names[offset], entry->d_name, len);
+ block->cb_data.table[nent].namelen = len;
+ block->cb_data.table[nent].offset = offset;
+ block->cb_data.table[nent].ino = entry->d_ino;
+ cachep->entries++;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_add_to_cache: added entry %s, len=%d, pos=%ld, entries=%d\n",
+entry->d_name, len, fpos, cachep->entries);
+#endif
+ return;
+ }
+ /*
+ * This block is full ... advance the index.
+ */
+ cachep->idx++;
+ if (cachep->idx > NINDEX) /* not likely */
+ goto out_full;
+ index++;
+#ifdef SMBFS_PARANOIA
+if (index->block)
+printk("smb_add_to_cache: new index already has block!\n");
+#endif
+
+ /*
+ * Get the next cache block
+ */
+get_block:
+ cachep->pages++;
+ page_off = PAGE_SIZE + (cachep->idx << PAGE_SHIFT);
+ block = (struct cache_block *) get_cached_page(inode, page_off, 1);
+ if (block)
+ {
+ index->block = block;
+ index->space = PAGE_SIZE;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_add_to_cache: inode=%p, pages=%d, block at %ld\n",
+inode, cachep->pages, page_off);
+#endif
+ goto add_entry;
+ }
+ /*
+ * On failure, just set the return status ...
+ */
+out_full:
+ cachep->status = -ENOMEM;
+out:
+ return;
+}
+
+int
+smb_find_in_cache(struct cache_head * cachep, off_t pos,
+ struct cache_dirent *entry)
+{
+ struct cache_index * index = cachep->index;
+ struct cache_block * block;
+ unsigned int i, nent, offset = 0;
+ off_t next_pos = 2;
+
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_find_in_cache: cache %p, looking for pos=%ld\n", cachep, pos);
+#endif
+ for (i = 0; i < cachep->pages; i++, index++)
+ {
+ if (pos < next_pos)
+ break;
+ nent = pos - next_pos;
+ next_pos += index->num_entries;
+ if (pos >= next_pos)
+ continue;
+ /*
+ * The entry is in this block. Note: we return
+ * then name as a reference with _no_ null byte.
+ */
+ block = index->block;
+ entry->ino = block->cb_data.table[nent].ino;
+ entry->len = block->cb_data.table[nent].namelen;
+ offset = block->cb_data.table[nent].offset;
+ entry->name = &block->cb_data.names[offset];
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_find_in_cache: found %s, len=%d, pos=%ld\n",
+entry->name, entry->len, pos);
+#endif
+ break;
+ }
+ return offset;
+}
+
+int
+smb_refill_dircache(struct cache_head * cachep, struct dentry *dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ int result;
+
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_refill_dircache: cache %s/%s, blocks=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, cachep->pages);
+#endif
+ /*
+ * Fill the cache, starting at position 2.
+ */
+retry:
+ inode->u.smbfs_i.cache_valid = 1;
+ result = smb_proc_readdir(dentry, 2, cachep);
+ if (result < 0)
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_refill_dircache: readdir failed, result=%d\n", result);
+#endif
+ goto out;
+ }
+
+ /*
+ * Check whether the cache was invalidated while
+ * we were doing the scan ...
+ */
+ if (!inode->u.smbfs_i.cache_valid)
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_refill_dircache: cache invalidated, retrying\n");
+#endif
+ goto retry;
+ }
+
+ result = cachep->status;
+ if (!result)
+ {
+ cachep->valid = 1;
+ }
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_refill_cache: cache %s/%s status=%d, entries=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+cachep->status, cachep->entries);
+#endif
+
+out:
+ return result;
+}
+
+void
+smb_invalid_dir_cache(struct inode * dir)
+{
+ /*
+ * Get rid of any unlocked pages, and clear the
+ * 'valid' flag in case a scan is in progress.
+ */
+ invalidate_inode_pages(dir);
+ dir->u.smbfs_i.cache_valid = 0;
+}
+
/* #define SMBFS_DEBUG_VERBOSE 1 */
/* #define pr_debug printk */
-#define this_dir_cached(dir) ((dir->i_sb == c_sb) && (dir->i_ino == c_ino))
-
-static long
-smb_dir_read(struct inode *inode, struct file *filp,
- char *buf, unsigned long count);
-
-static int
-smb_readdir(struct file *filp, void *dirent, filldir_t filldir);
+static long smb_dir_read(struct inode *, struct file *, char *, unsigned long);
+static int smb_readdir(struct file *, void *, filldir_t);
+static int smb_dir_open(struct inode *, struct file *);
static int smb_lookup(struct inode *, struct dentry *);
static int smb_create(struct inode *, struct dentry *, int);
static int smb_mkdir(struct inode *, struct dentry *, int);
static int smb_rmdir(struct inode *, struct dentry *);
static int smb_unlink(struct inode *, struct dentry *);
-static int smb_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+static int smb_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
static struct file_operations smb_dir_operations =
{
NULL, /* poll - default */
smb_ioctl, /* ioctl */
NULL, /* mmap */
- NULL, /* no special open code */
+ smb_dir_open, /* open(struct inode *, struct file *) */
NULL, /* no special release code */
NULL /* fsync */
};
NULL /* smap */
};
-static void smb_put_dentry(struct dentry *);
-static struct dentry_operations smbfs_dentry_operations =
-{
- NULL, /* revalidate */
- NULL, /* d_hash */
- NULL, /* d_compare */
- smb_put_dentry /* d_delete */
-};
-
static long
smb_dir_read(struct inode *inode, struct file *filp, char *buf,
unsigned long count)
}
/*
- * This is the callback from dput(). We close the file so that
- * cached dentries don't keep the file open.
- */
-void
-smb_put_dentry(struct dentry *dentry)
-{
- struct inode *ino = dentry->d_inode;
- if (ino)
- smb_close(ino);
-}
-
-/* Static variables for the dir cache */
-static struct smb_dirent *c_entry = NULL;
-static struct super_block * c_sb = NULL;
-static unsigned long c_ino = 0;
-static int c_seen_eof;
-static int c_size;
-static int c_last_returned_index;
-
-static struct smb_dirent *
-smb_search_in_cache(struct inode *dir, unsigned long f_pos)
-{
- int i;
-
- if (this_dir_cached(dir))
- for (i = 0; i < c_size; i++)
- {
- if (c_entry[i].f_pos < f_pos)
- continue;
- if (c_entry[i].f_pos == f_pos)
- {
- c_last_returned_index = i;
- return &(c_entry[i]);
- }
- break;
- }
- return NULL;
-}
-
-/*
- * Compute the hash for a qstr ... move to include/linux/dcache.h?
+ * Compute the hash for a qstr.
+ * N.B. Move to include/linux/dcache.h?
*/
-static unsigned int hash_it(const char * name, unsigned int len)
+static unsigned int
+hash_it(const char * name, unsigned int len)
{
- unsigned long hash;
- hash = init_name_hash();
+ unsigned long hash = init_name_hash();
while (len--)
hash = partial_name_hash(*name++, hash);
return end_name_hash(hash);
}
-static struct semaphore refill_cache_sem = MUTEX;
/*
- * Called with the refill semaphore held.
+ * If a dentry already exists, we have to give the cache entry
+ * the correct inode number. This is needed for getcwd().
*/
-static int
-smb_refill_dir_cache(struct dentry *dentry, unsigned long f_pos)
+static unsigned long
+smb_find_ino(struct dentry *dentry, struct cache_dirent *entry)
{
- struct inode *dir = dentry->d_inode;
- ino_t ino_start;
- int i, result;
-
- result = smb_proc_readdir(dentry, f_pos,
- SMB_READDIR_CACHE_SIZE, c_entry);
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_refill_dir_cache: dir=%s/%s, pos=%lu, result=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, f_pos, result);
-#endif
-
- if (result <= 0)
- {
- /*
- * If an error occurred, the cache may have been partially
- * filled prior to failing, so we must invalidate.
- * N.B. Might not need to for 0 return ... save cache?
- */
- c_sb = NULL;
- c_ino = 0;
- c_seen_eof = 0;
- goto out;
- }
-
- /* Suppose there are a multiple of cache entries? */
- c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
- c_sb = dir->i_sb;
- c_ino = dir->i_ino;
- c_size = result;
- c_last_returned_index = 0; /* is this used? */
-
- ino_start = smb_invent_inos(c_size);
- /*
- * If a dentry already exists, we have to give the cache entry
- * the correct inode number. This is needed for getcwd().
- */
- for (i = 0; i < c_size; i++)
+ struct dentry * new_dentry;
+ struct qstr qname;
+ unsigned long ino = 0;
+
+ qname.name = entry->name;
+ qname.len = entry->len;
+ qname.hash = hash_it(qname.name, qname.len);
+ new_dentry = d_lookup(dentry, &qname);
+ if (new_dentry)
{
- struct dentry * new_dentry;
- struct qstr qname;
-
- c_entry[i].attr.f_ino = ino_start++;
- qname.name = c_entry[i].name;
- qname.len = c_entry[i].len;
- qname.hash = hash_it(qname.name, qname.len);
- new_dentry = d_lookup(dentry, &qname);
- if (new_dentry)
- {
- struct inode * inode = new_dentry->d_inode;
- if (inode)
- c_entry[i].attr.f_ino = inode->i_ino;
- dput(new_dentry);
- }
+ struct inode * inode = new_dentry->d_inode;
+ if (inode)
+ ino = inode->i_ino;
+ dput(new_dentry);
}
-out:
- return result;
+ if (!ino)
+ ino = smb_invent_inos(1);
+ return ino;
}
static int
{
struct dentry *dentry = filp->f_dentry;
struct inode *dir = dentry->d_inode;
- struct smb_dirent *entry;
+ struct cache_head *cachep;
int result;
pr_debug("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
pr_debug("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
dir->i_ino, c_ino);
-
- result = -EBADF;
- if ((dir == NULL) || !S_ISDIR(dir->i_mode))
+ /*
+ * Make sure our inode is up-to-date.
+ */
+ result = smb_revalidate_inode(dir);
+ if (result)
+ goto out;
+ /*
+ * Get the cache pointer ...
+ */
+ cachep = smb_get_dircache(dentry);
+ if (!cachep)
goto out;
-
/*
- * Check whether the directory cache exists yet
+ * Make sure the cache is up-to-date.
*/
- if (c_entry == NULL)
+ if (!cachep->valid)
{
- int size = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
- result = -ENOMEM;
- entry = (struct smb_dirent *) smb_vmalloc(size);
- /*
- * Somebody else may have allocated the cache,
- * so we check again to avoid a memory leak.
- */
- if (!c_entry)
- {
- if (!entry)
- goto out;
- c_entry = entry;
- } else if (entry) {
- printk("smb_readdir: cache already alloced!\n");
- smb_vfree(entry);
- }
+ result = smb_refill_dircache(cachep, dentry);
+ if (result)
+ goto up_and_out;
}
- result = 0;
switch ((unsigned int) filp->f_pos)
{
case 0:
if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0)
- goto out;
+ goto up_and_out;
filp->f_pos = 1;
case 1:
if (filldir(dirent, "..", 2, 1,
dentry->d_parent->d_inode->i_ino) < 0)
- goto out;
+ goto up_and_out;
filp->f_pos = 2;
}
- /*
- * Since filldir() could block if dirent is paged out,
- * we hold the refill semaphore while using the cache.
- * N.B. It's possible that the directory could change
- * between calls to readdir ... what to do??
- */
- down(&refill_cache_sem);
- entry = smb_search_in_cache(dir, filp->f_pos);
- if (entry == NULL)
+ while (1)
{
- /* Past the end of _this_ directory? */
- if (this_dir_cached(dir) && c_seen_eof &&
- filp->f_pos == c_entry[c_size-1].f_pos + 1)
+ struct cache_dirent this_dirent, *entry = &this_dirent;
+
+ if (!smb_find_in_cache(cachep, filp->f_pos, entry))
+ break;
+ /*
+ * Check whether to look up the inode number.
+ */
+ if (!entry->ino)
{
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_readdir: eof reached for %s/%s, c_size=%d, pos=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, c_size, (int) filp->f_pos);
-#endif
- goto up_and_out;
+ entry->ino = smb_find_ino(dentry, entry);
}
- result = smb_refill_dir_cache(dentry, filp->f_pos);
- if (result <= 0)
- goto up_and_out;
- entry = c_entry;
- }
-
- while (entry < &(c_entry[c_size]))
- {
- pr_debug("smb_readdir: entry->name = %s\n", entry->name);
if (filldir(dirent, entry->name, entry->len,
- entry->f_pos, entry->attr.f_ino) < 0)
+ filp->f_pos, entry->ino) < 0)
break;
-#if SMBFS_PARANOIA
-/* should never happen */
-if (!this_dir_cached(dir) || (entry->f_pos != filp->f_pos))
-printk("smb_readdir: cache changed!\n");
-#endif
filp->f_pos += 1;
- entry += 1;
}
result = 0;
+ /*
+ * Release the dircache.
+ */
up_and_out:
- up(&refill_cache_sem);
+ smb_free_dircache(cachep);
out:
return result;
}
-void
-smb_init_dir_cache(void)
+static int
+smb_dir_open(struct inode *dir, struct file *file)
{
- c_entry = NULL;
- c_sb = NULL;
- c_ino = 0;
- c_seen_eof = 0;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_dir_open: (%s/%s)\n", file->f_dentry->d_parent->d_name.name,
+file->f_dentry->d_name.name);
+#endif
+ return smb_revalidate_inode(dir);
}
-void
-smb_invalid_dir_cache(struct inode * dir)
+/*
+ * Dentry operations routines
+ */
+static int smb_lookup_validate(struct dentry *);
+static void smb_delete_dentry(struct dentry *);
+
+static struct dentry_operations smbfs_dentry_operations =
+{
+ smb_lookup_validate, /* d_validate(struct dentry *) */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ smb_delete_dentry /* d_delete(struct dentry *) */
+};
+
+/*
+ * This is the callback when the dcache has a lookup hit.
+ */
+static int smb_lookup_validate(struct dentry * dentry)
{
- if (this_dir_cached(dir))
+ struct inode * inode = dentry->d_inode;
+ unsigned long age = jiffies - dentry->d_time;
+ int valid;
+
+ /*
+ * The default validation is based on dentry age:
+ * we believe in dentries for 5 seconds. (But each
+ * successful server lookup renews the timestamp.)
+ */
+ valid = age < 5 * HZ || IS_ROOT(dentry);
+#ifdef SMBFS_DEBUG_VERBOSE
+if (!valid)
+printk("smb_lookup_validate: %s/%s not valid, age=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, age)
+#endif
+
+ if (inode)
+ {
+ if (is_bad_inode(inode))
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_lookup_validate: %s/%s has dud inode\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ valid = 0;
+ }
+ } else
{
- c_sb = NULL;
- c_ino = 0;
- c_seen_eof = 0;
+ /*
+ * What should we do for negative dentries?
+ */
}
+ return valid;
}
-void
-smb_free_dir_cache(void)
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to close files and unhash dentries with bad inodes.
+ */
+static void smb_delete_dentry(struct dentry * dentry)
{
- if (c_entry != NULL)
+ if (dentry->d_inode)
+ {
+ if (is_bad_inode(dentry->d_inode))
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_delete_dentry: bad inode, unhashing %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ d_drop(dentry);
+ }
+ smb_close_dentry(dentry);
+ } else
{
- /* N.B. can this block?? */
- smb_vfree(c_entry);
+ /* N.B. Unhash negative dentries? */
+ }
+}
+
+/*
+ * 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 smb_renew_times(struct dentry * dentry)
+{
+ for (;;) {
+ dentry->d_time = jiffies;
+ if (dentry == dentry->d_parent)
+ break;
+ dentry = dentry->d_parent;
}
- c_entry = NULL;
}
static int
-smb_lookup(struct inode *dir, struct dentry *d_entry)
+smb_lookup(struct inode *dir, struct dentry *dentry)
{
struct smb_fattr finfo;
struct inode *inode;
int error;
error = -ENAMETOOLONG;
- if (d_entry->d_name.len > SMB_MAXNAMELEN)
+ if (dentry->d_name.len > SMB_MAXNAMELEN)
goto out;
- error = smb_proc_getattr(d_entry->d_parent, &(d_entry->d_name), &finfo);
-#if SMBFS_PARANOIA
+ error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &finfo);
+#ifdef SMBFS_PARANOIA
if (error && error != -ENOENT)
printk("smb_lookup: find %s/%s failed, error=%d\n",
-d_entry->d_parent->d_name.name, d_entry->d_name.name, error);
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
#endif
inode = NULL;
if (inode)
{
/* cache the dentry pointer */
- inode->u.smbfs_i.dentry = d_entry;
+ inode->u.smbfs_i.dentry = dentry;
add_entry:
- d_entry->d_op = &smbfs_dentry_operations;
- d_add(d_entry, inode);
+ dentry->d_op = &smbfs_dentry_operations;
+ d_add(dentry, inode);
+ smb_renew_times(dentry);
error = 0;
}
}
* This code is common to all routines creating a new inode.
*/
static int
-smb_instantiate(struct inode *dir, struct dentry *dentry)
+smb_instantiate(struct dentry *dentry)
{
struct smb_fattr fattr;
int error;
- smb_invalid_dir_cache(dir);
-
error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr);
if (!error)
{
struct inode *inode;
error = -EACCES;
fattr.f_ino = smb_invent_inos(1);
- inode = smb_iget(dir->i_sb, &fattr);
+ inode = smb_iget(dentry->d_sb, &fattr);
if (inode)
{
/* cache the dentry pointer */
inode->u.smbfs_i.dentry = dentry;
d_instantiate(dentry, inode);
+ smb_renew_times(dentry);
error = 0;
}
}
* state. Currently we close it directly again, although this
* is not necessary anymore. */
+ smb_invalid_dir_cache(dir);
error = smb_proc_create(dentry->d_parent, &(dentry->d_name),
0, CURRENT_TIME);
if (!error)
{
- error = smb_instantiate(dir, dentry);
+ error = smb_instantiate(dentry);
}
out:
return error;
if (dentry->d_name.len > SMB_MAXNAMELEN)
goto out;
+ smb_invalid_dir_cache(dir);
error = smb_proc_mkdir(dentry->d_parent, &(dentry->d_name));
if (!error)
{
- error = smb_instantiate(dir, dentry);
+ error = smb_instantiate(dentry);
}
out:
return error;
int error;
error = -ENAMETOOLONG;
- if (dentry->d_name.len > NFS_MAXNAMLEN)
+ if (dentry->d_name.len > SMB_MAXNAMELEN)
goto out;
/*
*/
if (dentry->d_inode)
smb_close(dentry->d_inode);
- smb_invalid_dir_cache(dir);
+ smb_invalid_dir_cache(dir);
error = smb_proc_rmdir(dentry->d_parent, &(dentry->d_name));
if (!error)
{
+ smb_renew_times(dentry);
d_delete(dentry);
}
out:
*/
if (dentry->d_inode)
smb_close(dentry->d_inode);
- smb_invalid_dir_cache(dir);
+ smb_invalid_dir_cache(dir);
error = smb_proc_unlink(dentry->d_parent, &(dentry->d_name));
if (!error)
{
+ smb_renew_times(dentry);
d_delete(dentry);
}
out:
{
int error;
- error = -ENOTDIR;
- if (!old_dir || !S_ISDIR(old_dir->i_mode))
- {
- printk("smb_rename: old inode is NULL or not a directory\n");
- goto out;
- }
-
- if (!new_dir || !S_ISDIR(new_dir->i_mode))
- {
- printk("smb_rename: new inode is NULL or not a directory\n");
- goto out;
- }
-
error = -ENAMETOOLONG;
if (old_dentry->d_name.len > SMB_MAXNAMELEN ||
new_dentry->d_name.len > SMB_MAXNAMELEN)
if (new_dentry->d_inode)
smb_close(new_dentry->d_inode);
- /* Assume success and invalidate now */
smb_invalid_dir_cache(old_dir);
smb_invalid_dir_cache(new_dir);
-
error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name),
new_dentry->d_parent, &(new_dentry->d_name));
/*
*/
if (error == -EEXIST)
{
-#ifdef SMBFS_PARANOIA
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_rename: existing file %s/%s, d_count=%d\n",
new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
new_dentry->d_count);
#endif
error = smb_proc_unlink(new_dentry->d_parent,
&(new_dentry->d_name));
-#ifdef SMBFS_PARANOIA
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_rename: after unlink error=%d\n", error);
#endif
- if (error)
- goto out;
- d_delete(new_dentry);
-
- error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name),
- new_dentry->d_parent, &(new_dentry->d_name));
+ if (!error)
+ {
+ d_delete(new_dentry);
+ error = smb_proc_mv(old_dentry->d_parent,
+ &(old_dentry->d_name),
+ new_dentry->d_parent,
+ &(new_dentry->d_name));
+ }
}
/*
*/
if (!error)
{
+ smb_renew_times(old_dentry);
+ smb_renew_times(new_dentry->d_parent);
d_move(old_dentry, new_dentry);
}
out:
/* #define SMBFS_DEBUG_VERBOSE 1 */
/* #define pr_debug printk */
+extern int smb_get_rsize(struct smb_sb_info *);
+extern int smb_get_wsize(struct smb_sb_info *);
+
static inline int
min(int a, int b)
{
return a < b ? a : b;
}
+static inline void
+smb_unlock_page(struct page *page)
+{
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+}
+
static int
smb_fsync(struct file *file, struct dentry * dentry)
{
- printk("smb_fsync: sync file %s/%s\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_fsync: sync file %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
return 0;
}
static int
smb_readpage_sync(struct inode *inode, struct page *page)
{
- unsigned long offset = page->offset;
char *buffer = (char *) page_address(page);
+ unsigned long offset = page->offset;
struct dentry * dentry = inode->u.smbfs_i.dentry;
- int rsize = SMB_SERVER(inode)->opt.max_xmit - (SMB_HEADER_LEN+15);
- int result, refresh = 0;
+ int rsize = smb_get_rsize(SMB_SERVER(inode));
int count = PAGE_SIZE;
+ int result;
- pr_debug("SMB: smb_readpage_sync(%p)\n", page);
clear_bit(PG_error, &page->flags);
result = -EIO;
goto io_error;
}
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_readpage_sync: file %s/%s, count=%d@%ld, rsize=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, count, offset, rsize);
+#endif
result = smb_open(dentry, O_RDONLY);
if (result < 0)
goto io_error;
- /* Should revalidate inode ... */
do {
if (count < rsize)
rsize = count;
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_readpage: reading %s/%s, offset=%ld, buffer=%p, size=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, offset, buffer, rsize);
-#endif
result = smb_proc_read(inode, offset, rsize, buffer);
if (result < 0)
goto io_error;
- refresh = 1;
count -= result;
offset += result;
buffer += result;
result = 0;
io_error:
- if (refresh)
- smb_refresh_inode(inode);
- clear_bit(PG_locked, &page->flags);
- wake_up(&page->wait);
+ smb_unlock_page(page);
return result;
}
set_bit(PG_locked, &page->flags);
atomic_inc(&page->count);
error = smb_readpage_sync(inode, page);
- __free_page(page);
+ free_page(page_address(page));
return error;
}
smb_writepage_sync(struct inode *inode, struct page *page,
unsigned long offset, unsigned int count)
{
- int wsize = SMB_SERVER(inode)->opt.max_xmit - (SMB_HEADER_LEN+15);
+ u8 *buffer = (u8 *) page_address(page) + offset;
+ int wsize = smb_get_wsize(SMB_SERVER(inode));
int result, refresh = 0, written = 0;
- u8 *buffer;
- pr_debug("SMB: smb_writepage_sync(%x/%ld %d@%ld)\n",
- inode->i_dev, inode->i_ino,
- count, page->offset + offset);
-
- buffer = (u8 *) page_address(page) + offset;
offset += page->offset;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_writepage_sync: file %s/%s, count=%d@%ld, wsize=%d\n",
+((struct dentry *) inode->u.smbfs_i.dentry)->d_parent->d_name.name,
+((struct dentry *) inode->u.smbfs_i.dentry)->d_name.name, count, offset, wsize);
+#endif
do {
if (count < wsize)
wsize = count;
result = smb_proc_write(inode, offset, wsize, buffer);
-
- if (result < 0) {
+ if (result < 0)
+ {
/* Must mark the page invalid after I/O error */
clear_bit(PG_uptodate, &page->flags);
goto io_error;
} while (count);
io_error:
+#if 0
if (refresh)
smb_refresh_inode(inode);
-
- clear_bit(PG_locked, &page->flags);
- wake_up(&page->wait);
+#endif
+ smb_unlock_page(page);
return written ? written : result;
}
set_bit(PG_locked, &page->flags);
atomic_inc(&page->count);
result = smb_writepage_sync(inode, page, 0, PAGE_SIZE);
- __free_page(page);
+ free_page(page_address(page));
return result;
}
smb_updatepage(struct inode *inode, struct page *page, const char *buffer,
unsigned long offset, unsigned int count, int sync)
{
- u8 *page_addr;
- int result;
+ unsigned long page_addr = page_address(page);
+ int result;
pr_debug("SMB: smb_updatepage(%x/%ld %d@%ld, sync=%d)\n",
inode->i_dev, inode->i_ino,
set_bit(PG_locked, &page->flags);
atomic_inc(&page->count);
- page_addr = (u8 *) page_address(page);
-
- if (copy_from_user(page_addr + offset, buffer, count))
+ if (copy_from_user((char *) page_addr + offset, buffer, count))
goto bad_fault;
result = smb_writepage_sync(inode, page, offset, count);
out:
- __free_page(page);
+ free_page(page_addr);
return result;
bad_fault:
- printk("smb_updatepage: fault at page=%p buffer=%p\n", page, buffer);
+#ifdef SMBFS_PARANOIA
+printk("smb_updatepage: fault at addr=%lu, offset=%lu, buffer=%p\n",
+page_addr, offset, buffer);
+#endif
result = -EFAULT;
clear_bit(PG_uptodate, &page->flags);
- clear_bit(PG_locked, &page->flags);
- wake_up(&page->wait);
+ smb_unlock_page(page);
goto out;
}
{
int status;
- pr_debug("SMB: read(%x/%ld (%d), %lu@%lu)\n",
- inode->i_dev, inode->i_ino, inode->i_count,
- count, (unsigned long) file->f_pos);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_file_read: file %s/%s, count=%lu@%lu\n",
+file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name,
+count, (unsigned long) file->f_pos);
+#endif
status = smb_revalidate_inode(inode);
if (status >= 0)
struct inode * inode = dentry->d_inode;
int status;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_file_mmap: file %s/%s, address %lu - %lu\n",
+file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name,
+vma->vm_start, vma->vm_end);
+#endif
status = smb_revalidate_inode(inode);
if (status >= 0)
{
{
int result;
- pr_debug("SMB: write(%x/%ld (%d), %lu@%lu)\n",
- inode->i_dev, inode->i_ino, inode->i_count,
- count, (unsigned long) file->f_pos);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_file_write: file %s/%s, count=%lu@%lu\n",
+file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name,
+count, (unsigned long) file->f_pos);
+#endif
+#ifdef SMBFS_PARANOIA
+ /* Should be impossible now that inodes can't change mode */
result = -EINVAL;
- if (!inode) {
- printk("smb_file_write: inode = NULL\n");
+ if (!S_ISREG(inode->i_mode))
+ {
+ printk("smb_file_write: write to non-file, mode %07o\n",
+ inode->i_mode);
goto out;
}
+#endif
result = smb_revalidate_inode(inode);
- if (result < 0)
+ if (result)
goto out;
result = smb_open(file->f_dentry, O_WRONLY);
- if (result < 0)
+ if (result)
goto out;
- result = -EINVAL;
- if (!S_ISREG(inode->i_mode)) {
- printk("smb_file_write: write to non-file, mode %07o\n",
- inode->i_mode);
- goto out;
- }
-
- result = 0;
if (count > 0)
{
result = generic_file_write(inode, file, buf, count);
+ if (result > 0)
+ smb_refresh_inode(inode);
}
out:
return result;
}
+static int
+smb_file_open(struct inode *inode, struct file * file)
+{
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_file_open: inode=%p, file=%p\n", inode, file);
+#endif
+ return 0;
+}
+
+static int
+smb_file_release(struct inode *inode, struct file * file)
+{
+ struct dentry * dentry = file->f_dentry;
+
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_file_release: closing file %s/%s, d_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+#endif
+
+ if (dentry->d_count == 1)
+ {
+ smb_close(inode);
+ }
+ return 0;
+}
+
static struct file_operations smb_file_operations =
{
NULL, /* lseek - default */
NULL, /* readdir - bad */
NULL, /* poll - default */
smb_ioctl, /* ioctl */
- smb_file_mmap, /* mmap */
- NULL, /* open */
- NULL, /* release */
- smb_fsync, /* fsync */
+ smb_file_mmap, /* mmap(struct file*, struct vm_area_struct*) */
+ smb_file_open, /* open(struct inode*, struct file*) */
+ smb_file_release, /* release(struct inode*, struct file*) */
+ smb_fsync, /* fsync(struct file*, struct dentry*) */
+ NULL, /* fasync(struct file*, int) */
+ NULL, /* check_media_change(kdev_t dev) */
+ NULL, /* revalidate(kdev_t dev) */
+ NULL /* lock(struct file*, int, struct file_lock*) */
};
struct inode_operations smb_file_inode_operations =
*
*/
+#define SMBFS_DCACHE_EXT 1
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
-#include <linux/file.h>
-#include <linux/fcntl.h>
#include <linux/malloc.h>
#include <linux/init.h>
+#include <linux/dcache.h>
#include <asm/system.h>
#include <asm/uaccess.h>
-#define SB_of(server) ((struct super_block *) ((char *)(server) - \
- (unsigned long)(&((struct super_block *)0)->u.smbfs_sb)))
+#define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_VERBOSE 1 */
+#ifndef SMBFS_DCACHE_EXT
+#define shrink_dcache_sb(sb) shrink_dcache()
+#endif
+extern void smb_renew_times(struct dentry *);
extern int close_fp(struct file *filp);
static void smb_put_inode(struct inode *);
return ino;
}
-static struct smb_fattr *read_fattr;
+static struct smb_fattr *read_fattr = NULL;
static struct semaphore read_semaphore = MUTEX;
struct inode *
down(&read_semaphore);
read_fattr = fattr;
result = iget(sb, fattr->f_ino);
+ read_fattr = NULL;
up(&read_semaphore);
return result;
}
static void
smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
{
- inode->i_dev = inode->i_sb->s_dev;
inode->i_mode = fattr->f_mode;
inode->i_nlink = fattr->f_nlink;
inode->i_uid = fattr->f_uid;
inode->i_atime = fattr->f_atime;
inode->i_blksize= fattr->f_blksize;
inode->i_blocks = fattr->f_blocks;
+ /*
+ * Update the "last time refreshed" field for revalidation.
+ */
+ inode->u.smbfs_i.oldmtime = jiffies;
}
static void
{
pr_debug("smb_iget: %p\n", read_fattr);
- if ((atomic_read(&read_semaphore.count) == 1) ||
- (inode->i_ino != read_fattr->f_ino))
+ if (!read_fattr || inode->i_ino != read_fattr->f_ino)
{
printk("smb_read_inode called from invalid point\n");
return;
}
- smb_set_inode_attr(inode, read_fattr);
+ inode->i_dev = inode->i_sb->s_dev;
memset(&(inode->u.smbfs_i), 0, sizeof(inode->u.smbfs_i));
+ smb_set_inode_attr(inode, read_fattr);
if (S_ISREG(inode->i_mode))
inode->i_op = &smb_file_inode_operations;
void
smb_invalidate_inodes(struct smb_sb_info *server)
{
- printk("smb_invalidate_inodes\n");
- shrink_dcache(); /* should shrink only this sb */
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_invalidate_inodes\n");
+#endif
+ shrink_dcache_sb(SB_of(server));
invalidate_inodes(SB_of(server));
}
int
-smb_revalidate_inode(struct inode *ino)
+smb_revalidate_inode(struct inode *inode)
{
- struct dentry * dentry = ino->u.smbfs_i.dentry;
+ time_t last_time;
int error = 0;
pr_debug("smb_revalidate_inode\n");
- if (!ino)
- goto bad_no_inode;
- dentry = ino->u.smbfs_i.dentry;
-#if 0
- if (dentry)
+ /*
+ * Check whether we've recently refreshed the inode.
+ */
+ if (jiffies < inode->u.smbfs_i.oldmtime + HZ/10)
{
- printk("smb_revalidate: checking %s/%s\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_revalidate_inode: up-to-date, jiffies=%lu, oldtime=%lu\n",
+jiffies, inode->u.smbfs_i.oldmtime);
+#endif
+ goto out;
}
+
+ /*
+ * Save the last modified time, then refresh the inode
+ */
+ last_time = inode->i_mtime;
+ error = smb_refresh_inode(inode);
+ if (!error)
+ {
+ if (inode->i_mtime != last_time)
+ {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_revalidate: %s/%s changed, old=%ld, new=%ld\n",
+((struct dentry *)inode->u.smbfs_i.dentry)->d_parent->d_name.name,
+((struct dentry *)inode->u.smbfs_i.dentry)->d_name.name,
+(long) last_time, (long) inode->i_mtime);
#endif
+ if (!S_ISDIR(inode->i_mode))
+ invalidate_inode_pages(inode);
+ else
+ smb_invalid_dir_cache(inode);
+ }
+ }
out:
return error;
-
-bad_no_inode:
- printk("smb_revalidate: no inode!\n");
- error = -EINVAL;
- goto out;
}
+/*
+ * This is called to update the inode attributes after
+ * we've made changes to a file or directory.
+ */
int
-smb_refresh_inode(struct inode *ino)
+smb_refresh_inode(struct inode *inode)
{
- struct dentry * dentry = ino->u.smbfs_i.dentry;
+ struct dentry * dentry = inode->u.smbfs_i.dentry;
struct smb_fattr fattr;
int error;
pr_debug("smb_refresh_inode\n");
- error = -EIO;
- if (!dentry) {
+ if (!dentry)
+ {
printk("smb_refresh_inode: no dentry, can't refresh\n");
+ error = -EIO;
goto out;
}
- /* N.B. Should check for changes of important fields! cf. NFS */
+ /*
+ * Kludge alert ... for some reason we can't get attributes
+ * for the root directory, so just return success.
+ */
+ error = 0;
+ if (IS_ROOT(dentry))
+ goto out;
+
error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr);
if (!error)
{
- smb_set_inode_attr(ino, &fattr);
+ smb_renew_times(dentry);
+ /*
+ * Check whether the type part of the mode changed,
+ * and don't update the attributes if it did.
+ */
+ if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT))
+ smb_set_inode_attr(inode, &fattr);
+ else
+ {
+ /*
+ * Big trouble! The inode has become a new object,
+ * so any operations attempted on it are invalid.
+ *
+ * Take a couple of steps to limit damage:
+ * (1) Mark the inode as bad so that subsequent
+ * lookup validations will fail.
+ * (2) Clear i_nlink so the inode will be released
+ * at iput() time. (Unhash it as well?)
+ * We also invalidate the caches for good measure.
+ */
+#ifdef SMBFS_PARANOIA
+printk("smb_refresh_inode: %s/%s changed mode, %07o to %07o\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+inode->i_mode, fattr.f_mode);
+#endif
+ fattr.f_mode = inode->i_mode; /* save mode */
+ make_bad_inode(inode);
+ inode->i_mode = fattr.f_mode; /* restore mode */
+ inode->i_nlink = 0;
+ /*
+ * No need to worry about unhashing the dentry: the
+ * lookup validation will see that the inode is bad.
+ * But we may need to invalidate the caches ...
+ */
+ invalidate_inode_pages(inode);
+ smb_invalid_dir_cache(inode);
+ error = -EIO;
+ }
}
out:
return error;
+
}
/*
static void
smb_put_inode(struct inode *ino)
{
- struct dentry * dentry;
pr_debug("smb_put_inode: count = %d\n", ino->i_count);
if (ino->i_count > 1) {
+ struct dentry * dentry;
/*
* Check whether the dentry still holds this inode.
- * This looks scary, but should work ... d_inode is
- * cleared before iput() in the dcache.
+ * This looks scary, but should work ... if this is
+ * the last use, d_inode == NULL or d_count == 0.
*/
dentry = (struct dentry *) ino->u.smbfs_i.dentry;
- if (dentry && dentry->d_inode != ino) {
+ if (dentry && (dentry->d_inode != ino || dentry->d_count == 0))
+ {
ino->u.smbfs_i.dentry = NULL;
-#if 1
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_put_inode: cleared dentry for %s/%s (%ld), count=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, ino->i_ino, ino->i_count);
#endif
struct smb_mount_data *data = (struct smb_mount_data *)raw_data;
struct smb_fattr root;
kdev_t dev = sb->s_dev;
- unsigned char *packet;
struct inode *root_inode;
struct dentry *dentry;
if (data->version != SMB_MOUNT_VERSION)
goto out_wrong_data;
- packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE);
- if (!packet)
- goto out_no_mem;
-
lock_super(sb);
sb->s_blocksize = 1024; /* Eh... Is this correct? */
sb->s_blocksize_bits = 10;
sb->s_magic = SMB_SUPER_MAGIC;
- sb->s_dev = dev;
+ sb->s_dev = dev; /* shouldn't need this ... */
sb->s_op = &smb_sops;
sb->u.smbfs_sb.sock_file = NULL;
sb->u.smbfs_sb.sem = MUTEX;
+ sb->u.smbfs_sb.wait = NULL;
sb->u.smbfs_sb.conn_pid = 0;
- sb->u.smbfs_sb.packet = packet;
- sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE;
+ sb->u.smbfs_sb.state = CONN_INVALID; /* no connection yet */
sb->u.smbfs_sb.generation = 0;
+ sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE;
+ sb->u.smbfs_sb.packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE);
+ if (!sb->u.smbfs_sb.packet)
+ goto out_no_mem;
sb->u.smbfs_sb.m = *data;
sb->u.smbfs_sb.m.file_mode = (sb->u.smbfs_sb.m.file_mode &
(S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG;
sb->u.smbfs_sb.m.dir_mode = (sb->u.smbfs_sb.m.dir_mode &
(S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;
-
+ /*
+ * Keep the super block locked while we get the root inode.
+ */
smb_init_root_dirent(&(sb->u.smbfs_sb), &root);
-
- sb->s_root = NULL;
- unlock_super(sb);
-
root_inode = smb_iget(sb, &root);
if (!root_inode)
goto out_no_root;
+
dentry = d_alloc_root(root_inode, NULL);
if (!dentry)
goto out_no_root;
root_inode->u.smbfs_i.dentry = dentry;
sb->s_root = dentry;
+
+ unlock_super(sb);
return sb;
-out_no_data:
- printk("smb_read_super: missing data argument\n");
- goto out;
-out_wrong_data:
- printk(KERN_ERR "smb_read_super: wrong data argument."
- " Recompile smbmount.\n");
- goto out;
-out_no_mem:
- pr_debug("smb_read_super: could not alloc packet\n");
- goto out;
out_no_root:
- sb->s_dev = 0; /* iput() might block */
printk(KERN_ERR "smb_read_super: get root inode failed\n");
iput(root_inode);
- smb_vfree(packet);
-out:
+ smb_vfree(sb->u.smbfs_sb.packet);
+ goto out_unlock;
+out_no_mem:
+ printk("smb_read_super: could not alloc packet\n");
+ goto out_unlock;
+out_wrong_data:
+ printk(KERN_ERR "smb_read_super: wrong data argument."
+ " Recompile smbmount.\n");
+ goto out_fail;
+out_no_data:
+ printk("smb_read_super: missing data argument\n");
+ goto out_fail;
+out_unlock:
+ unlock_super(sb);
+out_fail:
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
int
smb_notify_change(struct inode *inode, struct iattr *attr)
{
+ struct smb_sb_info *server = SMB_SERVER(inode);
struct dentry *dentry = inode->u.smbfs_i.dentry;
- int error;
+ int error, refresh = 0;
error = -EIO;
if (!dentry)
goto out;
}
+ /*
+ * Make sure our inode is up-to-date ...
+ */
+ error = smb_revalidate_inode(inode);
+ if (error)
+ goto out;
+
if ((error = inode_change_ok(inode, attr)) < 0)
goto out;
error = -EPERM;
- if (((attr->ia_valid & ATTR_UID) &&
- (attr->ia_uid != SMB_SERVER(inode)->m.uid)))
+ if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->m.uid)))
goto out;
- if (((attr->ia_valid & ATTR_GID) &&
- (attr->ia_uid != SMB_SERVER(inode)->m.gid)))
+ if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->m.gid)))
goto out;
if (((attr->ia_valid & ATTR_MODE) &&
(attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
goto out;
- /*
- * Assume success and invalidate the parent's dir cache
- */
- smb_invalid_dir_cache(dentry->d_parent->d_inode);
-
if ((attr->ia_valid & ATTR_SIZE) != 0)
{
- if ((error = smb_open(dentry, O_WRONLY)) < 0)
+ error = smb_open(dentry, O_WRONLY);
+ if (error)
goto out;
-
- if ((error = smb_proc_trunc(SMB_SERVER(inode),
- inode->u.smbfs_i.fileid,
- attr->ia_size)) < 0)
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_notify_change: changing %s/%s, old size=%ld, new size=%ld\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+(long) inode->i_size, (long) attr->ia_size);
+#endif
+ error = smb_proc_trunc(server, inode->u.smbfs_i.fileid,
+ attr->ia_size);
+ if (error)
goto out;
+ /*
+ * We don't implement an i_op->truncate operation,
+ * so we have to update the page cache here.
+ */
+ if (attr->ia_size < inode->i_size) {
+ truncate_inode_pages(inode, attr->ia_size);
+ inode->i_size = attr->ia_size;
+ }
+ refresh = 1;
}
+
if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0)
{
-
struct smb_fattr fattr;
fattr.attr = 0;
if ((attr->ia_valid & ATTR_ATIME) != 0)
fattr.f_atime = attr->ia_atime;
- error = smb_proc_setattr(SMB_SERVER(inode), dentry, &fattr);
- if (error >= 0)
- {
- inode->i_ctime = fattr.f_ctime;
- inode->i_mtime = fattr.f_mtime;
- inode->i_atime = fattr.f_atime;
- }
+ error = smb_proc_setattr(server, dentry, &fattr);
+ if (error)
+ goto out;
+ refresh = 1;
}
+ error = 0;
+
out:
+ if (refresh)
+ {
+ /*
+ * N.B. Currently we're only using the dir cache for
+ * file names, so we don't need to invalidate here.
+ */
+#if 0
+ smb_invalid_dir_cache(dentry->d_parent->d_inode);
+#endif
+ smb_refresh_inode(inode);
+ }
return error;
}
smb_current_vmalloced = 0;
#endif
- smb_init_dir_cache();
read_semaphore = MUTEX;
return init_smb_fs();
cleanup_module(void)
{
pr_debug("smbfs: cleanup_module called\n");
- smb_free_dir_cache();
unregister_filesystem(&smb_fs_type);
#ifdef DEBUG_SMB_MALLOC
printk(KERN_DEBUG "smb_malloced: %d\n", smb_malloced);
* Copyright (C) 1997 by Volker Lendecke
*
* 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per
+ * 28/09/97 - Fixed smb_d_path [now smb_build_path()] to be non-recursive
+ * by Riccardo Facchetti
*/
#include <linux/config.h>
#include <linux/malloc.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
+#include <linux/dcache.h>
+#include <linux/dirent.h>
#include <asm/uaccess.h>
#include <asm/string.h>
/* #define SMBFS_DEBUG_VERBOSE 1 */
/* #define pr_debug printk */
-static int smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc);
-void smb_close_socket(struct smb_sb_info *);
+extern void smb_renew_times(struct dentry *);
static inline int
min(int a, int b)
}
}
+static void reverse_string(char *buf, int len) {
+ char c;
+ char *end = buf+len-1;
+
+ while(buf < end) {
+ c = *buf;
+ *(buf++) = *end;
+ *(end--) = c;
+ }
+}
+
/*****************************************************************************/
/* */
/* Encoding/Decoding section */
}
/*
- * Return the server for the specified dentry
- * N.B. Make this a #define in the smb header
+ * smb_build_path: build the path to entry and name storing it in buf.
+ * The path returned will have the trailing '\0'.
*/
-static struct smb_sb_info * server_from_dentry(struct dentry * dentry)
+static int smb_build_path(struct dentry * entry, struct qstr * name, char * buf)
{
- return &dentry->d_sb->u.smbfs_sb;
-}
+ char *path = buf;
-static int smb_d_path(struct dentry * entry, char * buf)
-{
+ if (entry == NULL)
+ goto test_name_and_out;
+
+ /*
+ * If IS_ROOT, we have to do no walking at all.
+ */
if (IS_ROOT(entry)) {
- *buf = '\\';
- return 1;
- } else {
- int len = smb_d_path(entry->d_parent, buf);
-
- buf += len;
- if (len > 1) {
- *buf++ = '\\';
- len++;
- }
- memcpy(buf, entry->d_name.name, entry->d_name.len);
- return len + entry->d_name.len;
+ *(path++) = '\\';
+ if (name != NULL)
+ goto name_and_out;
+ goto out;
+ }
+
+ /*
+ * Build the path string walking the tree backward from end to ROOT
+ * and store it in reversed order [see reverse_string()]
+ */
+ for (;;) {
+ memcpy(path, entry->d_name.name, entry->d_name.len);
+ reverse_string(path, entry->d_name.len);
+ path += entry->d_name.len;
+
+ *(path++) = '\\';
+
+ entry = entry->d_parent;
+
+ if (IS_ROOT(entry))
+ break;
+ }
+
+ reverse_string(buf, path-buf);
+
+test_name_and_out:
+ if (name != NULL) {
+ *(path++) = '\\';
+name_and_out:
+ memcpy(path, name->name, name->len);
+ path += name->len;
}
+out:
+ *(path++) = '\0';
+ return (path-buf);
}
static char *smb_encode_path(struct smb_sb_info *server, char *buf,
{
char *start = buf;
- if (dir != NULL)
- buf += smb_d_path(dir, buf);
-
- if (name != NULL) {
- *buf++ = '\\';
- memcpy(buf, name->name, name->len);
- buf += name->len;
- *buf++ = 0;
- }
+ buf += smb_build_path(dir, name, buf);
if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS)
str_upper(start);
(bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
}
+/*
+ * Returns the maximum read or write size for the current packet size
+ * and max_xmit value.
+ * N.B. Since this value is usually computed before locking the server,
+ * the server's packet size must never be decreased!
+ */
+static int
+smb_get_xmitsize(struct smb_sb_info *server, int overhead)
+{
+ int size = server->packet_size;
+
+ /*
+ * Start with the smaller of packet size and max_xmit ...
+ */
+ if (size > server->opt.max_xmit)
+ size = server->opt.max_xmit;
+ return size - overhead;
+}
+
+/*
+ * Calculate the maximum read size
+ */
+int
+smb_get_rsize(struct smb_sb_info *server)
+{
+ int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2;
+ int size = smb_get_xmitsize(server, overhead);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_get_rsize: packet=%d, xmit=%d, size=%d\n",
+server->packet_size, server->opt.max_xmit, size);
+#endif
+ return size;
+}
+
+/*
+ * Calculate the maximum write size
+ */
+int
+smb_get_wsize(struct smb_sb_info *server)
+{
+ int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2;
+ int size = smb_get_xmitsize(server, overhead);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_get_wsize: packet=%d, xmit=%d, size=%d\n",
+server->packet_size, server->opt.max_xmit, size);
+#endif
+ return size;
+}
+
static int
smb_errno(int errcls, int error)
{
up(&(server->sem));
}
-/* smb_request_ok: We expect the server to be locked. Then we do the
- request and check the answer completely. When smb_request_ok
- returns 0, you can be quite sure that everything went well. When
- the answer is <=0, the returned number is a valid unix errno. */
-
-static int
-smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
-{
- int result = 0;
-
- s->rcls = 0;
- s->err = 0;
-
- if (smb_request(s) < 0)
- {
- pr_debug("smb_request failed\n");
- result = -EIO;
- } else if (smb_valid_packet(s->packet) != 0)
- {
- pr_debug("not a valid packet!\n");
- result = -EIO;
- } else if (s->rcls != 0)
- {
- result = -smb_errno(s->rcls, s->err);
- } else if (smb_verify(s->packet, command, wct, bcc) != 0)
- {
- pr_debug("smb_verify failed\n");
- result = -EIO;
- }
- return result;
-}
-
/*
* smb_retry: This function should be called when smb_request_ok has
indicated an error. If the error was indicated because the
static int
smb_retry(struct smb_sb_info *server)
{
+ struct wait_queue wait = { current, NULL };
+ unsigned long timeout;
int result = 0;
if (server->state != CONN_INVALID)
goto out;
}
- printk("smb_retry: signalling process %d\n", server->conn_pid);
kill_proc(server->conn_pid, SIGUSR1, 0);
+#if 0
server->conn_pid = 0;
+#endif
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_retry: signalled pid %d, waiting for new connection\n",
+server->conn_pid);
+#endif
/*
- * Block here until we get a new connection.
- * N.B. This needs to be fixed ... we should wait in an
- * interruptible sleep for CONN_VALID.
+ * Wait here for a new connection.
*/
- printk("smb_retry: blocking for new connection\n");
- smb_lock_server(server);
+ timeout = jiffies + 10*HZ;
+ add_wait_queue(&server->wait, &wait);
+ while (1)
+ {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + HZ;
+ if (server->state != CONN_INVALID)
+ break;
+ if (jiffies > timeout)
+ {
+ printk("smb_retry: timed out, try again later\n");
+ break;
+ }
+ if (signal_pending(current))
+ {
+ printk("smb_retry: caught signal\n");
+ break;
+ }
+ schedule();
+ }
+ remove_wait_queue(&server->wait, &wait);
+ current->timeout = 0;
+ current->state = TASK_RUNNING;
if (server->state == CONN_VALID)
{
- printk("smb_retry: new connection pid=%d\n", server->conn_pid);
+#ifdef SMBFS_PARANOIA
+printk("smb_retry: new connection pid=%d\n", server->conn_pid);
+#endif
result = 1;
}
+
out:
return result;
}
+/* smb_request_ok: We expect the server to be locked. Then we do the
+ request and check the answer completely. When smb_request_ok
+ returns 0, you can be quite sure that everything went well. When
+ the answer is <=0, the returned number is a valid unix errno. */
+
+static int
+smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
+{
+ int result = 0;
+
+ s->rcls = 0;
+ s->err = 0;
+
+ /* Make sure we have a connection */
+ if (s->state != CONN_VALID && !smb_retry(s))
+ {
+ result = -EIO;
+ } else if (smb_request(s) < 0)
+ {
+ pr_debug("smb_request failed\n");
+ result = -EIO;
+ } else if (smb_valid_packet(s->packet) != 0)
+ {
+ pr_debug("not a valid packet!\n");
+ result = -EIO;
+ } else if (s->rcls != 0)
+ {
+ result = -smb_errno(s->rcls, s->err);
+ } else if (smb_verify(s->packet, command, wct, bcc) != 0)
+ {
+ pr_debug("smb_verify failed\n");
+ result = -EIO;
+ }
+ return result;
+}
+
/*
* This is called with the server locked after a successful smb_newconn().
* It installs the new connection pid, sets server->state to CONN_VALID,
- * and unlocks the server.
+ * and wakes up the process waiting for the new connection.
* N.B. The first call is made without locking the server -- need to fix!
*/
int
error = -EACCES;
if (!suser() && (current->uid != server->m.mounted_uid))
goto out;
+ if (atomic_read(&server->sem.count) == 1)
+ {
+ printk("smb_offerconn: server not locked, count=%d\n",
+ atomic_read(&server->sem.count));
+#if 0
+ goto out;
+#endif
+ }
server->conn_pid = current->pid;
server->state = CONN_VALID;
- printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid);
+ wake_up_interruptible(&server->wait);
+#ifdef SMBFS_PARANOIA
+printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid);
+#endif
error = 0;
- /*
- * The initial call may be made without the server locked?
- */
out:
- if (atomic_read(&server->sem.count) != 1)
- smb_unlock_server(server);
- else
- printk("smb_offerconn: server not locked, count=%d\n",
- atomic_read(&server->sem.count));
return error;
}
error = -EBADF;
if (opt->fd >= NR_OPEN || !(filp = current->files->fd[opt->fd]))
- goto out_unlock;
- if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode))
- goto out_unlock;
- if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode))
- goto out_unlock;
+ goto out;
+ if (!smb_valid_socket(filp->f_dentry->d_inode))
+ goto out;
error = -EACCES;
if (!suser() && (current->uid != server->m.mounted_uid))
- goto out_unlock;
+ goto out;
+ if (atomic_read(&server->sem.count) == 1)
+ {
+ printk("smb_newconn: server not locked, count=%d\n",
+ atomic_read(&server->sem.count));
+#if 0
+ goto out;
+#endif
+ }
/*
* Make sure the old socket is closed
server->sock_file = filp;
smb_catch_keepalive(server);
server->opt = *opt;
- pr_debug("smb_newconn: protocol = %d\n", server->opt.protocol);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_newconn: protocol=%d, max_xmit=%d\n",
+server->opt.protocol, server->opt.max_xmit);
+#endif
server->generation += 1;
error = 0;
out:
return error;
-
- /*
- * Unlock now if an error occurred.
- */
-out_unlock:
- if (atomic_read(&server->sem.count) != 1)
- smb_unlock_server(server);
- else
- printk("smb_newconn: server not locked, count=%d\n",
- atomic_read(&server->sem.count));
- goto out;
}
/* smb_setup_header: We completely set up the packet. You only have to
__u8 *p = server->packet;
__u8 *buf = server->packet;
+if (xmit_len > server->packet_size)
+printk("smb_setup_header: Aieee, xmit len > packet! len=%d, size=%d\n",
+xmit_len, server->packet_size);
+
p = smb_encode_smb_length(p, xmit_len - 4);
*p++ = 0xff;
}
/*
- * We're called with the server locked, and we leave it that way. We
- * try maximum permissions.
+ * We're called with the server locked, and we leave it that way.
+ * Set the permissions to be consistent with the desired access.
*/
static int
-smb_proc_open(struct smb_sb_info *server, struct dentry *dir)
+smb_proc_open(struct smb_sb_info *server, struct dentry *dir, int wish)
{
struct inode *ino = dir->d_inode;
+ int mode, read_write = 0x42, read_only = 0x40;
int error;
char *p;
+ mode = read_write;
+#if 0
+ if (!(wish & (O_WRONLY | O_RDWR)))
+ mode = read_only;
+#endif
+
retry:
p = smb_setup_header(server, SMBopen, 2, 0);
- WSET(server->packet, smb_vwv0, 0x42); /* read/write */
+ WSET(server->packet, smb_vwv0, mode);
WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
*p++ = 4;
p = smb_encode_path(server, p, dir, NULL);
smb_setup_bcc(server, p);
- if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ error = smb_request_ok(server, SMBopen, 7, 0);
+ if (error != 0)
{
if (smb_retry(server))
goto retry;
- if ((error != -EACCES) && (error != -ETXTBSY)
- && (error != -EROFS))
- goto out;
-
- p = smb_setup_header(server, SMBopen, 2, 0);
- WSET(server->packet, smb_vwv0, 0x40); /* read only */
- WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
- *p++ = 4;
- p = smb_encode_path(server, p, dir, NULL);
- smb_setup_bcc(server, p);
-
- if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ if (mode == read_write &&
+ (error == -EACCES || error == -ETXTBSY || error == -EROFS))
{
- if (smb_retry(server))
- goto retry;
- goto out;
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_open: %s/%s open failed, error=%d, retrying R/O\n",
+dir->d_parent->d_name.name, dir->d_name.name, error);
+#endif
+ mode = read_only;
+ goto retry;
}
}
/* We should now have data in vwv[0..6]. */
ino->u.smbfs_i.fileid = WVAL(server->packet, smb_vwv0);
ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1);
+ /* smb_vwv2 has mtime */
+ /* smb_vwv4 has size */
ino->u.smbfs_i.access = WVAL(server->packet, smb_vwv6);
ino->u.smbfs_i.access &= 3;
+ /* N.B. Suppose the open failed?? */
ino->u.smbfs_i.open = server->generation;
- pr_debug("smb_proc_open: entry->access = %d\n", ino->u.smbfs_i.access);
-out:
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_open: error=%d, access=%d\n", error, ino->u.smbfs_i.access);
+#endif
return error;
}
int
-smb_open(struct dentry *dir, int wish)
+smb_open(struct dentry *dentry, int wish)
{
- struct inode *i = dir->d_inode;
+ struct inode *i = dentry->d_inode;
int result;
- result = -EIO;
+ result = -ENOENT;
if (!i)
{
printk("smb_open: no inode for dentry %s/%s\n",
- dir->d_parent->d_name.name, dir->d_name.name);
+ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out;
}
/*
- * If the inode is already open, we don't need to lock the server.
+ * Note: If the caller holds an active dentry and the file is
+ * currently open, we can be sure that the file isn't about
+ * to be closed. (See smb_close_dentry() below.)
*/
if (!smb_is_open(i))
{
struct smb_sb_info *server = SMB_SERVER(i);
smb_lock_server(server);
- result = smb_proc_open(server, dir);
+ result = 0;
+ if (!smb_is_open(i))
+ result = smb_proc_open(server, dentry, wish);
smb_unlock_server(server);
if (result)
{
- printk("smb_open: %s/%s open failed, result=%d\n",
- dir->d_parent->d_name.name, dir->d_name.name,
- result);
+#ifdef SMBFS_PARANOIA
+printk("smb_open: %s/%s open failed, result=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, result);
+#endif
goto out;
}
+ /*
+ * A successful open means the path is still valid ...
+ */
+ smb_renew_times(dentry);
}
result = -EACCES;
/* We're called with the server locked */
-static int smb_proc_close(struct smb_sb_info *server,
- __u16 fileid, __u32 mtime)
+static int
+smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime)
{
smb_setup_header(server, SMBclose, 3, 0);
WSET(server->packet, smb_vwv0, fileid);
- DSET(server->packet, smb_vwv1, mtime);
+ DSET(server->packet, smb_vwv1, utc2local(mtime));
return smb_request_ok(server, SMBclose, 0, 0);
}
+/*
+ * Called with the server locked
+ */
+static int
+smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino)
+{
+ int result = 0;
+ if (smb_is_open(ino))
+ {
+ /*
+ * We clear the open flag in advance, in case another
+ * process observes the value while we block below.
+ */
+ ino->u.smbfs_i.open = 0;
+ result = smb_proc_close(server, ino->u.smbfs_i.fileid,
+ ino->i_mtime);
+ }
+ return result;
+}
+
int
smb_close(struct inode *ino)
{
{
struct smb_sb_info *server = SMB_SERVER(ino);
smb_lock_server(server);
- result = smb_proc_close(server, ino->u.smbfs_i.fileid,
- ino->i_mtime);
+ result = smb_proc_close_inode(server, ino);
smb_unlock_server(server);
- ino->u.smbfs_i.open = 0;
}
return result;
}
+/*
+ * This routine is called from dput() when d_count is going to 0.
+ * We use this to close the file so that cached dentries don't
+ * keep too many files open.
+ *
+ * There are some tricky race conditions here: the dentry may go
+ * back into use while we're closing the file, and we don't want
+ * the new user to be confused as to the open status.
+ */
+void
+smb_close_dentry(struct dentry * dentry)
+{
+ struct inode *ino = dentry->d_inode;
+
+ if (ino)
+ {
+ if (smb_is_open(ino))
+ {
+ struct smb_sb_info *server = SMB_SERVER(ino);
+ smb_lock_server(server);
+ /*
+ * Check whether the dentry is back in use.
+ */
+ if (dentry->d_count <= 1)
+ {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_close_dentry: closing %s/%s, count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+#endif
+ smb_proc_close_inode(server, ino);
+ }
+ smb_unlock_server(server);
+ }
+ }
+ /* Consider dropping negative dentries? */
+#if 0
+ else
+ d_drop(dentry);
+#endif
+}
+
/* In smb_proc_read and smb_proc_write we do not retry, because the
file-id would not be valid after a reconnection. */
-/* smb_proc_read: fs indicates if it should be copied with
- copy_to_user. */
-
int
-smb_proc_read(struct inode *ino, off_t offset, long count, char *data)
+smb_proc_read(struct inode *ino, off_t offset, int count, char *data)
{
struct smb_sb_info *server = SMB_SERVER(ino);
__u16 returned_count, data_len;
char *buf;
int result;
- struct dentry * dentry;
-
- if (!ino || !(dentry = ino->u.smbfs_i.dentry))
- {
- printk("smb_proc_read: no inode!\n");
- return -EIO;
- }
smb_lock_server(server);
smb_setup_header(server, SMBread, 5, 0);
-
- /* Achtung! Do not refer to the cached packet after the request! */
buf = server->packet;
WSET(buf, smb_vwv0, ino->u.smbfs_i.fileid);
WSET(buf, smb_vwv1, count);
result = smb_request_ok(server, SMBread, 5, -1);
if (result < 0)
goto out;
-#if 0
-printk("smb_proc_read: file %s/%s, result=%d, packet=%p\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, result, server->packet);
-#endif
returned_count = WVAL(server->packet, smb_vwv0);
buf = SMB_BUF(server->packet);
result = data_len;
out:
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_read: file %s/%s, count=%d, result=%d\n",
+((struct dentry *) ino->u.smbfs_i.dentry)->d_parent->d_name.name,
+((struct dentry *) ino->u.smbfs_i.dentry)->d_name.name, count, result);
+#endif
smb_unlock_server(server);
return result;
}
smb_proc_write(struct inode *ino, off_t offset, int count, const char *data)
{
struct smb_sb_info *server = SMB_SERVER(ino);
- int res = 0;
+ int result;
__u8 *p;
smb_lock_server(server);
+#if SMBFS_DEBUG_VERBOSE
+{struct dentry * dentry = ino->u.smbfs_i.dentry;
+printk("smb_proc_write: file %s/%s, count=%d@%ld, packet_size=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+count, offset, server->packet_size);
+}
+#endif
p = smb_setup_header(server, SMBwrite, 5, count + 3);
WSET(server->packet, smb_vwv0, ino->u.smbfs_i.fileid);
WSET(server->packet, smb_vwv1, count);
WSET(p, 0, count);
memcpy(p+2, data, count);
- if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
- res = WVAL(server->packet, smb_vwv0);
+ if ((result = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
+ result = WVAL(server->packet, smb_vwv0);
smb_unlock_server(server);
- return res;
+ return result;
}
int
smb_init_dirent(server, fattr);
fattr->attr = aDIR;
fattr->f_ino = 1;
+ fattr->f_mtime = CURRENT_TIME;
smb_finish_dirent(server, fattr);
}
static __u8 *
-smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
- struct smb_dirent *entry)
+smb_decode_dirent(struct smb_sb_info *server, __u8 *p, struct dirent *entry)
{
- smb_init_dirent(server, &(entry->attr));
+ int len;
p += SMB_STATUS_SIZE; /* reserved (search_status) */
- entry->attr.attr = *p;
- entry->attr.f_mtime = entry->attr.f_atime = entry->attr.f_ctime =
- date_dos2unix(WVAL(p, 1), WVAL(p, 3));
- entry->attr.f_size = DVAL(p, 5);
- entry->len = strlen(p + 9);
- if (entry->len > 12)
- {
- entry->len = 12;
- }
- memcpy(entry->name, p + 9, entry->len);
- entry->name[entry->len] = '\0';
- while (entry->len > 2)
+ len = strlen(p + 9);
+ if (len > 12)
{
- /* Pathworks fills names with spaces */
- entry->len -= 1;
- if (entry->name[entry->len] == ' ')
- {
- entry->name[entry->len] = '\0';
- }
+ len = 12;
}
+ memcpy(entry->d_name, p + 9, len);
+#ifdef SMBFS_TRIM_BLANKS
+ /*
+ * Trim trailing blanks for Pathworks servers
+ */
+ while (len > 2 && entry->d_name[len-1] == ' ')
+ len--;
+#endif
+ entry->d_name[len] = '\0';
+ entry->d_reclen = len;
+ entry->d_ino = 0; /* no inode number available */
+
switch (server->opt.case_handling)
{
case SMB_CASE_UPPER:
- str_upper(entry->name);
+ str_upper(entry->d_name);
break;
case SMB_CASE_LOWER:
- str_lower(entry->name);
+ str_lower(entry->d_name);
break;
default:
break;
}
pr_debug("smb_decode_dirent: name = %s\n", entry->name);
- smb_finish_dirent(server, &(entry->attr));
return p + 22;
}
static int
smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
- int cache_size, struct smb_dirent *entry)
+ void *cachep)
{
char *p;
- char *buf;
- int error;
int result;
- int i;
- int first, total_count;
- struct smb_dirent *current_entry;
+ int i, first, entries_seen, entries;
+ int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE;
__u16 bcc;
__u16 count;
char status[SMB_STATUS_SIZE];
- int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE;
-
static struct qstr mask = { "*.*", 3, 0 };
- pr_debug("SMB call readdir %d @ %d\n", cache_size, fpos);
+ pr_debug("smb_proc_readdir_short: %d @ %d\n", cache_size, fpos);
smb_lock_server(server);
+ /* N.B. We need to reinitialize the cache to restart */
retry:
+ smb_init_dircache(cachep);
first = 1;
- total_count = 0;
- current_entry = entry;
+ entries = 0;
+ entries_seen = 2; /* implicit . and .. */
while (1)
{
- buf = server->packet;
+ p = smb_setup_header(server, SMBsearch, 2, 0);
+ WSET(server->packet, smb_vwv0, entries_asked);
+ WSET(server->packet, smb_vwv1, aDIR);
+ *p++ = 4;
if (first == 1)
{
- p = smb_setup_header(server, SMBsearch, 2, 0);
- WSET(buf, smb_vwv0, entries_asked);
- WSET(buf, smb_vwv1, aDIR);
- *p++ = 4;
p = smb_encode_path(server, p, dir, &mask);
*p++ = 5;
WSET(p, 0, 0);
p += 2;
+ first = 0;
} else
{
- p = smb_setup_header(server, SMBsearch, 2, 0);
- WSET(buf, smb_vwv0, entries_asked);
- WSET(buf, smb_vwv1, aDIR);
- *p++ = 4;
*p++ = 0;
*p++ = 5;
WSET(p, 0, SMB_STATUS_SIZE);
smb_setup_bcc(server, p);
- if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0)
+ result = smb_request_ok(server, SMBsearch, 1, -1);
+ if (result < 0)
{
- if ((server->rcls == ERRDOS)
- && (server->err == ERRnofiles))
- {
- result = total_count - fpos;
- goto unlock_return;
- } else
- {
- if (smb_retry(server))
- {
- goto retry;
- }
- result = error;
- goto unlock_return;
- }
+ if ((server->rcls == ERRDOS) &&
+ (server->err == ERRnofiles))
+ break;
+ if (smb_retry(server))
+ goto retry;
+ goto unlock_return;
}
p = SMB_VWV(server->packet);
count = WVAL(p, 0);
- bcc = WVAL(p, 2);
-
- first = 0;
-
if (count <= 0)
- {
- result = total_count - fpos;
- goto unlock_return;
- }
+ break;
+
+ result = -EIO;
+ bcc = WVAL(p, 2);
if (bcc != count * SMB_DIRINFO_SIZE + 3)
- {
- result = -EIO;
goto unlock_return;
- }
p += 7;
/* Read the last entry into the status field. */
for (i = 0; i < count; i++)
{
- if (total_count < fpos)
- {
- p += SMB_DIRINFO_SIZE;
- pr_debug("smb_proc_readdir: skipped entry.\n");
- pr_debug(" total_count = %d\n"
- " i = %d, fpos = %d\n",
- total_count, i, fpos);
- } else if (total_count >= fpos + cache_size)
+ struct dirent this_ent, *entry = &this_ent;
+
+ p = smb_decode_dirent(server, p, entry);
+ if (entries_seen == 2 && entry->d_name[0] == '.')
{
- result = total_count - fpos;
- goto unlock_return;
- } else
+ if (entry->d_reclen == 1)
+ continue;
+ if (entry->d_name[1] == '.' &&
+ entry->d_reclen == 2)
+ continue;
+ }
+ if (entries_seen >= fpos)
{
- p = smb_decode_dirent(server, p,
- current_entry);
- current_entry->f_pos = total_count;
- pr_debug("smb_proc_readdir: entry->f_pos = "
- "%u\n", entry->f_pos);
- current_entry += 1;
+ pr_debug("smb_proc_readdir: fpos=%u\n",
+ entries_seen);
+ smb_add_to_cache(cachep, entry, entries_seen);
+ entries++;
}
- total_count += 1;
+#ifdef SMBFS_DEBUG_VERBOSE
+else
+printk("smb_proc_readdir: skipped, seen=%d, i=%d, fpos=%d\n",
+entries_seen, i, fpos);
+#endif
+ entries_seen++;
}
}
- unlock_return:
+ result = entries;
+
+ unlock_return:
smb_unlock_server(server);
return result;
}
-/* interpret a long filename structure - this is mostly guesses at the
- moment. The length of the structure is returned. The structure of
- a long filename depends on the info level. 260 is used by NT and 2
- is used by OS/2. */
-
+/*
+ * Interpret a long filename structure using the specified info level:
+ * level 1 -- Win NT, Win 95, OS/2
+ * level 2 -- OS/2
+ * level 259 -- File name and length only, Win NT, Win 95
+ * level 260 -- Win NT, Win 95
+ * There seem to be numerous inconsistencies and bugs in implementation.
+ */
static char *
smb_decode_long_dirent(struct smb_sb_info *server, char *p,
- struct smb_dirent *entry, int level)
+ struct dirent *entry, int level)
{
char *result;
unsigned int len;
- smb_init_dirent(server, &(entry->attr));
+ /*
+ * SMB doesn't have a concept of inode numbers ...
+ */
+ entry->d_ino = 0;
switch (level)
{
- /* We might add more levels later... */
case 1:
- entry->attr.f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
- entry->attr.f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
- entry->attr.f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
- entry->attr.f_size = DVAL(p, 16);
- entry->attr.attr = *(p+24);
- /*
- * Achtung, lengths can go up to 255
- */
len = *((unsigned char *) p + 26);
- entry->len = len;
- strncpy(entry->name, p + 27, len);
- entry->name[len] = '\0';
+ entry->d_reclen = len;
+ strncpy(entry->d_name, p + 27, len);
+ entry->d_name[len] = '\0';
result = p + 28 + len;
break;
+ case 259: /* SMB_FIND_FILE_NAMES_INFO = 0x103 */
+ /*
+ * This info level returns just the file name and length,
+ * which is all we need right now.
+ */
+ result = p + DVAL(p, 0);
+ /* DVAL(p, 4) should be resume key? Seems to be 0 .. */
+ len = DVAL(p, 8);
+ if (len > 255)
+ len = 255;
+ strncpy(entry->d_name, p + 12, len);
+ /*
+ * Kludge alert: Win NT 4.0 adds a trailing null byte and
+ * counts it in the name length, but Win 95 doesn't. Hence
+ * we test for a trailing null and decrement the length ...
+ */
+ if (len && entry->d_name[len-1] == '\0')
+ len--;
+ entry->d_name[len] = '\0';
+ entry->d_reclen = len;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_decode_long_dirent: info 259, len=%d, name=%s\n",
+len, entry->d_name);
+#endif
+ break;
+
default:
- printk("smb_decode: Unknown long filename format %d\n", level);
+ printk("smb_decode_long_dirent: Unknown level %d\n", level);
result = p + WVAL(p, 0);
}
switch (server->opt.case_handling)
{
case SMB_CASE_UPPER:
- str_upper(entry->name);
+ str_upper(entry->d_name);
break;
case SMB_CASE_LOWER:
- str_lower(entry->name);
+ str_lower(entry->d_name);
break;
default:
break;
}
- smb_finish_dirent(server, &(entry->attr));
-
return result;
}
static int
smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
- int cache_size, struct smb_dirent *cache)
+ void *cachep)
{
- /* NT uses 260, OS/2 uses 2. Both accept 1. */
- const int info_level = 1;
+ /* Both NT and OS/2 accept info level 1 (but see note below). */
+ int info_level = 1;
const int max_matches = 512;
- char *p;
- char *lastname;
- unsigned lastname_len;
- int i;
+ char *p, *mask, *lastname;
int first, entries, entries_seen;
unsigned char *resp_data = NULL;
__u16 command;
- int result;
-
- int ff_resume_key = 0;
+ int ff_resume_key = 0; /* this isn't being used */
int ff_searchcount = 0;
int ff_eos = 0;
int ff_lastname = 0;
int ff_dir_handle = 0;
int loop_count = 0;
+ int mask_len, i, result;
- char param[SMB_MAXPATHLEN + 2 + 12]; /* too long for the stack! */
- int mask_len;
- char *mask = &(param[12]);
-
+ char param[12 + SMB_MAXPATHLEN + 2]; /* too long for the stack! */
static struct qstr star = { "*", 1, 0 };
- mask_len = smb_encode_path(server, mask, dir, &star) - mask;
-
- mask[mask_len] = 0;
- mask[mask_len + 1] = 0;
-
- pr_debug("smb_readdir_long cache=%d, fpos=%d, mask=%s\n",
- cache_size, fpos, mask);
+ /*
+ * Check whether to change the info level. There appears to be
+ * a bug in Win NT 4.0's handling of info level 1, whereby it
+ * truncates the directory scan for certain patterns of files.
+ * Hence we use level 259 for NT. (Win 95 uses this too?)
+ */
+ if (server->opt.protocol >= SMB_PROTOCOL_NT1)
+ info_level = 259;
smb_lock_server(server);
retry:
-
+ /*
+ * Encode the initial path
+ */
+ mask = &(param[12]);
+ mask_len = smb_encode_path(server, mask, dir, &star) - mask;
first = 1;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_long: starting fpos=%d, mask=%s\n", fpos, mask);
+#endif
+ /*
+ * We must reinitialize the dircache when retrying.
+ */
+ smb_init_dircache(cachep);
entries = 0;
entries_seen = 2;
+ ff_eos = 0;
while (ff_eos == 0)
{
entries = -EIO;
break;
}
+
if (first != 0)
{
command = TRANSACT2_FINDFIRST;
} else
{
command = TRANSACT2_FINDNEXT;
- pr_debug("hand=0x%X resume=%d ff_lastnm=%d mask=%s\n",
- ff_dir_handle, ff_resume_key, ff_lastname,
- mask);
- WSET(param, 0, ff_dir_handle);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_long: handle=0x%X, resume=%d, lastname=%d, mask=%s\n",
+ff_dir_handle, ff_resume_key, ff_lastname, mask);
+#endif
+ WSET(param, 0, ff_dir_handle); /* search handle */
WSET(param, 2, max_matches); /* max count */
WSET(param, 4, info_level);
DSET(param, 6, ff_resume_key); /* ff_resume_key */
}
result = smb_trans2_request(server, command,
- 0, NULL, 12 + mask_len + 2, param,
+ 0, NULL, 12 + mask_len + 1, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
{
if (smb_retry(server))
{
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_readdir_long: error=%d, retrying\n", result);
+#endif
goto retry;
}
#ifdef SMBFS_PARANOIA
-printk("smb_proc_readdir_long: trans2_request error=%d\n", result);
+printk("smb_proc_readdir_long: error=%d, breaking\n", result);
#endif
entries = result;
break;
if (server->rcls != 0)
{
#ifdef SMBFS_PARANOIA
-printk("smb_proc_readdir_long: error, rcls=%d, err=%d\n",
+printk("smb_proc_readdir_long: rcls=%d, err=%d, breaking\n",
server->rcls, server->err);
#endif
- /* Why isn't this considered an error? */
- /* entries = -EIO; */
+ entries = -smb_errno(server->rcls, server->err);
break;
}
+#ifdef SMBFS_PARANOIA
+if (resp_data + resp_data_len > server->packet + server->packet_size)
+printk("s_p_r_l: data past packet end! data=%p, len=%d, packet=%p\n",
+resp_data + resp_data_len, resp_data_len, server->packet + server->packet_size);
+#endif
/* parse out some important return info */
if (first != 0)
{
break;
}
- /* point to the data bytes */
- p = resp_data;
/* we might need the lastname for continuations */
- lastname = "";
- lastname_len = 0;
+ mask_len = 0;
if (ff_lastname > 0)
{
- ff_resume_key = 0;
- lastname = p + ff_lastname;
+ lastname = resp_data + ff_lastname;
switch (info_level)
{
case 260:
- lastname_len = resp_data_len - ff_lastname;
+ if (ff_lastname < resp_data_len)
+ mask_len = resp_data_len - ff_lastname;
break;
case 1:
- lastname_len = *((unsigned char *) lastname++);
+ /* Win NT 4.0 doesn't set the length byte */
+ lastname++;
+ if (ff_lastname + 2 < resp_data_len)
+ mask_len = strlen(lastname);
break;
}
+ /*
+ * Update the mask string for the next message.
+ */
+ if (mask_len > 255)
+ mask_len = 255;
+ if (mask_len)
+ strncpy(mask, lastname, mask_len);
+ ff_resume_key = 0;
}
- lastname_len = min(lastname_len, 256);
- strncpy(mask, lastname, lastname_len);
- mask[lastname_len] = '\0';
-
+ mask[mask_len] = 0;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_long: new mask, len=%d@%d, mask=%s\n",
+mask_len, ff_lastname, mask);
+#endif
/* Now we are ready to parse smb directory entries. */
+ /* point to the data bytes */
+ p = resp_data;
for (i = 0; i < ff_searchcount; i++)
{
- struct smb_dirent *entry = &(cache[entries]);
+ struct dirent this_ent, *entry = &this_ent;
- p = smb_decode_long_dirent(server, p,
- entry, info_level);
+ p = smb_decode_long_dirent(server, p, entry,
+ info_level);
pr_debug("smb_readdir_long: got %s\n", entry->name);
- if ((entry->name[0] == '.')
- && ((entry->name[1] == '\0')
- || ((entry->name[1] == '.')
- && (entry->name[2] == '\0'))))
+ /* ignore . and .. from the server */
+ if (entries_seen == 2 && entry->d_name[0] == '.')
{
- /* ignore . and .. from the server */
- continue;
+ if (entry->d_reclen == 1)
+ continue;
+ if (entry->d_name[1] == '.' &&
+ entry->d_reclen == 2)
+ continue;
}
if (entries_seen >= fpos)
{
- entry->f_pos = entries_seen;
+ smb_add_to_cache(cachep, entry, entries_seen);
entries += 1;
}
- entries_seen += 1;
- if (entries < cache_size)
- continue;
-
- /* cache is full */
- goto finished;
+ entries_seen++;
}
- pr_debug("received %d entries (eos=%d resume=%d)\n",
- ff_searchcount, ff_eos, ff_resume_key);
-
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_long: received %d entries, eos=%d, resume=%d\n",
+ff_searchcount, ff_eos, ff_resume_key);
+#endif
first = 0;
}
- finished:
smb_unlock_server(server);
return entries;
}
int
-smb_proc_readdir(struct dentry *dir, int fpos,
- int cache_size, struct smb_dirent *entry)
+smb_proc_readdir(struct dentry *dir, int fpos, void *cachep)
{
struct smb_sb_info *server;
server = server_from_dentry(dir);
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
- return smb_proc_readdir_long(server, dir, fpos, cache_size,
- entry);
+ return smb_proc_readdir_long(server, dir, fpos, cachep);
else
- return smb_proc_readdir_short(server, dir, fpos, cache_size,
- entry);
+ return smb_proc_readdir_short(server, dir, fpos, cachep);
}
static int
smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
struct qstr *name, struct smb_fattr *attr)
{
- char param[SMB_MAXPATHLEN + 20];
char *p;
int result;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
+ char param[SMB_MAXPATHLEN + 20]; /* too big for the stack! */
+ smb_lock_server(server);
+
+ retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0);
p = smb_encode_path(server, param + 6, dir, name);
- smb_lock_server(server);
- retry:
result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
0, NULL, p - param, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
-
- if (server->rcls != 0)
- {
- result = -smb_errno(server->rcls, server->err);
- goto out;
- }
if (result < 0)
{
if (smb_retry(server))
goto retry;
goto out;
}
+ if (server->rcls != 0)
+ {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_getattr_trans2: for %s: result=%d, rcls=%d, err=%d\n",
+¶m[6], result, server->rcls, server->err);
+#endif
+ result = -smb_errno(server->rcls, server->err);
+ goto out;
+ }
result = -ENOENT;
if (resp_data_len < 22)
goto out;
struct smb_fattr *fattr)
{
struct smb_sb_info *server;
- int result = -1;
+ int result;
server = server_from_dentry(dir);
smb_init_dirent(server, fattr);
+ /*
+ * N.B. Why would we want to fall back to xxx_core on error?
+ * If the file doesn't exist (a very common case), the core
+ * protocol won't find it either.
+ */
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
result = smb_proc_getattr_trans2(server, dir, name, fattr);
-
- if (result < 0)
+ else
result = smb_proc_getattr_core(server, dir, name, fattr);
smb_finish_dirent(server, fattr);
smb_proc_setattr_trans2(struct smb_sb_info *server,
struct dentry *dir, struct smb_fattr *fattr)
{
- char param[SMB_MAXPATHLEN + 20];
- char data[26];
char *p;
int result;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
+ char param[SMB_MAXPATHLEN + 20]; /* too long for the stack! */
+ char data[26];
+
+ smb_lock_server(server);
+ retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0);
p = smb_encode_path(server, param + 6, dir, NULL);
WSET(data, 20, fattr->attr);
WSET(data, 22, 0);
- smb_lock_server(server);
- retry:
result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
26, data, p - param, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
-
- if (server->rcls != 0)
- {
- smb_unlock_server(server);
- return -smb_errno(server->rcls, server->err);
- }
if (result < 0)
+ {
if (smb_retry(server))
goto retry;
+ goto out;
+ }
+ if (server->rcls != 0)
+ result = -smb_errno(server->rcls, server->err);
+out:
smb_unlock_server(server);
return 0;
}
smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *fattr)
{
- int result = -1;
+ int result;
+ /*
+ * N.B. Why would we want to fall back to xxx_core on error?
+ */
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
result = smb_proc_setattr_trans2(server, dir, fattr);
-
- if (result < 0)
+ else
result = smb_proc_setattr_core(server, dir, fattr);
return result;
}
}
+int
+smb_valid_socket(struct inode * inode)
+{
+ return (inode && S_ISSOCK(inode->i_mode) &&
+ inode->u.socket_i.type == SOCK_STREAM);
+}
+
static struct socket *
server_sock(struct smb_sb_info *server)
{
struct file *file;
- struct inode *inode;
-
- if (server &&
- (file = server->sock_file) &&
- (inode = file->f_dentry->d_inode) &&
- S_ISSOCK(inode->i_mode) &&
- inode->u.socket_i.type == SOCK_STREAM)
- return &(inode->u.socket_i);
+
+ if (server && (file = server->sock_file))
+ {
+#ifdef SMBFS_PARANOIA
+ if (!smb_valid_socket(file->f_dentry->d_inode))
+ printk("smb_server_sock: bad socket!\n");
+#endif
+ return &file->f_dentry->d_inode->u.socket_i;
+ }
return NULL;
}
if (file)
{
- struct socket * socket = server_sock(server);
-
- printk("smb_close_socket: closing socket %p\n", socket);
- /*
- * We need a way to check for tasks running the callback!
- */
- if (socket->sk->data_ready == smb_data_callback)
- printk("smb_close_socket: still catching keepalives!\n");
-
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_close_socket: closing socket %p\n", server_sock(server));
+#endif
+#ifdef SMBFS_PARANOIA
+if (server_sock(server)->sk->data_ready == smb_data_callback)
+printk("smb_close_socket: still catching keepalives!\n");
+#endif
server->sock_file = NULL;
close_fp(file);
}
if (result < 0)
{
- pr_debug("smb_get_length: recv error = %d\n", -result);
+#ifdef SMBFS_PARANOIA
+printk("smb_get_length: recv error = %d\n", -result);
+#endif
return result;
}
switch (peek_buf[0])
goto re_recv;
default:
- pr_debug("smb_get_length: Invalid NBT packet\n");
+#ifdef SMBFS_PARANOIA
+printk("smb_get_length: Invalid NBT packet, code=%x\n", peek_buf[0]);
+#endif
return -EIO;
}
smb_receive(struct smb_sb_info *server)
{
struct socket *socket = server_sock(server);
- int len;
- int result;
+ int len, result;
unsigned char peek_buf[4];
- len = smb_get_length(socket, peek_buf);
-
- if (len < 0)
- {
- return len;
- }
+ result = smb_get_length(socket, peek_buf);
+ if (result < 0)
+ goto out;
+ len = result;
+ /*
+ * Some servers do not respect our max_xmit and send
+ * larger packets. Try to allocate a new packet,
+ * but don't free the old one unless we succeed.
+ */
if (len + 4 > server->packet_size)
{
- /* Some servers do not care about our max_xmit. They
- send larger packets */
+ char * packet;
pr_debug("smb_receive: Increase packet size from %d to %d\n",
server->packet_size, len + 4);
+ result = -ENOMEM;
+ packet = smb_vmalloc(len + 4);
+ if (packet == NULL)
+ goto out;
smb_vfree(server->packet);
- server->packet = 0;
- server->packet_size = 0;
- server->packet = smb_vmalloc(len + 4);
- if (server->packet == NULL)
- {
- return -ENOMEM;
- }
+ server->packet = packet;
server->packet_size = len + 4;
}
memcpy(server->packet, peek_buf, 4);
result = smb_receive_raw(socket, server->packet + 4, len);
-
if (result < 0)
{
- pr_debug("smb_receive: receive error: %d\n", result);
- return result;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_receive: receive error: %d\n", result);
+#endif
+ goto out;
}
server->rcls = *(server->packet+9);
server->err = WVAL(server->packet, 11);
if (server->rcls != 0)
printk("smb_receive: rcls=%d, err=%d\n", server->rcls, server->err);
#endif
+out:
return result;
}
+/*
+ * This routine needs a lot of work. We should check whether the packet
+ * is all one part before allocating a new one, and should try first to
+ * copy to a temp buffer before allocating.
+ * The final server->packet should be the larger of the two.
+ */
static int
smb_receive_trans2(struct smb_sb_info *server,
int *ldata, unsigned char **data,
*ldata = data_len;
*lparam = param_len;
+#ifdef SMBFS_PARANOIA
+if (buf_len < server->packet_size)
+printk("smb_receive_trans2: changing packet, old size=%d, new size=%d\n",
+server->packet_size, buf_len);
+#endif
smb_vfree(server->packet);
server->packet = rcv_buf;
server->packet_size = buf_len;
unsigned char *buffer;
result = -EBADF;
- if (!server) /* this can't happen */
- goto bad_no_server;
-
buffer = server->packet;
if (!buffer)
goto bad_no_packet;
return result;
bad_conn:
- printk("smb_request: result %d, setting invalid\n", result);
+#ifdef SMBFS_PARANOIA
+printk("smb_request: result %d, setting invalid\n", result);
+#endif
server->state = CONN_INVALID;
smb_invalidate_inodes(server);
goto out;
-bad_no_server:
- printk("smb_request: no server!\n");
- goto out;
bad_no_packet:
printk("smb_request: no packet!\n");
goto out;
struct iovec iov[4];
struct msghdr msg;
+ /* N.B. This test isn't valid! packet_size may be < max_xmit */
if ((bcc + oparam) > server->opt.max_xmit)
{
return -ENOMEM;
WSET(server->packet, smb_tpscnt, lparam);
WSET(server->packet, smb_tdscnt, ldata);
+ /* N.B. these values should reflect out current packet size */
WSET(server->packet, smb_mprcnt, TRANS2_MAX_TRANSFER);
WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER);
WSET(server->packet, smb_msrcnt, 0);
return result;
bad_conn:
- printk("smb_trans2_request: connection bad, setting invalid\n");
+#ifdef SMBFS_PARANOIA
+printk("smb_trans2_request: connection bad, setting invalid\n");
+#endif
server->state = CONN_INVALID;
smb_invalidate_inodes(server);
goto out;
* Added change_root: Werner Almesberger & Hans Lermen, Feb '96
*/
-#include <stdarg.h>
-
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
/*
* Whee.. Weird sysv syscall.
*/
-asmlinkage int sys_sysfs(int option, ...)
+asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2)
{
- va_list args;
int retval = -EINVAL;
- unsigned int index;
lock_kernel();
- va_start(args, option);
switch (option) {
case 1:
- retval = fs_index(va_arg(args, const char *));
+ retval = fs_index((const char *) arg1);
break;
case 2:
- index = va_arg(args, unsigned int);
- retval = fs_name(index, va_arg(args, char *));
+ retval = fs_name(arg1, (char *) arg2);
break;
case 3:
retval = fs_maxindex();
break;
}
- va_end(args);
unlock_kernel();
return retval;
}
struct file_system_type * fstype;
struct dentry * dentry = NULL;
struct inode * inode = NULL;
- struct file_operations * fops;
kdev_t dev;
int retval = -EPERM;
- const char * t;
unsigned long flags = 0;
unsigned long page = 0;
+ struct file dummy; /* allows read-write or read-only flag */
lock_kernel();
if (!suser())
free_page(page);
goto out;
}
+
retval = copy_mount_options (type, &page);
if (retval < 0)
goto out;
retval = -ENODEV;
if (!fstype)
goto out;
- t = fstype->name;
- fops = NULL;
+
+ memset(&dummy, 0, sizeof(dummy));
if (fstype->fs_flags & FS_REQUIRES_DEV) {
dentry = namei(dev_name);
retval = PTR_ERR(dentry);
if (MAJOR(dev) >= MAX_BLKDEV)
goto dput_and_out;
- fops = get_blkfops(MAJOR(dev));
retval = -ENOTBLK;
- if (!fops)
+ dummy.f_op = get_blkfops(MAJOR(dev));
+ if (!dummy.f_op)
goto dput_and_out;
- if (fops->open) {
- struct file dummy; /* allows read-write or read-only flag */
- memset(&dummy, 0, sizeof(dummy));
+ if (dummy.f_op->open) {
dummy.f_dentry = dentry;
dummy.f_mode = (new_flags & MS_RDONLY) ? 1 : 3;
- retval = fops->open(inode, &dummy);
+ retval = dummy.f_op->open(inode, &dummy);
if (retval)
goto dput_and_out;
}
if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) {
flags = new_flags & ~MS_MGC_MSK;
retval = copy_mount_options(data, &page);
- if (retval < 0) {
- put_unnamed_dev(dev);
- goto dput_and_out;
- }
+ if (retval < 0)
+ goto clean_up;
}
- retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page);
+ retval = do_mount(dev, dev_name, dir_name, fstype->name, flags,
+ (void *) page);
free_page(page);
- if (retval && fops && fops->release) {
- fops->release(inode, NULL);
- put_unnamed_dev(dev);
- }
+ if (retval)
+ goto clean_up;
+
dput_and_out:
dput(dentry);
out:
unlock_kernel();
return retval;
+
+clean_up:
+ if (dummy.f_op) {
+ if (dummy.f_op->release)
+ dummy.f_op->release(inode, NULL);
+ } else
+ put_unnamed_dev(dev);
+ goto dput_and_out;
}
__initfunc(static void do_mount_root(void))
/* allocate/de-allocate */
extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name);
extern void prune_dcache(int);
+extern void shrink_dcache_sb(struct super_block *);
extern int d_invalidate(struct dentry *);
#define shrink_dcache() prune_dcache(0)
/* And dynamically-tunable limits and defaults: */
extern int max_inodes;
-extern int max_files, nr_files;
+extern int max_files, nr_files, nr_free_files;
#define NR_INODE 4096 /* this should be bigger than NR_FILE */
#define NR_FILE 1024 /* this can well be larger on a larger system */
+#define NR_RESERVED_FILES 10 /* reserved for root */
#define MAY_EXEC 1
#define MAY_WRITE 2
extern void init_fifo(struct inode * inode);
extern struct inode_operations fifo_inode_operations;
+/* Invalid inode operations -- fs/bad_inode.c */
+extern void make_bad_inode(struct inode * inode);
+extern int is_bad_inode(struct inode * inode);
+
extern struct file_operations connecting_fifo_fops;
extern struct file_operations read_fifo_fops;
extern struct file_operations write_fifo_fops;
/*
* Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
*/
+
struct list_head {
struct list_head *next, *prev;
};
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
-static inline void list_add(struct list_head *new, struct list_head *head)
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head * new,
+ struct list_head * prev,
+ struct list_head * next)
{
- struct list_head *next = head->next;
next->prev = new;
new->next = next;
- new->prev = head;
- head->next = new;
+ new->prev = prev;
+ prev->next = new;
}
-static inline void list_del(struct list_head *entry)
+/*
+ * Insert a new entry after the specified head..
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
{
- struct list_head *next, *prev;
- next = entry->next;
- prev = entry->prev;
next->prev = prev;
prev->next = next;
}
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
static inline int list_empty(struct list_head *head)
{
return head->next == head;
extern unsigned long page_unuse(unsigned long);
extern int shrink_mmap(int, int);
extern void truncate_inode_pages(struct inode *, unsigned long);
+extern unsigned long get_cached_page(struct inode *, unsigned long, int);
+extern void put_cached_page(unsigned long);
#define GFP_BUFFER 0x00
#define GFP_ATOMIC 0x01
struct nfs_time mtime;
};
-struct nfs_entry {
- __u32 fileid;
- char * name;
- unsigned int length:31,
- eof:1;
- __u32 cookie;
-};
-
struct nfs_fsinfo {
__u32 tsize;
__u32 bsize;
extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
const char *name);
extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
- u32 cookie, unsigned int size, struct nfs_entry *entry);
+ u32 cookie, unsigned int size, __u32 *entry);
extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *res);
extern int init_nfs_fs(void);
extern struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
struct nfs_fattr *fattr);
-extern void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr);
+extern int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr);
extern int nfs_revalidate(struct inode *);
extern int _nfs_revalidate_inode(struct nfs_server *, struct inode *);
unsigned long size;
struct inode_operations * ops;
int (*get_info)(char *, char **, off_t, int, int);
- void (*fill_inode)(struct inode *);
+ void (*fill_inode)(struct inode *, int);
struct proc_dir_entry *next, *parent, *subdir;
void *data;
int (*read_proc)(char *page, char **start, off_t off,
int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char *buffer,
unsigned long count, void *data);
+ unsigned int count; /* use count */
+ int deleted; /* delete flag */
};
extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
#ifndef _LINUX_SMB_FS_H
#define _LINUX_SMB_FS_H
+#include <linux/dirent.h>
#include <linux/smb.h>
/*
/* linux/fs/smbfs/dir.c */
extern struct inode_operations smb_dir_inode_operations;
-struct smb_inode_info *smb_find_inode(struct smb_sb_info *server, ino_t ino);
-void smb_free_inode_info(struct smb_inode_info *i);
-void smb_free_all_inodes(struct smb_sb_info *server);
-void smb_init_root(struct smb_sb_info *server);
-int smb_stat_root(struct smb_sb_info *server);
-void smb_init_dir_cache(void);
-void smb_invalid_dir_cache(struct inode *);
-void smb_free_dir_cache(void);
+void smb_init_root(struct smb_sb_info *);
+int smb_stat_root(struct smb_sb_info *);
+void smb_renew_times(struct dentry *);
/* linux/fs/smbfs/ioctl.c */
int smb_ioctl (struct inode * inode, struct file * filp,
unsigned int cmd, unsigned long arg);
/* linux/fs/smbfs/inode.c */
-struct super_block *smb_read_super(struct super_block *sb,
- void *raw_data, int silent);
+struct super_block *smb_read_super(struct super_block *, void *, int);
extern int init_smb_fs(void);
-void smb_invalidate_inodes(struct smb_sb_info *server);
-int smb_revalidate_inode(struct inode *i);
-int smb_refresh_inode(struct inode *i);
-int smb_notify_change(struct inode *inode, struct iattr *attr);
-void smb_invalidate_connection(struct smb_sb_info *server);
-int smb_conn_is_valid(struct smb_sb_info *server);
-unsigned long smb_invent_inos(unsigned long n);
+void smb_invalidate_inodes(struct smb_sb_info *);
+int smb_revalidate_inode(struct inode *);
+int smb_refresh_inode(struct inode *);
+int smb_notify_change(struct inode *, struct iattr *);
+void smb_invalidate_connection(struct smb_sb_info *);
+int smb_conn_is_valid(struct smb_sb_info *);
+unsigned long smb_invent_inos(unsigned long);
struct inode *smb_iget(struct super_block *, struct smb_fattr *);
/* linux/fs/smbfs/proc.c */
int smb_offerconn(struct smb_sb_info *server);
int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt);
int smb_close(struct inode *);
+void smb_close_dentry(struct dentry *);
int smb_open(struct dentry *, int);
static inline int
smb_is_open(struct inode *i)
return (i->u.smbfs_i.open == SMB_SERVER(i)->generation);
}
-int smb_proc_read(struct inode *, off_t, long, char *);
+int smb_proc_read(struct inode *, off_t, int, char *);
int smb_proc_write(struct inode *, off_t, int, const char *);
int smb_proc_create(struct dentry *, struct qstr *, __u16, time_t);
int smb_proc_mv(struct dentry *, struct qstr *, struct dentry *, struct qstr *);
int smb_proc_mkdir(struct dentry *, struct qstr *);
int smb_proc_rmdir(struct dentry *, struct qstr *);
int smb_proc_unlink(struct dentry *dir, struct qstr *);
-int smb_proc_readdir(struct dentry *dir, int fpos, int cache_size, struct smb_dirent *entry);
-int smb_proc_getattr(struct dentry *dir, struct qstr *name,
- struct smb_fattr *entry);
-int smb_proc_setattr(struct smb_sb_info *server,
- struct dentry *dir,
- struct smb_fattr *new_finfo);
-int smb_proc_dskattr(struct super_block *sb, struct statfs *attr);
-int smb_proc_reconnect(struct smb_sb_info *server);
-int smb_proc_connect(struct smb_sb_info *server);
-int smb_proc_disconnect(struct smb_sb_info *server);
-int smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length);
+int smb_proc_readdir(struct dentry *, int, void *);
+int smb_proc_getattr(struct dentry *, struct qstr *, struct smb_fattr *);
+int smb_proc_setattr(struct smb_sb_info *, struct dentry *, struct smb_fattr *);
+int smb_proc_dskattr(struct super_block *, struct statfs *);
+int smb_proc_reconnect(struct smb_sb_info *);
+int smb_proc_connect(struct smb_sb_info *);
+int smb_proc_disconnect(struct smb_sb_info *);
+int smb_proc_trunc(struct smb_sb_info *, __u16, __u32);
void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *);
/* linux/fs/smbfs/sock.c */
+int smb_valid_socket(struct inode *);
+void smb_close_socket(struct smb_sb_info *);
int smb_release(struct smb_sb_info *server);
int smb_connect(struct smb_sb_info *server);
int smb_request(struct smb_sb_info *server);
-int smb_request_read_raw(struct smb_sb_info *server,
- unsigned char *target, int max_len);
-int smb_request_write_raw(struct smb_sb_info *server,
- unsigned const char *source, int length);
+int smb_request_read_raw(struct smb_sb_info *, unsigned char *, int);
+int smb_request_write_raw(struct smb_sb_info *, unsigned const char *, int);
int smb_catch_keepalive(struct smb_sb_info *server);
int smb_dont_catch_keepalive(struct smb_sb_info *server);
int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
/* linux/fs/smbfs/mmap.c */
int smb_mmap(struct file * file, struct vm_area_struct * vma);
+/* fs/smbfs/cache.c */
+
+/*
+ * The cache index describes the pages mapped starting
+ * at offset PAGE_SIZE. We keep only a minimal amount
+ * of information here.
+ */
+struct cache_index {
+ unsigned short num_entries;
+ unsigned short space;
+ struct cache_block * block;
+};
+
+#define NINDEX (PAGE_SIZE-64)/sizeof(struct cache_index)
+/*
+ * The cache head is mapped as the page at offset 0.
+ */
+struct cache_head {
+ int valid;
+ int status; /* error code or 0 */
+ int entries; /* total entries */
+ int pages; /* number of data pages */
+ int idx; /* index of current data page */
+ struct cache_index index[NINDEX];
+};
+
+/*
+ * An array of cache_entry structures holds information
+ * for each object in the cache_block.
+ */
+struct cache_entry {
+ ino_t ino;
+ unsigned short namelen;
+ unsigned short offset;
+};
+
+/*
+ * The cache blocks hold the actual data. The entry table grows up
+ * while the names grow down, and we have space until they meet.
+ */
+struct cache_block {
+ union {
+ struct cache_entry table[1];
+ char names[PAGE_SIZE];
+ } cb_data;
+};
+
+/*
+ * To return an entry, we can pass a reference to the
+ * name instead of having to copy it.
+ */
+struct cache_dirent {
+ ino_t ino;
+ unsigned long pos;
+ int len;
+ char * name;
+};
+
+struct cache_head * smb_get_dircache(struct dentry *);
+void smb_init_dircache(struct cache_head *);
+void smb_free_dircache(struct cache_head *);
+int smb_refill_dircache(struct cache_head *, struct dentry *);
+void smb_add_to_cache(struct cache_head *, struct dirent *, off_t);
+int smb_find_in_cache(struct cache_head *, off_t, struct cache_dirent *);
+void smb_invalid_dir_cache(struct inode *);
+
#endif /* __KERNEL__ */
#endif /* _LINUX_SMB_FS_H */
* (open == generation).
*/
unsigned int open;
- void * dentry; /* The dentry we were opened with */
__u16 fileid; /* What id to handle a file with? */
__u16 attr; /* Attribute fields, DOS value */
__u16 access; /* Access bits. */
+ __u16 cache_valid; /* dircache valid? */
+ unsigned long oldmtime; /* last time refreshed */
+ void * dentry; /* The dentry we were opened with */
};
#endif
#include <linux/smb.h>
#include <linux/smb_mount.h>
+/* Get the server for the specified dentry */
+#define server_from_dentry(dentry) &dentry->d_sb->u.smbfs_sb
+#define SB_of(server) ((struct super_block *) ((char *)(server) - \
+ (unsigned long)(&((struct super_block *)0)->u.smbfs_sb)))
+
struct smb_sb_info {
enum smb_conn_state state;
struct file * sock_file;
struct smb_conn_opt opt;
struct semaphore sem;
+ struct wait_queue * wait;
__u32 packet_size;
unsigned char * packet;
void rpc_delay(struct rpc_task *, unsigned long);
void * rpc_allocate(unsigned int flags, unsigned int);
void rpc_free(void *);
-void rpciod_up(void);
+int rpciod_up(void);
void rpciod_down(void);
extern __inline__ void *
struct vm_area_struct * mpnt, *tmp, **pprev;
int retval;
- mm->mmap = mm->mmap_cache = NULL;
flush_cache_mm(current->mm);
pprev = &mm->mmap;
for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
if (retval)
goto fail_nomem;
}
- flush_tlb_mm(current->mm);
- return 0;
+ retval = 0;
fail_nomem:
flush_tlb_mm(current->mm);
mm->count = 1;
mm->def_flags = 0;
mm->mmap_sem = MUTEX;
- mm->pgd = NULL;
+ /*
+ * Leave mm->pgd set to the parent's pgd
+ * so that pgd_offset() is always valid.
+ */
mm->mmap = mm->mmap_cache = NULL;
/* It has not run yet, so cannot be present in anyone's
goto free_mm;
retval = dup_mmap(mm);
if (retval)
- goto free_mm;
+ goto free_pt;
return 0;
free_mm:
+ mm->pgd = NULL;
+free_pt:
tsk->mm = NULL;
mmput(mm);
fail_nomem:
struct files_struct *oldf, *newf;
struct file **old_fds, **new_fds;
+ /*
+ * A background process may not have any files ...
+ */
oldf = current->files;
+ if (!oldf)
+ return 0;
+
if (clone_flags & CLONE_FILES) {
oldf->count++;
return 0;
}
++total_forks;
error = p->pid;
- goto fork_out;
+bad_fork:
+ unlock_kernel();
+ return error;
bad_fork_cleanup_sighand:
exit_sighand(p);
nr_tasks--;
bad_fork_free:
free_task_struct(p);
-bad_fork:
-fork_out:
- unlock_kernel();
- return error;
+ goto bad_fork;
}
static void files_ctor(void *fp, kmem_cache_t *cachep, unsigned long flags)
EXPORT_SYMBOL(posix_block_lock);
EXPORT_SYMBOL(posix_unblock_lock);
EXPORT_SYMBOL(dput);
+EXPORT_SYMBOL(get_cached_page);
+EXPORT_SYMBOL(put_cached_page);
#if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
EXPORT_SYMBOL(do_nfsservctl);
EXPORT_SYMBOL(get_hash_table);
EXPORT_SYMBOL(get_empty_inode);
EXPORT_SYMBOL(insert_inode_hash);
+EXPORT_SYMBOL(make_bad_inode);
+EXPORT_SYMBOL(is_bad_inode);
EXPORT_SYMBOL(event);
EXPORT_SYMBOL(__down);
EXPORT_SYMBOL(__up);
0444, NULL, &proc_dointvec},
{KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int),
0644, NULL, &proc_dointvec},
- {KERN_NRFILE, "file-nr", &nr_files, sizeof(int),
+ {KERN_NRFILE, "file-nr", &nr_files, 3*sizeof(int),
0444, NULL, &proc_dointvec},
{KERN_MAXFILE, "file-max", &max_files, sizeof(int),
0644, NULL, &proc_dointvec},
return written;
return status;
}
+
+/*
+ * Support routines for directory cacheing using the page cache.
+ */
+
+/*
+ * Finds the page at the specified offset, installing a new page
+ * if requested. The count is incremented and the page is locked.
+ *
+ * Note: we don't have to worry about races here, as the caller
+ * is holding the inode semaphore.
+ */
+unsigned long get_cached_page(struct inode * inode, unsigned long offset,
+ int new)
+{
+ struct page * page;
+ struct page ** hash;
+ unsigned long page_cache;
+
+ hash = page_hash(inode, offset);
+ page = __find_page(inode, offset, *hash);
+ if (!page) {
+ if (!new)
+ goto out;
+ page_cache = get_free_page(GFP_KERNEL);
+ if (!page_cache)
+ goto out;
+ page = mem_map + MAP_NR(page_cache);
+ add_to_page_cache(page, inode, offset, hash);
+ }
+ if (atomic_read(&page->count) != 2)
+ printk("get_cached_page: page count=%d\n",
+ atomic_read(&page->count));
+ if (test_bit(PG_locked, &page->flags))
+ printk("get_cached_page: page already locked!\n");
+ set_bit(PG_locked, &page->flags);
+
+out:
+ return page_address(page);
+}
+
+/*
+ * Unlock and free a page.
+ */
+void put_cached_page(unsigned long addr)
+{
+ struct page * page = mem_map + MAP_NR(addr);
+
+ if (!test_bit(PG_locked, &page->flags))
+ printk("put_cached_page: page not locked!\n");
+ if (atomic_read(&page->count) != 2)
+ printk("put_cached_page: page count=%d\n",
+ atomic_read(&page->count));
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+ __free_page(page);
+}
#include <linux/smp_lock.h>
#include <linux/slab.h>
-#include <asm/dma.h>
-#include <asm/system.h> /* for cli()/sti() */
-#include <asm/uaccess.h> /* for copy_to/from_user */
#include <asm/bitops.h>
#include <asm/pgtable.h>
printk ("Starting kswapd v%.*s\n", i, s);
}
+#define MAX_SWAP_FAIL 3
/*
* The background pageout daemon.
* Started as a kernel thread from the init process.
init_swap_timer();
while (1) {
+ int fail;
+
kswapd_awake = 0;
current->signal = 0;
run_task_queue(&tq_disk);
* We now only swap out as many pages as needed.
* When we are truly low on memory, we swap out
* synchronously (WAIT == 1). -- Rik.
+ * If we've had too many consecutive failures,
+ * go back to sleep to let other tasks run.
+ */
+ for (fail = 0; fail++ < MAX_SWAP_FAIL;) {
+ int pages, wait;
+
+ pages = nr_free_pages;
+ if (nr_free_pages >= min_free_pages)
+ pages += atomic_read(&nr_async_pages);
+ if (pages >= free_pages_high)
+ break;
+ wait = (pages < free_pages_low);
+ if (try_to_free_page(GFP_KERNEL, 0, wait))
+ fail = 0;
+ }
+ /*
+ * Report failure if we couldn't reach the minimum goal.
*/
- while(nr_free_pages < min_free_pages)
- try_to_free_page(GFP_KERNEL, 0, 1);
- while((nr_free_pages + atomic_read(&nr_async_pages)) < free_pages_low)
- try_to_free_page(GFP_KERNEL, 0, 1);
- while((nr_free_pages + atomic_read(&nr_async_pages)) < free_pages_high)
- try_to_free_page(GFP_KERNEL, 0, 0);
+ if (nr_free_pages < min_free_pages)
+ printk("kswapd: failed, got %d of %d\n",
+ nr_free_pages, min_free_pages);
}
}
/*
* Properly shut down an RPC client, terminating all outstanding
- * requests.
+ * requests. Note that we must be certain that cl_oneshot and
+ * cl_dead are cleared, or else the client would be destroyed
+ * when the last task releases it.
*/
int
rpc_shutdown_client(struct rpc_clnt *clnt)
{
dprintk("RPC: shutting down %s client for %s\n",
- clnt->cl_protname, clnt->cl_server);
+ clnt->cl_protname, clnt->cl_server);
while (clnt->cl_users) {
- dprintk("sigmask %08lx\n", current->signal);
- dprintk("users %d\n", clnt->cl_users);
- clnt->cl_dead = 1;
+#ifdef RPC_DEBUG
+ printk("rpc_shutdown_client: client %s, tasks=%d\n",
+ clnt->cl_protname, clnt->cl_users);
+#endif
+ /* Don't let rpc_release_client destroy us */
+ clnt->cl_oneshot = 0;
+ clnt->cl_dead = 0;
rpc_killall_tasks(clnt);
sleep_on(&destroy_wait);
}
{
dprintk("RPC: rpc_release_client(%p, %d)\n",
clnt, clnt->cl_users);
- if (--(clnt->cl_users) == 0) {
- wake_up(&destroy_wait);
- if (clnt->cl_oneshot || clnt->cl_dead)
- rpc_destroy_client(clnt);
- }
- dprintk("RPC: rpc_release_client done\n");
+ if (clnt->cl_users) {
+ if (--(clnt->cl_users) > 0)
+ return;
+ } else
+ printk("rpc_release_client: %s client already free??\n",
+ clnt->cl_protname);
+
+ wake_up(&destroy_wait);
+ if (clnt->cl_oneshot || clnt->cl_dead)
+ rpc_destroy_client(clnt);
}
/*
if ((async = (flags & RPC_TASK_ASYNC)) != 0) {
if (!func)
func = rpc_default_callback;
- if (!(task = rpc_new_task(clnt, func, flags))) {
- current->blocked = oldmask;
- return -ENOMEM;
- }
+ status = -ENOMEM;
+ if (!(task = rpc_new_task(clnt, func, flags)))
+ goto out;
task->tk_calldata = data;
} else {
rpc_init_task(task, clnt, NULL, flags);
} else
async = 0;
+ status = 0;
if (!async) {
status = task->tk_status;
rpc_release_task(task);
- } else
- status = 0;
+ }
+out:
current->blocked = oldmask;
return status;
}
if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL)
return;
+ printk("RPC: buffer allocation failed for task %p\n", task);
if (1 || !signalled()) {
xprt_release(task);
}
clnt->cl_binding = 1;
- task->tk_status = 0;
- if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) {
- task->tk_status = -EACCES;
+ task->tk_status = -EACCES; /* why set this? returns -EIO below */
+ if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot)))
goto bailout;
- }
- if (!(child = rpc_new_child(pmap_clnt, task))) {
- rpc_destroy_client(pmap_clnt);
+ task->tk_status = 0;
+
+ /*
+ * Note: rpc_new_child will release client after a failure.
+ */
+ if (!(child = rpc_new_child(pmap_clnt, task)))
goto bailout;
- }
/* Setup the call info struct */
rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0);
*/
static struct wait_queue * rpciod_idle = NULL;
static struct wait_queue * rpciod_killer = NULL;
-static int rpciod_sema = 0;
+static struct semaphore rpciod_sema = MUTEX;
+static unsigned int rpciod_users = 0;
static pid_t rpciod_pid = 0;
static int rpc_inhibit = 0;
current->pid);
}
+/*
+ * Create a new task for the specified client. We have to
+ * clean up after an allocation failure, as the client may
+ * have specified "oneshot".
+ */
struct rpc_task *
rpc_new_task(struct rpc_clnt *clnt, rpc_action callback, int flags)
{
struct rpc_task *task;
- if (!(task = (struct rpc_task *) rpc_allocate(flags, sizeof(*task))))
- return NULL;
+ task = (struct rpc_task *) rpc_allocate(flags, sizeof(*task));
+ if (!task)
+ goto cleanup;
rpc_init_task(task, clnt, callback, flags);
dprintk("RPC: %4d allocated task\n", task->tk_pid);
task->tk_flags |= RPC_TASK_DYNAMIC;
+out:
return task;
+
+cleanup:
+ /* Check whether to release the client */
+ if (clnt) {
+ printk("rpc_new_task: failed, users=%d, oneshot=%d\n",
+ clnt->cl_users, clnt->cl_oneshot);
+ clnt->cl_users++; /* pretend we were used ... */
+ rpc_release_client(clnt);
+ }
+ goto out;
}
void
rpc_release_task(child);
}
+/*
+ * Note: rpc_new_task releases the client after a failure.
+ */
struct rpc_task *
rpc_new_child(struct rpc_clnt *clnt, struct rpc_task *parent)
{
unsigned long oldflags;
int rounds = 0;
+ MOD_INC_USE_COUNT;
lock_kernel();
+ /*
+ * Let our maker know we're running ...
+ */
rpciod_pid = current->pid;
+ wake_up(&rpciod_idle);
- MOD_INC_USE_COUNT;
- /* exit_files(current); */
+ exit_files(current);
exit_mm(current);
current->blocked |= ~_S(SIGKILL);
current->session = 1;
sprintf(current->comm, "rpciod");
dprintk("RPC: rpciod starting (pid %d)\n", rpciod_pid);
- while (rpciod_sema) {
+ while (rpciod_users) {
if (signalled()) {
if (current->signal & _S(SIGKILL)) {
rpciod_killall();
} else {
printk("rpciod: ignoring signal (%d users)\n",
- rpciod_sema);
+ rpciod_users);
}
current->signal &= current->blocked;
}
__rpc_schedule();
- if (++rounds >= 64) /* safeguard */
+ if (++rounds >= 64) { /* safeguard */
schedule();
+ rounds = 0;
+ }
save_flags(oldflags); cli();
if (!schedq.task) {
dprintk("RPC: rpciod back to sleep\n");
interruptible_sleep_on(&rpciod_idle);
dprintk("RPC: switch to rpciod\n");
+ rounds = 0;
}
restore_flags(oldflags);
}
}
}
-void
+/*
+ * Start up the rpciod process if it's not already running.
+ */
+int
rpciod_up(void)
{
- dprintk("rpciod_up pid %d sema %d\n", rpciod_pid, rpciod_sema);
- if (!(rpciod_sema++) || !rpciod_pid)
- kernel_thread(rpciod, &rpciod_killer, 0);
+ int error = 0;
+
+ MOD_INC_USE_COUNT;
+ down(&rpciod_sema);
+ dprintk("rpciod_up: pid %d, users %d\n", rpciod_pid, rpciod_users);
+ rpciod_users++;
+ if (rpciod_pid)
+ goto out;
+ /*
+ * If there's no pid, we should be the first user.
+ */
+ if (rpciod_users > 1)
+ printk("rpciod_up: no pid, %d users??\n", rpciod_users);
+ /*
+ * Create the rpciod thread and wait for it to start.
+ */
+ error = kernel_thread(rpciod, &rpciod_killer, 0);
+ if (error < 0) {
+ printk("rpciod_up: create thread failed, error=%d\n", error);
+ goto out;
+ }
+ sleep_on(&rpciod_idle);
+ error = 0;
+out:
+ up(&rpciod_sema);
+ MOD_DEC_USE_COUNT;
+ return error;
}
void
rpciod_down(void)
{
- dprintk("rpciod_down pid %d sema %d\n", rpciod_pid, rpciod_sema);
- if (--rpciod_sema > 0)
- return;
+ unsigned long oldflags;
+
+ MOD_INC_USE_COUNT;
+ down(&rpciod_sema);
+ dprintk("rpciod_down pid %d sema %d\n", rpciod_pid, rpciod_users);
+ if (rpciod_users) {
+ if (--rpciod_users)
+ goto out;
+ } else
+ printk("rpciod_down: pid=%d, no users??\n", rpciod_pid);
+
+ if (!rpciod_pid) {
+ printk("rpciod_down: Nothing to do!\n");
+ goto out;
+ }
- rpciod_sema = 0;
kill_proc(rpciod_pid, SIGKILL, 1);
+ /*
+ * Usually rpciod will exit very quickly, so we
+ * wait briefly before checking the process id.
+ */
+ oldflags = current->signal;
+ current->signal = 0;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 1;
+ schedule();
+ current->timeout = 0;
+ /*
+ * Display a message if we're going to wait longer.
+ */
while (rpciod_pid) {
- if (signalled())
- return;
+ printk("rpciod_down: waiting for pid %d to exit\n", rpciod_pid);
+ if (signalled()) {
+ printk("rpciod_down: caught signal\n");
+ break;
+ }
interruptible_sleep_on(&rpciod_killer);
}
+ current->signal = oldflags;
+out:
+ up(&rpciod_sema);
+ MOD_DEC_USE_COUNT;
}
serv->sv_program->pg_name,
serv->sv_nrthreads);
- if (--(serv->sv_nrthreads) != 0)
- return;
+ if (serv->sv_nrthreads) {
+ if (--(serv->sv_nrthreads) != 0)
+ return;
+ } else
+ printk("svc_destroy: no threads for serv=%p!\n", serv);
+
while ((svsk = serv->sv_allsocks) != NULL)
svc_delete_socket(svsk);
int
svc_create_thread(svc_thread_fn func, struct svc_serv *serv)
{
- struct svc_rqst *rqstp = 0;
- int error;
+ struct svc_rqst *rqstp;
+ int error = -ENOMEM;
- if (!(rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL)))
- return -ENOMEM;
+ rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL);
+ if (!rqstp)
+ goto out;
memset(rqstp, 0, sizeof(*rqstp));
if (!(rqstp->rq_argp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL))
|| !(rqstp->rq_resp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL))
- || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz)) {
- error = -ENOMEM;
- goto failure;
- }
+ || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz))
+ goto out_thread;
serv->sv_nrthreads++;
- if ((error = kernel_thread((int (*)(void *)) func, rqstp, 0)) < 0)
- goto failure;
-
rqstp->rq_server = serv;
- return 0;
+ error = kernel_thread((int (*)(void *)) func, rqstp, 0);
+ if (error < 0)
+ goto out_thread;
+ error = 0;
+out:
+ return error;
-failure:
+out_thread:
svc_exit_thread(rqstp);
- return error;
+ goto out;
}
/*
kfree(rqstp);
/* Release the server */
- svc_destroy(serv);
+ if (serv)
+ svc_destroy(serv);
}
/*
if ((svsk = svc_sock_dequeue(serv)) != NULL) {
enable_bh(NET_BH);
rqstp->rq_sock = svsk;
- svsk->sk_inuse++;
+ svsk->sk_inuse++; /* N.B. where is this decremented? */
} else {
/* No data pending. Go to sleep */
rqstp->rq_sock = NULL;
rqstp->rq_wait = NULL;
svc_serv_enqueue(serv, rqstp);
- current->state = TASK_UNINTERRUPTIBLE;
+ /*
+ * We have to be able to interrupt this wait
+ * to bring down the daemons ...
+ */
+ current->state = TASK_INTERRUPTIBLE;
add_wait_queue(&rqstp->rq_wait, &wait);
enable_bh(NET_BH);
schedule();
}
}
+printk("svc_recv: svsk=%p, use count=%d\n", svsk, svsk->sk_inuse);
dprintk("svc: server %p servicing socket %p\n", rqstp, svsk);
len = svsk->sk_recvfrom(rqstp);