Commit f59dfe26 authored by Linus Torvalds's avatar Linus Torvalds

Import 2.1.58

parent b23c7003
...@@ -632,8 +632,9 @@ S: USA ...@@ -632,8 +632,9 @@ S: USA
N: Nick Holloway N: Nick Holloway
E: Nick.Holloway@alfie.demon.co.uk E: Nick.Holloway@alfie.demon.co.uk
E: Nick.Holloway@parallax.co.uk E: Nick.Holloway@parallax.co.uk
D: Small patches for kernel, libc W: http://www.alfie.demon.co.uk/
D: MAKEDEV 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: 15 Duke Street
S: Chapelfields S: Chapelfields
S: Coventry S: Coventry
......
VERSION = 2 VERSION = 2
PATCHLEVEL = 1 PATCHLEVEL = 1
SUBLEVEL = 57 SUBLEVEL = 58
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
......
...@@ -172,9 +172,10 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) ...@@ -172,9 +172,10 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
/* Are we prepared to handle this kernel fault? */ /* Are we prepared to handle this kernel fault? */
if ((fixup = search_exception_table(regs->eip)) != 0) { 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, tsk->comm,
regs->eip, regs->eip,
address,
fixup); fixup);
regs->eip = fixup; regs->eip = fixup;
goto out; goto out;
......
...@@ -19,7 +19,7 @@ static struct dentry * bad_follow_link(struct inode * ino, struct dentry *base) ...@@ -19,7 +19,7 @@ static struct dentry * bad_follow_link(struct inode * ino, struct dentry *base)
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
} }
static int return_EIO() static int return_EIO(void)
{ {
return -EIO; return -EIO;
} }
...@@ -81,3 +81,12 @@ void make_bad_inode(struct inode * inode) ...@@ -81,3 +81,12 @@ void make_bad_inode(struct inode * inode)
inode->i_op = &bad_inode_ops; 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);
}
...@@ -281,21 +281,19 @@ static int proc_write_register(struct file *file, const char *buffer, ...@@ -281,21 +281,19 @@ static int proc_write_register(struct file *file, const char *buffer,
const char *sp; const char *sp;
char del, *dp; char del, *dp;
struct binfmt_entry *e; struct binfmt_entry *e;
int memsize, cnt = count - 1, err = 0; int memsize, cnt = count - 1, err;
MOD_INC_USE_COUNT;
/* some sanity checks */ /* some sanity checks */
if ((count < 11) || (count > 256)) { err = -EINVAL;
err = -EINVAL; if ((count < 11) || (count > 256))
goto _err; goto _err;
}
err = -ENOMEM;
memsize = sizeof(struct binfmt_entry) + count; memsize = sizeof(struct binfmt_entry) + count;
if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER))) { if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER)))
err = -ENOMEM;
goto _err; goto _err;
}
err = 0;
sp = buffer + 1; sp = buffer + 1;
del = buffer[0]; del = buffer[0];
dp = (char *)e + sizeof(struct binfmt_entry); dp = (char *)e + sizeof(struct binfmt_entry);
...@@ -327,12 +325,8 @@ static int proc_write_register(struct file *file, const char *buffer, ...@@ -327,12 +325,8 @@ static int proc_write_register(struct file *file, const char *buffer,
/* more sanity checks */ /* more sanity checks */
if (err || !(!cnt || (!(--cnt) && (*sp == '\n'))) || if (err || !(!cnt || (!(--cnt) && (*sp == '\n'))) ||
(e->size < 1) || ((e->size + e->offset) > 127) || (e->size < 1) || ((e->size + e->offset) > 127) ||
!(e->proc_name) || !(e->interpreter) || !(e->proc_name) || !(e->interpreter) || entry_proc_setup(e))
entry_proc_setup(e)) { goto free_err;
kfree(e);
err = -EINVAL;
goto _err;
}
write_lock(&entries_lock); write_lock(&entries_lock);
e->next = entries; e->next = entries;
...@@ -341,8 +335,11 @@ static int proc_write_register(struct file *file, const char *buffer, ...@@ -341,8 +335,11 @@ static int proc_write_register(struct file *file, const char *buffer,
err = count; err = count;
_err: _err:
MOD_DEC_USE_COUNT;
return err; return err;
free_err:
kfree(e);
err = -EINVAL;
goto _err;
} }
/* /*
...@@ -357,7 +354,6 @@ static int proc_read_status(char *page, char **start, off_t off, ...@@ -357,7 +354,6 @@ static int proc_read_status(char *page, char **start, off_t off,
char *dp; char *dp;
int elen, i, err; int elen, i, err;
MOD_INC_USE_COUNT;
#ifndef VERBOSE_STATUS #ifndef VERBOSE_STATUS
if (data) { if (data) {
if (!(e = get_entry((int) data))) { if (!(e = get_entry((int) data))) {
...@@ -415,7 +411,6 @@ static int proc_read_status(char *page, char **start, off_t off, ...@@ -415,7 +411,6 @@ static int proc_read_status(char *page, char **start, off_t off,
err = elen; err = elen;
_err: _err:
MOD_DEC_USE_COUNT;
return err; return err;
} }
...@@ -429,7 +424,6 @@ static int proc_write_status(struct file *file, const char *buffer, ...@@ -429,7 +424,6 @@ static int proc_write_status(struct file *file, const char *buffer,
struct binfmt_entry *e; struct binfmt_entry *e;
int res = count; int res = count;
MOD_INC_USE_COUNT;
if (buffer[count-1] == '\n') if (buffer[count-1] == '\n')
count--; count--;
if ((count == 1) && !(buffer[0] & ~('0' | '1'))) { if ((count == 1) && !(buffer[0] & ~('0' | '1'))) {
...@@ -449,7 +443,6 @@ static int proc_write_status(struct file *file, const char *buffer, ...@@ -449,7 +443,6 @@ static int proc_write_status(struct file *file, const char *buffer,
} else { } else {
res = -EINVAL; res = -EINVAL;
} }
MOD_DEC_USE_COUNT;
return res; return res;
} }
...@@ -477,29 +470,57 @@ static int entry_proc_setup(struct binfmt_entry *e) ...@@ -477,29 +470,57 @@ static int entry_proc_setup(struct binfmt_entry *e)
return 0; 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)) __initfunc(int init_misc_binfmt(void))
{ {
struct proc_dir_entry *status = NULL, *reg; struct proc_dir_entry *status = NULL, *reg;
int error = -ENOMEM;
if (!(bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR, bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR, NULL);
NULL)) || if (!bm_dir)
!(status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR, goto out;
bm_dir)) || #ifdef MODULE
!(reg = create_proc_entry("register", S_IFREG | S_IWUSR, bm_dir->fill_inode = bm_modcount;
bm_dir))) { #endif
if (status)
remove_proc_entry("status", bm_dir); status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR,
if (bm_dir) bm_dir);
remove_proc_entry("sys/fs/binfmt_misc", NULL); if (!status)
return -ENOMEM; goto cleanup_bm;
}
status->read_proc = proc_read_status; status->read_proc = proc_read_status;
status->write_proc = proc_write_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; 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 #ifdef MODULE
......
...@@ -61,37 +61,54 @@ static inline void d_free(struct dentry *dentry) ...@@ -61,37 +61,54 @@ static inline void d_free(struct dentry *dentry)
*/ */
void dput(struct dentry *dentry) void dput(struct dentry *dentry)
{ {
if (dentry) { int count;
int count;
if (!dentry)
return;
repeat: repeat:
count = dentry->d_count-1; count = dentry->d_count - 1;
if (count < 0) { if (count != 0)
printk("Negative d_count (%d) for %s/%s\n", goto out;
count,
dentry->d_parent->d_name.name, /*
dentry->d_name.name); * Note that if d_op->d_delete blocks,
*(int *)0 = 0; * 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; dentry->d_count = count;
if (!count) { return;
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);
}
} }
printk("Negative d_count (%d) for %s/%s\n",
count,
dentry->d_parent->d_name.name,
dentry->d_name.name);
*(int *)0 = 0;
} }
/* /*
...@@ -99,13 +116,14 @@ void dput(struct dentry *dentry) ...@@ -99,13 +116,14 @@ void dput(struct dentry *dentry)
* possible. If there are other users of the dentry we * possible. If there are other users of the dentry we
* can't invalidate it. * can't invalidate it.
* *
* This is currently incorrect. We should try to see if * We should probably try to see if we can invalidate
* we can invalidate any unused children - right now we * any unused children - right now we refuse to invalidate
* refuse to invalidate way too much. * too much. That would require a better child list
* data structure, though.
*/ */
int d_invalidate(struct dentry * dentry) 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) if (dentry->d_count != 1)
return -EBUSY; return -EBUSY;
...@@ -113,6 +131,27 @@ int d_invalidate(struct dentry * dentry) ...@@ -113,6 +131,27 @@ int d_invalidate(struct dentry * dentry)
return 0; 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 * Shrink the dcache. This is done when we need
* more memory, or simply when we need to unmount * more memory, or simply when we need to unmount
...@@ -131,24 +170,65 @@ void prune_dcache(int count) ...@@ -131,24 +170,65 @@ void prune_dcache(int count)
INIT_LIST_HEAD(tmp); INIT_LIST_HEAD(tmp);
dentry = list_entry(tmp, struct dentry, d_lru); dentry = list_entry(tmp, struct dentry, d_lru);
if (!dentry->d_count) { if (!dentry->d_count) {
struct dentry * parent; prune_one_dentry(dentry);
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);
if (!--count) if (!--count)
break; 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) #define NAME_ALLOC_LEN(len) ((len+16) & ~15)
struct dentry * d_alloc(struct dentry * parent, const struct qstr *name) struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
......
...@@ -18,8 +18,9 @@ ...@@ -18,8 +18,9 @@
static kmem_cache_t *filp_cache; static kmem_cache_t *filp_cache;
/* sysctl tunables... */ /* sysctl tunables... */
int nr_files = 0; int nr_files = 0; /* read only */
int max_files = NR_FILE; 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 */ /* Free list management, if you are here you must have f_count == 0 */
static struct file * free_filps = NULL; static struct file * free_filps = NULL;
...@@ -30,6 +31,7 @@ void insert_file_free(struct file *file) ...@@ -30,6 +31,7 @@ void insert_file_free(struct file *file)
free_filps->f_pprev = &file->f_next; free_filps->f_pprev = &file->f_next;
free_filps = file; free_filps = file;
file->f_pprev = &free_filps; file->f_pprev = &free_filps;
nr_free_files++;
} }
/* The list of in-use filp's must be exported (ugh...) */ /* The list of in-use filp's must be exported (ugh...) */
...@@ -43,6 +45,7 @@ static inline void put_inuse(struct file *file) ...@@ -43,6 +45,7 @@ static inline void put_inuse(struct file *file)
file->f_pprev = &inuse_filps; file->f_pprev = &inuse_filps;
} }
/* N.B. This should be an __initfunc ... */
void file_table_init(void) void file_table_init(void)
{ {
filp_cache = kmem_cache_create("filp", sizeof(struct file), filp_cache = kmem_cache_create("filp", sizeof(struct file),
...@@ -50,6 +53,11 @@ void file_table_init(void) ...@@ -50,6 +53,11 @@ void file_table_init(void)
SLAB_HWCACHE_ALIGN, NULL, NULL); SLAB_HWCACHE_ALIGN, NULL, NULL);
if(!filp_cache) if(!filp_cache)
panic("VFS: Cannot alloc filp SLAB 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. /* Find an unused file structure and return a pointer to it.
...@@ -61,24 +69,31 @@ struct file * get_empty_filp(void) ...@@ -61,24 +69,31 @@ struct file * get_empty_filp(void)
static int old_max = 0; static int old_max = 0;
struct file * f; struct file * f;
f = free_filps; if (nr_free_files > NR_RESERVED_FILES) {
if (!f) used_one:
goto get_more; f = free_filps;
remove_filp(f); remove_filp(f);
got_one: nr_free_files--;
memset(f, 0, sizeof(*f)); new_one:
f->f_count = 1; memset(f, 0, sizeof(*f));
f->f_version = ++event; f->f_count = 1;
put_inuse(f); f->f_version = ++event;
return f; 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)) { * 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); f = kmem_cache_alloc(filp_cache, SLAB_KERNEL);
if (f) { if (f) {
nr_files++; nr_files++;
goto got_one; goto new_one;
} }
/* Big problems... */ /* Big problems... */
printk("VFS: filp allocation failed\n"); printk("VFS: filp allocation failed\n");
......
...@@ -160,8 +160,9 @@ static inline int ...@@ -160,8 +160,9 @@ static inline int
nlmclnt_grace_wait(struct nlm_host *host) nlmclnt_grace_wait(struct nlm_host *host)
{ {
if (!host->h_reclaiming) if (!host->h_reclaiming)
current->timeout = 10 * HZ; current->timeout = jiffies + 10 * HZ;
interruptible_sleep_on(&host->h_gracewait); interruptible_sleep_on(&host->h_gracewait);
current->timeout = 0;
return signalled()? -ERESTARTSYS : 0; return signalled()? -ERESTARTSYS : 0;
} }
...@@ -178,9 +179,11 @@ nlmclnt_alloc_call(void) ...@@ -178,9 +179,11 @@ nlmclnt_alloc_call(void)
sizeof(struct nlm_rqst)); sizeof(struct nlm_rqst));
if (call) if (call)
return 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; current->state = TASK_INTERRUPTIBLE;
schedule(); schedule();
current->timeout = 0;
} }
return NULL; return NULL;
} }
...@@ -232,6 +235,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc) ...@@ -232,6 +235,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc)
/* Back off a little and try again */ /* Back off a little and try again */
current->timeout = jiffies + 15 * HZ; current->timeout = jiffies + 15 * HZ;
interruptible_sleep_on(&host->h_gracewait); interruptible_sleep_on(&host->h_gracewait);
current->timeout = 0;
} while (!signalled()); } while (!signalled());
return -ERESTARTSYS; return -ERESTARTSYS;
......
...@@ -42,11 +42,15 @@ ...@@ -42,11 +42,15 @@
extern struct svc_program nlmsvc_program; extern struct svc_program nlmsvc_program;
struct nlmsvc_binding * nlmsvc_ops = NULL; struct nlmsvc_binding * nlmsvc_ops = NULL;
static int nlmsvc_sema = 0; static struct semaphore nlmsvc_sema = MUTEX;
static int nlmsvc_pid = 0; static unsigned int nlmsvc_users = 0;
static pid_t nlmsvc_pid = 0;
unsigned long nlmsvc_grace_period = 0; unsigned long nlmsvc_grace_period = 0;
unsigned long nlmsvc_timeout = 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. * Currently the following can be set only at insmod time.
* Ideally, they would be accessible through the sysctl interface. * Ideally, they would be accessible through the sysctl interface.
...@@ -64,10 +68,16 @@ lockd(struct svc_rqst *rqstp) ...@@ -64,10 +68,16 @@ lockd(struct svc_rqst *rqstp)
sigset_t oldsigmask; sigset_t oldsigmask;
int err = 0; int err = 0;
lock_kernel();
/* Lock module and set up kernel thread */ /* Lock module and set up kernel thread */
MOD_INC_USE_COUNT; 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); exit_mm(current);
current->session = 1; current->session = 1;
current->pgrp = 1; current->pgrp = 1;
...@@ -76,6 +86,12 @@ lockd(struct svc_rqst *rqstp) ...@@ -76,6 +86,12 @@ lockd(struct svc_rqst *rqstp)
/* kick rpciod */ /* kick rpciod */
rpciod_up(); 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"); dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
if (!nlm_timeout) if (!nlm_timeout)
...@@ -94,14 +110,14 @@ lockd(struct svc_rqst *rqstp) ...@@ -94,14 +110,14 @@ lockd(struct svc_rqst *rqstp)
nlmsvc_grace_period += jiffies; nlmsvc_grace_period += jiffies;
nlmsvc_timeout = nlm_timeout * HZ; nlmsvc_timeout = nlm_timeout * HZ;
nlmsvc_pid = current->pid;
/* /*
* The main request loop. We don't terminate until the last * 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 * 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()) if (signalled())
current->signal = 0; current->signal = 0;
...@@ -156,8 +172,17 @@ lockd(struct svc_rqst *rqstp) ...@@ -156,8 +172,17 @@ lockd(struct svc_rqst *rqstp)
nlmsvc_ops->exp_unlock(); 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 */ /* Exit the RPC thread */
svc_exit_thread(rqstp); svc_exit_thread(rqstp);
...@@ -166,7 +191,6 @@ lockd(struct svc_rqst *rqstp) ...@@ -166,7 +191,6 @@ lockd(struct svc_rqst *rqstp)
/* Release module */ /* Release module */
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
nlmsvc_pid = 0;
} }
/* /*
...@@ -185,42 +209,100 @@ lockd_makesock(struct svc_serv *serv, int protocol, unsigned short port) ...@@ -185,42 +209,100 @@ lockd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
return svc_create_socket(serv, protocol, &sin); return svc_create_socket(serv, protocol, &sin);
} }
/*
* Bring up the lockd process if it's not already up.
*/
int int
lockd_up(void) lockd_up(void)
{ {
struct svc_serv * serv; struct svc_serv * serv;
int error; int error = 0;
if (nlmsvc_pid || nlmsvc_sema++) down(&nlmsvc_sema);
return 0; /*
* 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) * Sanity check: if there's no pid,
return -ENOMEM; * 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) { || (error = lockd_makesock(serv, IPPROTO_TCP, 0)) < 0) {
svc_destroy(serv); printk("lockd_up: makesock failed, error=%d\n", error);
return 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); 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 void
lockd_down(void) lockd_down(void)
{ {
if (!nlmsvc_pid || --nlmsvc_sema > 0) down(&nlmsvc_sema);
return; 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); 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 #ifdef MODULE
...@@ -235,6 +317,11 @@ lockd_down(void) ...@@ -235,6 +317,11 @@ lockd_down(void)
int int
init_module(void) init_module(void)
{ {
/* Init the static variables */
nlmsvc_sema = MUTEX;
nlmsvc_users = 0;
nlmsvc_pid = 0;
lockd_exit = NULL;
nlmxdr_init(); nlmxdr_init();
return 0; return 0;
} }
......
...@@ -881,9 +881,6 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, ...@@ -881,9 +881,6 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
if (caller->fl_type != F_UNLCK) { if (caller->fl_type != F_UNLCK) {
repeat: repeat:
error = -ERESTARTSYS;
if (signal_pending(current))
goto out;
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX)) if (!(fl->fl_flags & FL_POSIX))
continue; continue;
...@@ -895,6 +892,9 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, ...@@ -895,6 +892,9 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
error = -EDEADLK; error = -EDEADLK;
if (posix_locks_deadlock(caller, fl)) if (posix_locks_deadlock(caller, fl))
goto out; goto out;
error = -ERESTARTSYS;
if (signal_pending(current))
goto out;
locks_insert_block(fl, caller); locks_insert_block(fl, caller);
interruptible_sleep_on(&caller->fl_wait); interruptible_sleep_on(&caller->fl_wait);
locks_delete_block(fl, caller); locks_delete_block(fl, caller);
......
...@@ -232,11 +232,14 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name) ...@@ -232,11 +232,14 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
result = d_lookup(parent, name); result = d_lookup(parent, name);
if (!result) { if (!result) {
struct dentry * dentry = d_alloc(parent, name); struct dentry * dentry = d_alloc(parent, name);
int error = dir->i_op->lookup(dir, dentry); result = ERR_PTR(-ENOMEM);
result = ERR_PTR(error); if (dentry) {
if (!error) int error = dir->i_op->lookup(dir, dentry);
result = dget(dentry->d_mounts); result = ERR_PTR(error);
dput(dentry); if (!error)
result = dget(dentry->d_mounts);
dput(dentry);
}
} }
up(&dir->i_sem); up(&dir->i_sem);
return result; return result;
...@@ -1170,7 +1173,8 @@ static inline int do_rename(const char * oldname, const char * newname) ...@@ -1170,7 +1173,8 @@ static inline int do_rename(const char * oldname, const char * newname)
if (new_dir->d_inode->i_sb && new_dir->d_inode->i_sb->dq_op) 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); 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: exit_lock:
double_unlock(new_dir, old_dir); double_unlock(new_dir, old_dir);
......
This diff is collapsed.
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#define NFSDBG_FACILITY NFSDBG_VFS #define NFSDBG_FACILITY NFSDBG_VFS
extern void nfs_invalidate_dircache_sb(struct super_block *);
static int nfs_notify_change(struct inode *, struct iattr *); static int nfs_notify_change(struct inode *, struct iattr *);
static void nfs_put_inode(struct inode *); static void nfs_put_inode(struct inode *);
static void nfs_delete_inode(struct inode *); static void nfs_delete_inode(struct inode *);
...@@ -67,6 +69,7 @@ nfs_read_inode(struct inode * inode) ...@@ -67,6 +69,7 @@ nfs_read_inode(struct inode * inode)
{ {
inode->i_blksize = inode->i_sb->s_blocksize; inode->i_blksize = inode->i_sb->s_blocksize;
inode->i_mode = 0; inode->i_mode = 0;
inode->i_rdev = 0;
inode->i_op = NULL; inode->i_op = NULL;
NFS_CACHEINV(inode); NFS_CACHEINV(inode);
} }
...@@ -75,6 +78,11 @@ static void ...@@ -75,6 +78,11 @@ static void
nfs_put_inode(struct inode * inode) nfs_put_inode(struct inode * inode)
{ {
dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); 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 static void
...@@ -90,13 +98,21 @@ nfs_put_super(struct super_block *sb) ...@@ -90,13 +98,21 @@ nfs_put_super(struct super_block *sb)
struct nfs_server *server = &sb->u.nfs_sb.s_server; struct nfs_server *server = &sb->u.nfs_sb.s_server;
struct rpc_clnt *rpc; struct rpc_clnt *rpc;
/*
* Lock the super block while we bring down the daemons.
*/
lock_super(sb);
if ((rpc = server->client) != NULL) if ((rpc = server->client) != NULL)
rpc_shutdown_client(rpc); rpc_shutdown_client(rpc);
if (!(server->flags & NFS_MOUNT_NONLM)) if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */ lockd_down(); /* release rpc.lockd */
rpciod_down(); /* release rpciod */ rpciod_down(); /* release rpciod */
lock_super(sb); /*
* Invalidate the dircache for this superblock.
*/
nfs_invalidate_dircache_sb(sb);
sb->s_dev = 0; sb->s_dev = 0;
unlock_super(sb); unlock_super(sb);
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
...@@ -147,14 +163,12 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) ...@@ -147,14 +163,12 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
unsigned int authflavor; unsigned int authflavor;
int tcp; int tcp;
kdev_t dev = sb->s_dev; kdev_t dev = sb->s_dev;
struct inode *root_inode;
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
if (!data) { if (!data)
printk("nfs_read_super: missing data argument\n"); goto out_miss_args;
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
if (data->version != NFS_MOUNT_VERSION) { if (data->version != NFS_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n", printk("nfs warning: mount version %s than kernel\n",
data->version < NFS_MOUNT_VERSION ? "older" : "newer"); data->version < NFS_MOUNT_VERSION ? "older" : "newer");
...@@ -164,13 +178,19 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) ...@@ -164,13 +178,19 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
data->bsize = 0; 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); lock_super(sb);
server = &sb->u.nfs_sb.s_server;
sb->s_magic = NFS_SUPER_MAGIC; sb->s_magic = NFS_SUPER_MAGIC;
sb->s_dev = dev; sb->s_dev = dev;
sb->s_op = &nfs_sops; sb->s_op = &nfs_sops;
sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); 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->rsize = nfs_block_size(data->rsize, NULL);
server->wsize = nfs_block_size(data->wsize, NULL); server->wsize = nfs_block_size(data->wsize, NULL);
server->flags = data->flags; server->flags = data->flags;
...@@ -179,15 +199,6 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) ...@@ -179,15 +199,6 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
server->acdirmin = data->acdirmin*HZ; server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ; server->acdirmax = data->acdirmax*HZ;
strcpy(server->hostname, data->hostname); 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? */ /* Which protocol do we use? */
tcp = (data->flags & NFS_MOUNT_TCP); tcp = (data->flags & NFS_MOUNT_TCP);
...@@ -210,18 +221,13 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) ...@@ -210,18 +221,13 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
/* Now create transport and client */ /* Now create transport and client */
xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP, xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
&srvaddr, &timeparms); &srvaddr, &timeparms);
if (xprt == NULL) { if (xprt == NULL)
printk("NFS: cannot create RPC transport.\n"); goto out_no_xprt;
goto failure;
}
clnt = rpc_create_client(xprt, server->hostname, &nfs_program, clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
NFS_VERSION, authflavor); NFS_VERSION, authflavor);
if (clnt == NULL) { if (clnt == NULL)
printk("NFS: cannot create RPC client.\n"); goto out_no_client;
xprt_destroy(xprt);
goto failure;
}
clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0; clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0;
clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0; clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0;
...@@ -229,29 +235,67 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) ...@@ -229,29 +235,67 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
server->client = clnt; server->client = clnt;
/* Fire up rpciod if not yet running */ /* Fire up rpciod if not yet running */
#ifdef RPCIOD_RESULT
if (rpciod_up())
goto out_no_iod;
#else
rpciod_up(); 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); unlock_super(sb);
sb->s_root = d_alloc_root(nfs_fhget(sb, &data->root, NULL), NULL); /* Check whether to start the lockd process */
if (sb->s_root != NULL) { if (!(server->flags & NFS_MOUNT_NONLM))
/* We're airborne */ lockd_up();
if (!(server->flags & NFS_MOUNT_NONLM)) return sb;
lockd_up();
return sb;
}
/* Yargs. It didn't work out. */ /* Yargs. It didn't work out. */
out_no_root:
printk("nfs_read_super: get root inode failed\n"); printk("nfs_read_super: get root inode failed\n");
rpc_shutdown_client(server->client); iput(root_inode);
rpciod_down(); rpciod_down();
#ifdef RPCIOD_RESULT
goto out_shutdown;
failure: out_no_iod:
MOD_DEC_USE_COUNT; printk("nfs_read_super: couldn't start rpciod!\n");
if (sb->s_lock) out_shutdown:
unlock_super(sb); #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; sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL; return NULL;
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#define NFSDBG_FACILITY NFSDBG_XDR #define NFSDBG_FACILITY NFSDBG_XDR
/* #define NFS_PARANOIA 1 */
#define QUADLEN(len) (((len) + 3) >> 2) #define QUADLEN(len) (((len) + 3) >> 2)
static int nfs_stat_to_errno(int stat); static int nfs_stat_to_errno(int stat);
...@@ -371,17 +372,18 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args) ...@@ -371,17 +372,18 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
* to avoid a malloc of NFS_MAXNAMLEN+1 for each file name. * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
* After decoding, the layout in memory looks like this: * After decoding, the layout in memory looks like this:
* entry1 entry2 ... entryN <space> stringN ... string2 string1 * 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 * Note that the strings are not null-terminated so that the entire number
* of entries returned by the server should fit into the buffer. * of entries returned by the server should fit into the buffer.
*/ */
static int static int
nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
{ {
struct nfs_entry *entry;
struct iovec *iov = req->rq_rvec; struct iovec *iov = req->rq_rvec;
int status, nr, len; int status, nr, len;
char *string; char *string, *start;
u32 *end; u32 *end;
__u32 fileid, cookie, *entry;
if ((status = ntohl(*p++))) if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status); return -nfs_stat_to_errno(status);
...@@ -396,10 +398,11 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) ...@@ -396,10 +398,11 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
end = (u32 *) ((u8 *) p + iov[1].iov_len); end = (u32 *) ((u8 *) p + iov[1].iov_len);
/* Get start and end of dirent buffer */ /* 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; string = (char *) res->buffer + res->bufsiz;
for (nr = 0; *p++; nr++, entry++) { for (nr = 0; *p++; nr++) {
entry->fileid = ntohl(*p++); fileid = ntohl(*p++);
len = ntohl(*p++); len = ntohl(*p++);
if ((p + QUADLEN(len) + 3) > end) { if ((p + QUADLEN(len) + 3) > end) {
...@@ -413,27 +416,36 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) ...@@ -413,27 +416,36 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
return -errno_NFSERR_IO; return -errno_NFSERR_IO;
} }
string -= len; string -= len;
if ((void *) (entry+1) > (void *) string) { if ((void *) (entry+3) > (void *) string) {
/* This may actually happen because an nfs_entry /*
* will take up more space than the XDR data. On * This error is impossible as long as the temp
* 32bit machines that's due to 8byte alignment, * buffer is no larger than the user buffer. The
* on 64bit machines that's because the char * takes * current packing algorithm uses the same amount
* up 2 longs. * of space in the user buffer as in the XDR data,
* * so it's guaranteed to fit.
* THIS IS BAD!
*/ */
printk(KERN_NOTICE "NFS: should not happen in %s!\n", printk("NFS: incorrect buffer size in %s!\n",
__FUNCTION__); __FUNCTION__);
break; break;
} }
entry->name = string;
entry->length = len;
memmove(string, p, len); memmove(string, p, len);
p += QUADLEN(len); p += QUADLEN(len);
entry->cookie = ntohl(*p++); cookie = ntohl(*p++);
entry->eof = !p[0] && p[1]; /*
* 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; return nr;
} }
......
...@@ -384,17 +384,18 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args) ...@@ -384,17 +384,18 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
* to avoid a malloc of NFS_MAXNAMLEN+1 for each file name. * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
* After decoding, the layout in memory looks like this: * After decoding, the layout in memory looks like this:
* entry1 entry2 ... entryN <space> stringN ... string2 string1 * 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 * Note that the strings are not null-terminated so that the entire number
* of entries returned by the server should fit into the buffer. * of entries returned by the server should fit into the buffer.
*/ */
static int static int
nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
{ {
struct nfs_entry *entry;
struct iovec *iov = req->rq_rvec; struct iovec *iov = req->rq_rvec;
int status, nr, len; int status, nr, len;
char *string; char *string, *start;
u32 *end; u32 *end;
__u32 fileid, cookie, *entry;
if ((status = ntohl(*p++))) if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status); return -nfs_stat_to_errno(status);
...@@ -413,10 +414,11 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) ...@@ -413,10 +414,11 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
return -errno_NFSERR_IO; return -errno_NFSERR_IO;
} }
string = (char *) res->buffer + res->bufsiz; entry = (__u32 *) res->buffer;
entry = (struct nfs_entry *) res->buffer; start = (char *) res->buffer;
for (nr = 0; *p++; nr++, entry++) { string = start + res->bufsiz;
entry->fileid = ntohl(*p++); for (nr = 0; *p++; nr++) {
fileid = ntohl(*p++);
len = ntohl(*p++); len = ntohl(*p++);
if ((p + QUADLEN(len) + 3) > end) { if ((p + QUADLEN(len) + 3) > end) {
...@@ -430,22 +432,40 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) ...@@ -430,22 +432,40 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
return -errno_NFSERR_IO; return -errno_NFSERR_IO;
} }
string -= len; string -= len;
if ((void *) (entry+1) > (void *) string) { if ((void *) (entry+3) > (void *) string) {
dprintk("NFS: shouldnothappen in readdirres_decode!\n"); /*
break; /* should not happen */ * 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); memmove(string, p, len);
p += QUADLEN(len); p += QUADLEN(len);
entry->cookie = ntohl(*p++); cookie = ntohl(*p++);
entry->eof = !p[0] && p[1]; /*
* 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", 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; return nr;
} }
......
...@@ -250,25 +250,43 @@ nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name) ...@@ -250,25 +250,43 @@ nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
*/ */
int int
nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, 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_readdirargs arg;
struct nfs_readdirres res; struct nfs_readdirres res;
void * buffer; void * buffer;
unsigned int buf_size = PAGE_SIZE;
int status; int status;
/* First get a temp buffer for the readdir reply */ /* First get a temp buffer for the readdir reply */
while (!(buffer = (void *) get_free_page(GFP_USER))) { /* N.B. does this really need to be cleared? */
need_resched = 1; status = -ENOMEM;
schedule(); buffer = (void *) get_free_page(GFP_KERNEL);
if (signalled()) if (!buffer)
return -ERESTARTSYS; 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.fh = fhandle;
arg.cookie = cookie; arg.cookie = cookie;
arg.buffer = buffer; arg.buffer = buffer;
arg.bufsiz = server->rsize < PAGE_SIZE? server->rsize : PAGE_SIZE; arg.bufsiz = buf_size;
res.buffer = entry; res.buffer = entry;
res.bufsiz = size; res.bufsiz = size;
...@@ -276,6 +294,7 @@ nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -276,6 +294,7 @@ nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0); status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0);
dprintk("NFS reply readdir: %d\n", status); dprintk("NFS reply readdir: %d\n", status);
free_page((unsigned long) buffer); free_page((unsigned long) buffer);
out:
return status; return status;
} }
......
...@@ -348,6 +348,12 @@ static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr) ...@@ -348,6 +348,12 @@ static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr)
if (!p || !p->mm || ptr >= TASK_SIZE) if (!p || !p->mm || ptr >= TASK_SIZE)
return 0; 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); page_dir = pgd_offset(p->mm,ptr);
if (pgd_none(*page_dir)) if (pgd_none(*page_dir))
return 0; return 0;
...@@ -917,24 +923,34 @@ static int get_statm(int pid, char * buffer) ...@@ -917,24 +923,34 @@ static int get_statm(int pid, char * buffer)
#define MAPS_LINE_MAX MAPS_LINE_MAX8 #define MAPS_LINE_MAX MAPS_LINE_MAX8
static long read_maps (int pid, struct file * file, static long read_maps (int pid, struct file * file, char * buf,
char * buf, unsigned long count) unsigned long count)
{ {
struct task_struct *p = find_task_by_pid(pid); struct task_struct *p;
char * destptr; struct vm_area_struct * map, * next;
char * destptr = buf, * buffer;
loff_t lineno; loff_t lineno;
int column; int column, i, volatile_task;
struct vm_area_struct * map; long retval;
int i;
char * buffer; /*
* 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) if (!p)
return -EINVAL; goto freepage_out;
if (!p->mm || p->mm == &init_mm || count == 0) 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 */ /* decode f_pos */
lineno = file->f_pos >> MAPS_LINE_SHIFT; lineno = file->f_pos >> MAPS_LINE_SHIFT;
...@@ -944,9 +960,7 @@ static long read_maps (int pid, struct file * file, ...@@ -944,9 +960,7 @@ static long read_maps (int pid, struct file * file,
for (map = p->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) for (map = p->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++)
continue; continue;
destptr = buf; for ( ; map ; map = next ) {
for ( ; map ; ) {
/* produce the next line */ /* produce the next line */
char *line; char *line;
char str[5], *cp = str; char str[5], *cp = str;
...@@ -957,6 +971,10 @@ static long read_maps (int pid, struct file * file, ...@@ -957,6 +971,10 @@ static long read_maps (int pid, struct file * file,
MAPS_LINE_MAX4 : MAPS_LINE_MAX8; MAPS_LINE_MAX4 : MAPS_LINE_MAX8;
int len; int len;
/*
* Get the next vma now (but it won't be used if we sleep).
*/
next = map->vm_next;
flags = map->vm_flags; flags = map->vm_flags;
*cp++ = flags & VM_READ ? 'r' : '-'; *cp++ = flags & VM_READ ? 'r' : '-';
...@@ -993,20 +1011,19 @@ static long read_maps (int pid, struct file * file, ...@@ -993,20 +1011,19 @@ static long read_maps (int pid, struct file * file,
if (column >= len) { if (column >= len) {
column = 0; /* continue with next line at column 0 */ column = 0; /* continue with next line at column 0 */
lineno++; lineno++;
map = map->vm_next; continue; /* we haven't slept */
continue;
} }
i = len-column; i = len-column;
if (i > count) if (i > count)
i = count; i = count;
copy_to_user(destptr, line+column, i); copy_to_user(destptr, line+column, i); /* may have slept */
destptr += i; count -= i; destptr += i;
column += i; count -= i;
column += i;
if (column >= len) { if (column >= len) {
column = 0; /* next time: next line at column 0 */ column = 0; /* next time: next line at column 0 */
lineno++; lineno++;
map = map->vm_next;
} }
/* done? */ /* done? */
...@@ -1016,15 +1033,20 @@ static long read_maps (int pid, struct file * file, ...@@ -1016,15 +1033,20 @@ static long read_maps (int pid, struct file * file,
/* By writing to user space, we might have slept. /* By writing to user space, we might have slept.
* Stop the loop, to avoid a race condition. * Stop the loop, to avoid a race condition.
*/ */
if (p != current) if (volatile_task)
break; break;
} }
/* encode f_pos */ /* encode f_pos */
file->f_pos = (lineno << MAPS_LINE_SHIFT) + column; file->f_pos = (lineno << MAPS_LINE_SHIFT) + column;
getlen_out:
retval = destptr - buf;
freepage_out:
free_page((unsigned long)buffer); free_page((unsigned long)buffer);
return destptr-buf; out:
return retval;
} }
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
......
...@@ -50,13 +50,17 @@ static struct inode_operations proc_base_inode_operations = { ...@@ -50,13 +50,17 @@ static struct inode_operations proc_base_inode_operations = {
NULL /* permission */ 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; struct task_struct *p;
int pid = inode->i_ino >> 16; int pid = inode->i_ino >> 16;
int ino = inode->i_ino & 0xffff; 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) { if (p->dumpable || ino == PROC_PID_INO) {
inode->i_uid = p->euid; inode->i_uid = p->euid;
inode->i_gid = p->gid; inode->i_gid = p->gid;
......
...@@ -16,11 +16,13 @@ ...@@ -16,11 +16,13 @@
#include <linux/stat.h> #include <linux/stat.h>
#include <asm/bitops.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, static long proc_file_read(struct inode * inode, struct file * file,
char * buf, unsigned long nbytes); char * buf, unsigned long nbytes);
static long proc_file_write(struct inode * inode, struct file * file, static long proc_file_write(struct inode * inode, struct file * file,
const char * buffer, unsigned long count); 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) int proc_match(int len, const char *name,struct proc_dir_entry * de)
{ {
...@@ -44,17 +46,14 @@ static struct file_operations proc_file_operations = { ...@@ -44,17 +46,14 @@ static struct file_operations proc_file_operations = {
NULL /* can't fsync */ NULL /* can't fsync */
}; };
/*
* proc files can do almost nothing..
*/
struct inode_operations proc_file_inode_operations = { struct inode_operations proc_file_inode_operations = {
&proc_file_operations, /* default proc file-ops */ &proc_file_operations, /* default proc file-ops */
NULL, /* create */ NULL, /* create */
NULL, /* lookup */ NULL, /* lookup */
NULL, /* link */ NULL, /* link */
NULL, /* unlink */ NULL, /* unlink */
NULL, /* symlink */ NULL, /* symlink */
NULL, /* mkdir */ NULL, /* mkdir */
NULL, /* rmdir */ NULL, /* rmdir */
NULL, /* mknod */ NULL, /* mknod */
NULL, /* rename */ NULL, /* rename */
...@@ -240,57 +239,77 @@ static int xlate_proc_name(const char *name, ...@@ -240,57 +239,77 @@ static int xlate_proc_name(const char *name,
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent) struct proc_dir_entry *parent)
{ {
struct proc_dir_entry *ent; struct proc_dir_entry *ent = NULL;
const char *fn; const char *fn = name;
int len;
if (parent)
fn = name; if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
else { goto out;
if (xlate_proc_name(name, &parent, &fn)) len = strlen(fn);
return NULL;
}
ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL); ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
if (!ent) if (!ent)
return NULL; goto out;
memset(ent, 0, sizeof(struct proc_dir_entry)); 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; mode |= S_IRUGO | S_IXUGO;
else if (mode == 0) ent->ops = &proc_dyna_dir_inode_operations;
mode = S_IFREG | S_IRUGO;
ent->name = fn;
ent->namelen = strlen(fn);
ent->mode = mode;
if (S_ISDIR(mode))
ent->nlink = 2; ent->nlink = 2;
else }
else if (mode == 0) {
mode = S_IFREG | S_IRUGO;
ent->nlink = 1; ent->nlink = 1;
}
ent->mode = mode;
proc_register(parent, ent); proc_register(parent, ent);
out:
return ent; 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) void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
{ {
struct proc_dir_entry *de; struct proc_dir_entry *de;
const char *fn; const char *fn = name;
int len; int len;
if (parent) if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
fn = name; goto out;
else
if (xlate_proc_name(name, &parent, &fn))
return;
len = strlen(fn); len = strlen(fn);
for (de = parent->subdir; de ; de = de->next) { for (de = parent->subdir; de ; de = de->next) {
if (proc_match(len, fn, de)) if (proc_match(len, fn, de))
break; 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); 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;
} }
...@@ -17,23 +17,66 @@ ...@@ -17,23 +17,66 @@
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.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) static void proc_put_inode(struct inode *inode)
{ {
#ifdef CONFIG_SUN_OPENPROMFS_MODULE #ifdef CONFIG_SUN_OPENPROMFS_MODULE
if ((inode->i_ino >= PROC_OPENPROM_FIRST) if ((inode->i_ino >= PROC_OPENPROM_FIRST) &&
&& (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) &&
&& proc_openprom_use) proc_openprom_use)
(*proc_openprom_use)(inode, 0); (*proc_openprom_use)(inode, 0);
#endif #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) static void proc_delete_inode(struct inode *inode)
{ {
printk("proc_delete_inode()?\n"); struct proc_dir_entry *de = inode->u.generic_ip;
inode->i_size = 0; 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) static void proc_put_super(struct super_block *sb)
...@@ -47,7 +90,7 @@ static struct super_operations proc_sops = { ...@@ -47,7 +90,7 @@ static struct super_operations proc_sops = {
proc_read_inode, proc_read_inode,
proc_write_inode, proc_write_inode,
proc_put_inode, proc_put_inode,
proc_delete_inode, proc_delete_inode, /* delete_inode(struct inode *) */
NULL, NULL,
proc_put_super, proc_put_super,
NULL, NULL,
...@@ -85,9 +128,24 @@ static int parse_options(char *options,uid_t *uid,gid_t *gid) ...@@ -85,9 +128,24 @@ static int parse_options(char *options,uid_t *uid,gid_t *gid)
return 1; 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 #ifdef CONFIG_SUN_OPENPROMFS_MODULE
if ((inode->i_ino >= PROC_OPENPROM_FIRST) if ((inode->i_ino >= PROC_OPENPROM_FIRST)
...@@ -95,23 +153,29 @@ struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_e ...@@ -95,23 +153,29 @@ struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_e
&& proc_openprom_use) && proc_openprom_use)
(*proc_openprom_use)(inode, 1); (*proc_openprom_use)(inode, 1);
#endif #endif
if (inode && inode->i_sb == s) { /* N.B. How can this test ever fail?? */
inode->u.generic_ip = (void *) de; if (inode->i_sb != sb)
if (de) { printk("proc_get_inode: inode fubar\n");
if (de->mode) {
inode->i_mode = de->mode; inode->u.generic_ip = (void *) de;
inode->i_uid = de->uid; if (de) {
inode->i_gid = de->gid; if (de->mode) {
} inode->i_mode = de->mode;
if (de->size) inode->i_uid = de->uid;
inode->i_size = de->size; inode->i_gid = de->gid;
if (de->ops)
inode->i_op = de->ops;
if (de->nlink)
inode->i_nlink = de->nlink;
if (de->fill_inode)
de->fill_inode(inode);
} }
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 * Fixup the root inode's nlink value
...@@ -126,26 +190,40 @@ struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_e ...@@ -126,26 +190,40 @@ struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_e
} }
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
} }
out:
return inode; return inode;
out_fail:
de_put(de);
goto out;
} }
struct super_block *proc_read_super(struct super_block *s,void *data, struct super_block *proc_read_super(struct super_block *s,void *data,
int silent) int silent)
{ {
struct inode * root_inode;
lock_super(s); lock_super(s);
s->s_blocksize = 1024; s->s_blocksize = 1024;
s->s_blocksize_bits = 10; s->s_blocksize_bits = 10;
s->s_magic = PROC_SUPER_MAGIC; s->s_magic = PROC_SUPER_MAGIC;
s->s_op = &proc_sops; 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); 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; 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) int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
static int proc_root_readdir(struct file *, void *, filldir_t); static int proc_root_readdir(struct file *, void *, filldir_t);
static int proc_root_lookup(struct inode *,struct dentry *); 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}; static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};
...@@ -72,6 +73,29 @@ struct inode_operations proc_dir_inode_operations = { ...@@ -72,6 +73,29 @@ struct inode_operations proc_dir_inode_operations = {
NULL /* permission */ 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 * The root /proc directory is special, as it has the
* <pid> directories. Thus we don't use the generic * <pid> directories. Thus we don't use the generic
...@@ -173,7 +197,8 @@ proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, fil ...@@ -173,7 +197,8 @@ proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, fil
int proc_openprom_regdev(struct openpromfs_dev *d) 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->next = proc_openprom_devices;
d->inode = proc_openpromdev_ino++; d->inode = proc_openpromdev_ino++;
proc_openprom_devices = d; proc_openprom_devices = d;
...@@ -218,6 +243,7 @@ proc_openprom_defreaddir(struct inode * inode, struct file * filp, ...@@ -218,6 +243,7 @@ proc_openprom_defreaddir(struct inode * inode, struct file * filp,
(inode, filp, dirent, filldir); (inode, filp, dirent, filldir);
return -EINVAL; return -EINVAL;
} }
#define OPENPROM_DEFREADDIR proc_openprom_defreaddir
static int static int
proc_openprom_deflookup(struct inode * dir, struct dentry *dentry) proc_openprom_deflookup(struct inode * dir, struct dentry *dentry)
...@@ -229,17 +255,17 @@ proc_openprom_deflookup(struct inode * dir, struct dentry *dentry) ...@@ -229,17 +255,17 @@ proc_openprom_deflookup(struct inode * dir, struct dentry *dentry)
(dir, dentry); (dir, dentry);
return -ENOENT; return -ENOENT;
} }
#define OPENPROM_DEFLOOKUP proc_openprom_deflookup
#else
#define OPENPROM_DEFREADDIR NULL
#define OPENPROM_DEFLOOKUP NULL
#endif #endif
static struct file_operations proc_openprom_operations = { static struct file_operations proc_openprom_operations = {
NULL, /* lseek - default */ NULL, /* lseek - default */
NULL, /* read - bad */ NULL, /* read - bad */
NULL, /* write - bad */ NULL, /* write - bad */
#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD) OPENPROM_DEFREADDIR, /* readdir */
proc_openprom_defreaddir,/* readdir */
#else
NULL, /* readdir */
#endif
NULL, /* poll - default */ NULL, /* poll - default */
NULL, /* ioctl - default */ NULL, /* ioctl - default */
NULL, /* mmap */ NULL, /* mmap */
...@@ -251,11 +277,7 @@ static struct file_operations proc_openprom_operations = { ...@@ -251,11 +277,7 @@ static struct file_operations proc_openprom_operations = {
struct inode_operations proc_openprom_inode_operations = { struct inode_operations proc_openprom_inode_operations = {
&proc_openprom_operations,/* default net directory file-ops */ &proc_openprom_operations,/* default net directory file-ops */
NULL, /* create */ NULL, /* create */
#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD) OPENPROM_DEFLOOKUP, /* lookup */
proc_openprom_deflookup,/* lookup */
#else
NULL, /* lookup */
#endif
NULL, /* link */ NULL, /* link */
NULL, /* unlink */ NULL, /* unlink */
NULL, /* symlink */ NULL, /* symlink */
...@@ -638,6 +660,26 @@ void proc_root_init(void) ...@@ -638,6 +660,26 @@ void proc_root_init(void)
#endif #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 * Don't create negative dentries here, return -ENOENT by hand
* instead. * instead.
...@@ -646,12 +688,15 @@ int proc_lookup(struct inode * dir, struct dentry *dentry) ...@@ -646,12 +688,15 @@ int proc_lookup(struct inode * dir, struct dentry *dentry)
{ {
struct inode *inode; struct inode *inode;
struct proc_dir_entry * de; struct proc_dir_entry * de;
int error;
error = -ENOTDIR;
if (!dir || !S_ISDIR(dir->i_mode)) if (!dir || !S_ISDIR(dir->i_mode))
return -ENOTDIR; goto out;
de = (struct proc_dir_entry *) dir->u.generic_ip; error = -ENOENT;
inode = NULL; inode = NULL;
de = (struct proc_dir_entry *) dir->u.generic_ip;
if (de) { if (de) {
for (de = de->subdir; de ; de = de->next) { for (de = de->subdir; de ; de = de->next) {
if (!de || !de->low_ino) if (!de || !de->low_ino)
...@@ -660,18 +705,20 @@ int proc_lookup(struct inode * dir, struct dentry *dentry) ...@@ -660,18 +705,20 @@ int proc_lookup(struct inode * dir, struct dentry *dentry)
continue; continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
int ino = de->low_ino | (dir->i_ino & ~(0xffff)); int ino = de->low_ino | (dir->i_ino & ~(0xffff));
error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de); inode = proc_get_inode(dir->i_sb, ino, de);
if (!inode)
return -EINVAL;
break; break;
} }
} }
} }
if (!inode)
return -ENOENT;
d_add(dentry, inode); if (inode) {
return 0; 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) static int proc_root_lookup(struct inode * dir, struct dentry * dentry)
...@@ -721,6 +768,8 @@ static int proc_root_lookup(struct inode * dir, struct dentry * dentry) ...@@ -721,6 +768,8 @@ static int proc_root_lookup(struct inode * dir, struct dentry * dentry)
if (!inode) if (!inode)
return -EINVAL; return -EINVAL;
} }
dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode); d_add(dentry, inode);
return 0; return 0;
} }
...@@ -827,3 +876,15 @@ static int proc_root_readdir(struct file * filp, ...@@ -827,3 +876,15 @@ static int proc_root_readdir(struct file * filp,
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
return 0; 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;
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
# Note 2! The CFLAGS definitions are now in the main makefile... # Note 2! The CFLAGS definitions are now in the main makefile...
O_TARGET := smbfs.o 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) M_OBJS := $(O_TARGET)
# If you want debugging output, please uncomment the following line # If you want debugging output, please uncomment the following line
......
/*
* 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;
}
This diff is collapsed.
...@@ -23,17 +23,29 @@ ...@@ -23,17 +23,29 @@
/* #define SMBFS_DEBUG_VERBOSE 1 */ /* #define SMBFS_DEBUG_VERBOSE 1 */
/* #define pr_debug printk */ /* #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 static inline int
min(int a, int b) min(int a, int b)
{ {
return a < b ? a : 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 static int
smb_fsync(struct file *file, struct dentry * dentry) smb_fsync(struct file *file, struct dentry * dentry)
{ {
printk("smb_fsync: sync file %s/%s\n", #ifdef SMBFS_DEBUG_VERBOSE
dentry->d_parent->d_name.name, dentry->d_name.name); printk("smb_fsync: sync file %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
return 0; return 0;
} }
...@@ -43,14 +55,13 @@ smb_fsync(struct file *file, struct dentry * dentry) ...@@ -43,14 +55,13 @@ smb_fsync(struct file *file, struct dentry * dentry)
static int static int
smb_readpage_sync(struct inode *inode, struct page *page) smb_readpage_sync(struct inode *inode, struct page *page)
{ {
unsigned long offset = page->offset;
char *buffer = (char *) page_address(page); char *buffer = (char *) page_address(page);
unsigned long offset = page->offset;
struct dentry * dentry = inode->u.smbfs_i.dentry; struct dentry * dentry = inode->u.smbfs_i.dentry;
int rsize = SMB_SERVER(inode)->opt.max_xmit - (SMB_HEADER_LEN+15); int rsize = smb_get_rsize(SMB_SERVER(inode));
int result, refresh = 0;
int count = PAGE_SIZE; int count = PAGE_SIZE;
int result;
pr_debug("SMB: smb_readpage_sync(%p)\n", page);
clear_bit(PG_error, &page->flags); clear_bit(PG_error, &page->flags);
result = -EIO; result = -EIO;
...@@ -60,24 +71,22 @@ smb_readpage_sync(struct inode *inode, struct page *page) ...@@ -60,24 +71,22 @@ smb_readpage_sync(struct inode *inode, struct page *page)
goto io_error; 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); result = smb_open(dentry, O_RDONLY);
if (result < 0) if (result < 0)
goto io_error; goto io_error;
/* Should revalidate inode ... */
do { do {
if (count < rsize) if (count < rsize)
rsize = count; 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); result = smb_proc_read(inode, offset, rsize, buffer);
if (result < 0) if (result < 0)
goto io_error; goto io_error;
refresh = 1;
count -= result; count -= result;
offset += result; offset += result;
buffer += result; buffer += result;
...@@ -90,10 +99,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, offset, buffer, rsize); ...@@ -90,10 +99,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, offset, buffer, rsize);
result = 0; result = 0;
io_error: io_error:
if (refresh) smb_unlock_page(page);
smb_refresh_inode(inode);
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
return result; return result;
} }
...@@ -110,7 +116,7 @@ smb_readpage(struct inode *inode, struct page *page) ...@@ -110,7 +116,7 @@ smb_readpage(struct inode *inode, struct page *page)
set_bit(PG_locked, &page->flags); set_bit(PG_locked, &page->flags);
atomic_inc(&page->count); atomic_inc(&page->count);
error = smb_readpage_sync(inode, page); error = smb_readpage_sync(inode, page);
__free_page(page); free_page(page_address(page));
return error; return error;
} }
...@@ -122,24 +128,24 @@ static int ...@@ -122,24 +128,24 @@ static int
smb_writepage_sync(struct inode *inode, struct page *page, smb_writepage_sync(struct inode *inode, struct page *page,
unsigned long offset, unsigned int count) 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; 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; 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 { do {
if (count < wsize) if (count < wsize)
wsize = count; wsize = count;
result = smb_proc_write(inode, offset, wsize, buffer); result = smb_proc_write(inode, offset, wsize, buffer);
if (result < 0)
if (result < 0) { {
/* Must mark the page invalid after I/O error */ /* Must mark the page invalid after I/O error */
clear_bit(PG_uptodate, &page->flags); clear_bit(PG_uptodate, &page->flags);
goto io_error; goto io_error;
...@@ -157,11 +163,11 @@ printk("smb_writepage_sync: short write, wsize=%d, result=%d\n", wsize, result); ...@@ -157,11 +163,11 @@ printk("smb_writepage_sync: short write, wsize=%d, result=%d\n", wsize, result);
} while (count); } while (count);
io_error: io_error:
#if 0
if (refresh) if (refresh)
smb_refresh_inode(inode); smb_refresh_inode(inode);
#endif
clear_bit(PG_locked, &page->flags); smb_unlock_page(page);
wake_up(&page->wait);
return written ? written : result; return written ? written : result;
} }
...@@ -181,7 +187,7 @@ smb_writepage(struct inode *inode, struct page *page) ...@@ -181,7 +187,7 @@ smb_writepage(struct inode *inode, struct page *page)
set_bit(PG_locked, &page->flags); set_bit(PG_locked, &page->flags);
atomic_inc(&page->count); atomic_inc(&page->count);
result = smb_writepage_sync(inode, page, 0, PAGE_SIZE); result = smb_writepage_sync(inode, page, 0, PAGE_SIZE);
__free_page(page); free_page(page_address(page));
return result; return result;
} }
...@@ -189,8 +195,8 @@ static int ...@@ -189,8 +195,8 @@ static int
smb_updatepage(struct inode *inode, struct page *page, const char *buffer, smb_updatepage(struct inode *inode, struct page *page, const char *buffer,
unsigned long offset, unsigned int count, int sync) unsigned long offset, unsigned int count, int sync)
{ {
u8 *page_addr; unsigned long page_addr = page_address(page);
int result; int result;
pr_debug("SMB: smb_updatepage(%x/%ld %d@%ld, sync=%d)\n", pr_debug("SMB: smb_updatepage(%x/%ld %d@%ld, sync=%d)\n",
inode->i_dev, inode->i_ino, inode->i_dev, inode->i_ino,
...@@ -203,21 +209,21 @@ smb_updatepage(struct inode *inode, struct page *page, const char *buffer, ...@@ -203,21 +209,21 @@ smb_updatepage(struct inode *inode, struct page *page, const char *buffer,
set_bit(PG_locked, &page->flags); set_bit(PG_locked, &page->flags);
atomic_inc(&page->count); atomic_inc(&page->count);
page_addr = (u8 *) page_address(page); if (copy_from_user((char *) page_addr + offset, buffer, count))
if (copy_from_user(page_addr + offset, buffer, count))
goto bad_fault; goto bad_fault;
result = smb_writepage_sync(inode, page, offset, count); result = smb_writepage_sync(inode, page, offset, count);
out: out:
__free_page(page); free_page(page_addr);
return result; return result;
bad_fault: 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; result = -EFAULT;
clear_bit(PG_uptodate, &page->flags); clear_bit(PG_uptodate, &page->flags);
clear_bit(PG_locked, &page->flags); smb_unlock_page(page);
wake_up(&page->wait);
goto out; goto out;
} }
...@@ -227,9 +233,11 @@ smb_file_read(struct inode * inode, struct file * file, ...@@ -227,9 +233,11 @@ smb_file_read(struct inode * inode, struct file * file,
{ {
int status; int status;
pr_debug("SMB: read(%x/%ld (%d), %lu@%lu)\n", #ifdef SMBFS_DEBUG_VERBOSE
inode->i_dev, inode->i_ino, inode->i_count, printk("smb_file_read: file %s/%s, count=%lu@%lu\n",
count, (unsigned long) file->f_pos); 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); status = smb_revalidate_inode(inode);
if (status >= 0) if (status >= 0)
...@@ -246,6 +254,11 @@ smb_file_mmap(struct file * file, struct vm_area_struct * vma) ...@@ -246,6 +254,11 @@ smb_file_mmap(struct file * file, struct vm_area_struct * vma)
struct inode * inode = dentry->d_inode; struct inode * inode = dentry->d_inode;
int status; 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); status = smb_revalidate_inode(inode);
if (status >= 0) if (status >= 0)
{ {
...@@ -263,40 +276,67 @@ smb_file_write(struct inode *inode, struct file *file, ...@@ -263,40 +276,67 @@ smb_file_write(struct inode *inode, struct file *file,
{ {
int result; int result;
pr_debug("SMB: write(%x/%ld (%d), %lu@%lu)\n", #ifdef SMBFS_DEBUG_VERBOSE
inode->i_dev, inode->i_ino, inode->i_count, printk("smb_file_write: file %s/%s, count=%lu@%lu\n",
count, (unsigned long) file->f_pos); 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; result = -EINVAL;
if (!inode) { if (!S_ISREG(inode->i_mode))
printk("smb_file_write: inode = NULL\n"); {
printk("smb_file_write: write to non-file, mode %07o\n",
inode->i_mode);
goto out; goto out;
} }
#endif
result = smb_revalidate_inode(inode); result = smb_revalidate_inode(inode);
if (result < 0) if (result)
goto out; goto out;
result = smb_open(file->f_dentry, O_WRONLY); result = smb_open(file->f_dentry, O_WRONLY);
if (result < 0) if (result)
goto out; 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) if (count > 0)
{ {
result = generic_file_write(inode, file, buf, count); result = generic_file_write(inode, file, buf, count);
if (result > 0)
smb_refresh_inode(inode);
} }
out: out:
return result; 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 = static struct file_operations smb_file_operations =
{ {
NULL, /* lseek - default */ NULL, /* lseek - default */
...@@ -305,10 +345,14 @@ static struct file_operations smb_file_operations = ...@@ -305,10 +345,14 @@ static struct file_operations smb_file_operations =
NULL, /* readdir - bad */ NULL, /* readdir - bad */
NULL, /* poll - default */ NULL, /* poll - default */
smb_ioctl, /* ioctl */ smb_ioctl, /* ioctl */
smb_file_mmap, /* mmap */ smb_file_mmap, /* mmap(struct file*, struct vm_area_struct*) */
NULL, /* open */ smb_file_open, /* open(struct inode*, struct file*) */
NULL, /* release */ smb_file_release, /* release(struct inode*, struct file*) */
smb_fsync, /* fsync */ 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 = struct inode_operations smb_file_inode_operations =
......
This diff is collapsed.
This diff is collapsed.
...@@ -126,18 +126,26 @@ smb_data_callback(struct sock *sk, int len) ...@@ -126,18 +126,26 @@ smb_data_callback(struct sock *sk, int len)
} }
} }
int
smb_valid_socket(struct inode * inode)
{
return (inode && S_ISSOCK(inode->i_mode) &&
inode->u.socket_i.type == SOCK_STREAM);
}
static struct socket * static struct socket *
server_sock(struct smb_sb_info *server) server_sock(struct smb_sb_info *server)
{ {
struct file *file; struct file *file;
struct inode *inode;
if (server && (file = server->sock_file))
if (server && {
(file = server->sock_file) && #ifdef SMBFS_PARANOIA
(inode = file->f_dentry->d_inode) && if (!smb_valid_socket(file->f_dentry->d_inode))
S_ISSOCK(inode->i_mode) && printk("smb_server_sock: bad socket!\n");
inode->u.socket_i.type == SOCK_STREAM) #endif
return &(inode->u.socket_i); return &file->f_dentry->d_inode->u.socket_i;
}
return NULL; return NULL;
} }
...@@ -242,15 +250,13 @@ smb_close_socket(struct smb_sb_info *server) ...@@ -242,15 +250,13 @@ smb_close_socket(struct smb_sb_info *server)
if (file) if (file)
{ {
struct socket * socket = server_sock(server); #ifdef SMBFS_DEBUG_VERBOSE
printk("smb_close_socket: closing socket %p\n", server_sock(server));
printk("smb_close_socket: closing socket %p\n", socket); #endif
/* #ifdef SMBFS_PARANOIA
* We need a way to check for tasks running the callback! if (server_sock(server)->sk->data_ready == smb_data_callback)
*/ printk("smb_close_socket: still catching keepalives!\n");
if (socket->sk->data_ready == smb_data_callback) #endif
printk("smb_close_socket: still catching keepalives!\n");
server->sock_file = NULL; server->sock_file = NULL;
close_fp(file); close_fp(file);
} }
...@@ -325,7 +331,9 @@ smb_get_length(struct socket *socket, unsigned char *header) ...@@ -325,7 +331,9 @@ smb_get_length(struct socket *socket, unsigned char *header)
if (result < 0) 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; return result;
} }
switch (peek_buf[0]) switch (peek_buf[0])
...@@ -339,7 +347,9 @@ smb_get_length(struct socket *socket, unsigned char *header) ...@@ -339,7 +347,9 @@ smb_get_length(struct socket *socket, unsigned char *header)
goto re_recv; goto re_recv;
default: 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; return -EIO;
} }
...@@ -359,39 +369,39 @@ static int ...@@ -359,39 +369,39 @@ static int
smb_receive(struct smb_sb_info *server) smb_receive(struct smb_sb_info *server)
{ {
struct socket *socket = server_sock(server); struct socket *socket = server_sock(server);
int len; int len, result;
int result;
unsigned char peek_buf[4]; unsigned char peek_buf[4];
len = smb_get_length(socket, peek_buf); result = smb_get_length(socket, peek_buf);
if (result < 0)
if (len < 0) goto out;
{ len = result;
return len; /*
} * 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) if (len + 4 > server->packet_size)
{ {
/* Some servers do not care about our max_xmit. They char * packet;
send larger packets */
pr_debug("smb_receive: Increase packet size from %d to %d\n", pr_debug("smb_receive: Increase packet size from %d to %d\n",
server->packet_size, len + 4); server->packet_size, len + 4);
result = -ENOMEM;
packet = smb_vmalloc(len + 4);
if (packet == NULL)
goto out;
smb_vfree(server->packet); smb_vfree(server->packet);
server->packet = 0; server->packet = packet;
server->packet_size = 0;
server->packet = smb_vmalloc(len + 4);
if (server->packet == NULL)
{
return -ENOMEM;
}
server->packet_size = len + 4; server->packet_size = len + 4;
} }
memcpy(server->packet, peek_buf, 4); memcpy(server->packet, peek_buf, 4);
result = smb_receive_raw(socket, server->packet + 4, len); result = smb_receive_raw(socket, server->packet + 4, len);
if (result < 0) if (result < 0)
{ {
pr_debug("smb_receive: receive error: %d\n", result); #ifdef SMBFS_DEBUG_VERBOSE
return result; printk("smb_receive: receive error: %d\n", result);
#endif
goto out;
} }
server->rcls = *(server->packet+9); server->rcls = *(server->packet+9);
server->err = WVAL(server->packet, 11); server->err = WVAL(server->packet, 11);
...@@ -400,9 +410,16 @@ smb_receive(struct smb_sb_info *server) ...@@ -400,9 +410,16 @@ smb_receive(struct smb_sb_info *server)
if (server->rcls != 0) if (server->rcls != 0)
printk("smb_receive: rcls=%d, err=%d\n", server->rcls, server->err); printk("smb_receive: rcls=%d, err=%d\n", server->rcls, server->err);
#endif #endif
out:
return result; 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 static int
smb_receive_trans2(struct smb_sb_info *server, smb_receive_trans2(struct smb_sb_info *server,
int *ldata, unsigned char **data, int *ldata, unsigned char **data,
...@@ -515,6 +532,11 @@ data_len, total_data, param_len, total_param); ...@@ -515,6 +532,11 @@ data_len, total_data, param_len, total_param);
*ldata = data_len; *ldata = data_len;
*lparam = param_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); smb_vfree(server->packet);
server->packet = rcv_buf; server->packet = rcv_buf;
server->packet_size = buf_len; server->packet_size = buf_len;
...@@ -537,9 +559,6 @@ smb_request(struct smb_sb_info *server) ...@@ -537,9 +559,6 @@ smb_request(struct smb_sb_info *server)
unsigned char *buffer; unsigned char *buffer;
result = -EBADF; result = -EBADF;
if (!server) /* this can't happen */
goto bad_no_server;
buffer = server->packet; buffer = server->packet;
if (!buffer) if (!buffer)
goto bad_no_packet; goto bad_no_packet;
...@@ -586,13 +605,12 @@ smb_request(struct smb_sb_info *server) ...@@ -586,13 +605,12 @@ smb_request(struct smb_sb_info *server)
return result; return result;
bad_conn: 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; server->state = CONN_INVALID;
smb_invalidate_inodes(server); smb_invalidate_inodes(server);
goto out; goto out;
bad_no_server:
printk("smb_request: no server!\n");
goto out;
bad_no_packet: bad_no_packet:
printk("smb_request: no packet!\n"); printk("smb_request: no packet!\n");
goto out; goto out;
...@@ -631,6 +649,7 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command, ...@@ -631,6 +649,7 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
struct iovec iov[4]; struct iovec iov[4];
struct msghdr msg; struct msghdr msg;
/* N.B. This test isn't valid! packet_size may be < max_xmit */
if ((bcc + oparam) > server->opt.max_xmit) if ((bcc + oparam) > server->opt.max_xmit)
{ {
return -ENOMEM; return -ENOMEM;
...@@ -639,6 +658,7 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command, ...@@ -639,6 +658,7 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
WSET(server->packet, smb_tpscnt, lparam); WSET(server->packet, smb_tpscnt, lparam);
WSET(server->packet, smb_tdscnt, ldata); 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_mprcnt, TRANS2_MAX_TRANSFER);
WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER); WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER);
WSET(server->packet, smb_msrcnt, 0); WSET(server->packet, smb_msrcnt, 0);
...@@ -745,7 +765,9 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, ...@@ -745,7 +765,9 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
return result; return result;
bad_conn: 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; server->state = CONN_INVALID;
smb_invalidate_inodes(server); smb_invalidate_inodes(server);
goto out; goto out;
......
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
* Added change_root: Werner Almesberger & Hans Lermen, Feb '96 * Added change_root: Werner Almesberger & Hans Lermen, Feb '96
*/ */
#include <stdarg.h>
#include <linux/config.h> #include <linux/config.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -252,29 +250,24 @@ static int fs_maxindex(void) ...@@ -252,29 +250,24 @@ static int fs_maxindex(void)
/* /*
* Whee.. Weird sysv syscall. * 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; int retval = -EINVAL;
unsigned int index;
lock_kernel(); lock_kernel();
va_start(args, option);
switch (option) { switch (option) {
case 1: case 1:
retval = fs_index(va_arg(args, const char *)); retval = fs_index((const char *) arg1);
break; break;
case 2: case 2:
index = va_arg(args, unsigned int); retval = fs_name(arg1, (char *) arg2);
retval = fs_name(index, va_arg(args, char *));
break; break;
case 3: case 3:
retval = fs_maxindex(); retval = fs_maxindex();
break; break;
} }
va_end(args);
unlock_kernel(); unlock_kernel();
return retval; return retval;
} }
...@@ -933,12 +926,11 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, ...@@ -933,12 +926,11 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
struct file_system_type * fstype; struct file_system_type * fstype;
struct dentry * dentry = NULL; struct dentry * dentry = NULL;
struct inode * inode = NULL; struct inode * inode = NULL;
struct file_operations * fops;
kdev_t dev; kdev_t dev;
int retval = -EPERM; int retval = -EPERM;
const char * t;
unsigned long flags = 0; unsigned long flags = 0;
unsigned long page = 0; unsigned long page = 0;
struct file dummy; /* allows read-write or read-only flag */
lock_kernel(); lock_kernel();
if (!suser()) if (!suser())
...@@ -954,6 +946,7 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, ...@@ -954,6 +946,7 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
free_page(page); free_page(page);
goto out; goto out;
} }
retval = copy_mount_options (type, &page); retval = copy_mount_options (type, &page);
if (retval < 0) if (retval < 0)
goto out; goto out;
...@@ -962,8 +955,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, ...@@ -962,8 +955,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
retval = -ENODEV; retval = -ENODEV;
if (!fstype) if (!fstype)
goto out; goto out;
t = fstype->name;
fops = NULL; memset(&dummy, 0, sizeof(dummy));
if (fstype->fs_flags & FS_REQUIRES_DEV) { if (fstype->fs_flags & FS_REQUIRES_DEV) {
dentry = namei(dev_name); dentry = namei(dev_name);
retval = PTR_ERR(dentry); retval = PTR_ERR(dentry);
...@@ -984,17 +977,15 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, ...@@ -984,17 +977,15 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
if (MAJOR(dev) >= MAX_BLKDEV) if (MAJOR(dev) >= MAX_BLKDEV)
goto dput_and_out; goto dput_and_out;
fops = get_blkfops(MAJOR(dev));
retval = -ENOTBLK; retval = -ENOTBLK;
if (!fops) dummy.f_op = get_blkfops(MAJOR(dev));
if (!dummy.f_op)
goto dput_and_out; goto dput_and_out;
if (fops->open) { if (dummy.f_op->open) {
struct file dummy; /* allows read-write or read-only flag */
memset(&dummy, 0, sizeof(dummy));
dummy.f_dentry = dentry; dummy.f_dentry = dentry;
dummy.f_mode = (new_flags & MS_RDONLY) ? 1 : 3; dummy.f_mode = (new_flags & MS_RDONLY) ? 1 : 3;
retval = fops->open(inode, &dummy); retval = dummy.f_op->open(inode, &dummy);
if (retval) if (retval)
goto dput_and_out; goto dput_and_out;
} }
...@@ -1009,22 +1000,28 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, ...@@ -1009,22 +1000,28 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) { if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) {
flags = new_flags & ~MS_MGC_MSK; flags = new_flags & ~MS_MGC_MSK;
retval = copy_mount_options(data, &page); retval = copy_mount_options(data, &page);
if (retval < 0) { if (retval < 0)
put_unnamed_dev(dev); goto clean_up;
goto dput_and_out;
}
} }
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); free_page(page);
if (retval && fops && fops->release) { if (retval)
fops->release(inode, NULL); goto clean_up;
put_unnamed_dev(dev);
}
dput_and_out: dput_and_out:
dput(dentry); dput(dentry);
out: out:
unlock_kernel(); unlock_kernel();
return retval; 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)) __initfunc(static void do_mount_root(void))
......
...@@ -104,6 +104,7 @@ extern void d_delete(struct dentry *); ...@@ -104,6 +104,7 @@ extern void d_delete(struct dentry *);
/* allocate/de-allocate */ /* allocate/de-allocate */
extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name); extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name);
extern void prune_dcache(int); extern void prune_dcache(int);
extern void shrink_dcache_sb(struct super_block *);
extern int d_invalidate(struct dentry *); extern int d_invalidate(struct dentry *);
#define shrink_dcache() prune_dcache(0) #define shrink_dcache() prune_dcache(0)
......
...@@ -42,9 +42,10 @@ ...@@ -42,9 +42,10 @@
/* And dynamically-tunable limits and defaults: */ /* And dynamically-tunable limits and defaults: */
extern int max_inodes; 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_INODE 4096 /* this should be bigger than NR_FILE */
#define NR_FILE 1024 /* this can well be larger on a larger system */ #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_EXEC 1
#define MAY_WRITE 2 #define MAY_WRITE 2
...@@ -628,6 +629,10 @@ extern struct inode_operations chrdev_inode_operations; ...@@ -628,6 +629,10 @@ extern struct inode_operations chrdev_inode_operations;
extern void init_fifo(struct inode * inode); extern void init_fifo(struct inode * inode);
extern struct inode_operations fifo_inode_operations; 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 connecting_fifo_fops;
extern struct file_operations read_fifo_fops; extern struct file_operations read_fifo_fops;
extern struct file_operations write_fifo_fops; extern struct file_operations write_fifo_fops;
......
...@@ -3,7 +3,14 @@ ...@@ -3,7 +3,14 @@
/* /*
* Simple doubly linked list implementation. * 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 {
struct list_head *next, *prev; struct list_head *next, *prev;
}; };
...@@ -15,24 +22,48 @@ struct list_head { ...@@ -15,24 +22,48 @@ struct list_head {
(ptr)->next = (ptr); (ptr)->prev = (ptr); \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0) } 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; next->prev = new;
new->next = next; new->next = next;
new->prev = head; new->prev = prev;
head->next = new; 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; next->prev = prev;
prev->next = next; 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) static inline int list_empty(struct list_head *head)
{ {
return head->next == head; return head->next == head;
......
...@@ -296,6 +296,8 @@ extern unsigned long get_unmapped_area(unsigned long, unsigned long); ...@@ -296,6 +296,8 @@ extern unsigned long get_unmapped_area(unsigned long, unsigned long);
extern unsigned long page_unuse(unsigned long); extern unsigned long page_unuse(unsigned long);
extern int shrink_mmap(int, int); extern int shrink_mmap(int, int);
extern void truncate_inode_pages(struct inode *, unsigned long); 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_BUFFER 0x00
#define GFP_ATOMIC 0x01 #define GFP_ATOMIC 0x01
......
...@@ -129,14 +129,6 @@ struct nfs_sattr { ...@@ -129,14 +129,6 @@ struct nfs_sattr {
struct nfs_time mtime; struct nfs_time mtime;
}; };
struct nfs_entry {
__u32 fileid;
char * name;
unsigned int length:31,
eof:1;
__u32 cookie;
};
struct nfs_fsinfo { struct nfs_fsinfo {
__u32 tsize; __u32 tsize;
__u32 bsize; __u32 bsize;
......
...@@ -125,7 +125,7 @@ extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir, ...@@ -125,7 +125,7 @@ extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
const char *name); const char *name);
extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, 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, extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *res); struct nfs_fsinfo *res);
...@@ -138,7 +138,7 @@ extern struct super_block *nfs_read_super(struct super_block *sb, ...@@ -138,7 +138,7 @@ extern struct super_block *nfs_read_super(struct super_block *sb,
extern int init_nfs_fs(void); extern int init_nfs_fs(void);
extern struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, extern struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
struct nfs_fattr *fattr); 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(struct inode *);
extern int _nfs_revalidate_inode(struct nfs_server *, struct inode *); extern int _nfs_revalidate_inode(struct nfs_server *, struct inode *);
......
...@@ -237,13 +237,15 @@ struct proc_dir_entry { ...@@ -237,13 +237,15 @@ struct proc_dir_entry {
unsigned long size; unsigned long size;
struct inode_operations * ops; struct inode_operations * ops;
int (*get_info)(char *, char **, off_t, int, int); 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; struct proc_dir_entry *next, *parent, *subdir;
void *data; void *data;
int (*read_proc)(char *page, char **start, off_t off, int (*read_proc)(char *page, char **start, off_t off,
int count, int *eof, void *data); int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char *buffer, int (*write_proc)(struct file *file, const char *buffer,
unsigned long count, void *data); 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, extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
......
This diff is collapsed.
...@@ -22,11 +22,13 @@ struct smb_inode_info { ...@@ -22,11 +22,13 @@ struct smb_inode_info {
* (open == generation). * (open == generation).
*/ */
unsigned int open; unsigned int open;
void * dentry; /* The dentry we were opened with */
__u16 fileid; /* What id to handle a file with? */ __u16 fileid; /* What id to handle a file with? */
__u16 attr; /* Attribute fields, DOS value */ __u16 attr; /* Attribute fields, DOS value */
__u16 access; /* Access bits. */ __u16 access; /* Access bits. */
__u16 cache_valid; /* dircache valid? */
unsigned long oldmtime; /* last time refreshed */
void * dentry; /* The dentry we were opened with */
}; };
#endif #endif
......
...@@ -15,6 +15,11 @@ ...@@ -15,6 +15,11 @@
#include <linux/smb.h> #include <linux/smb.h>
#include <linux/smb_mount.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 { struct smb_sb_info {
enum smb_conn_state state; enum smb_conn_state state;
struct file * sock_file; struct file * sock_file;
...@@ -29,6 +34,7 @@ struct smb_sb_info { ...@@ -29,6 +34,7 @@ struct smb_sb_info {
struct smb_conn_opt opt; struct smb_conn_opt opt;
struct semaphore sem; struct semaphore sem;
struct wait_queue * wait;
__u32 packet_size; __u32 packet_size;
unsigned char * packet; unsigned char * packet;
......
...@@ -143,7 +143,7 @@ void rpc_del_timer(struct rpc_task *); ...@@ -143,7 +143,7 @@ void rpc_del_timer(struct rpc_task *);
void rpc_delay(struct rpc_task *, unsigned long); void rpc_delay(struct rpc_task *, unsigned long);
void * rpc_allocate(unsigned int flags, unsigned int); void * rpc_allocate(unsigned int flags, unsigned int);
void rpc_free(void *); void rpc_free(void *);
void rpciod_up(void); int rpciod_up(void);
void rpciod_down(void); void rpciod_down(void);
extern __inline__ void * extern __inline__ void *
......
...@@ -208,7 +208,6 @@ static inline int dup_mmap(struct mm_struct * mm) ...@@ -208,7 +208,6 @@ static inline int dup_mmap(struct mm_struct * mm)
struct vm_area_struct * mpnt, *tmp, **pprev; struct vm_area_struct * mpnt, *tmp, **pprev;
int retval; int retval;
mm->mmap = mm->mmap_cache = NULL;
flush_cache_mm(current->mm); flush_cache_mm(current->mm);
pprev = &mm->mmap; pprev = &mm->mmap;
for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) { for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
...@@ -254,8 +253,7 @@ static inline int dup_mmap(struct mm_struct * mm) ...@@ -254,8 +253,7 @@ static inline int dup_mmap(struct mm_struct * mm)
if (retval) if (retval)
goto fail_nomem; goto fail_nomem;
} }
flush_tlb_mm(current->mm); retval = 0;
return 0;
fail_nomem: fail_nomem:
flush_tlb_mm(current->mm); flush_tlb_mm(current->mm);
...@@ -276,7 +274,10 @@ struct mm_struct * mm_alloc(void) ...@@ -276,7 +274,10 @@ struct mm_struct * mm_alloc(void)
mm->count = 1; mm->count = 1;
mm->def_flags = 0; mm->def_flags = 0;
mm->mmap_sem = MUTEX; 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; mm->mmap = mm->mmap_cache = NULL;
/* It has not run yet, so cannot be present in anyone's /* It has not run yet, so cannot be present in anyone's
...@@ -324,10 +325,12 @@ static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk) ...@@ -324,10 +325,12 @@ static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
goto free_mm; goto free_mm;
retval = dup_mmap(mm); retval = dup_mmap(mm);
if (retval) if (retval)
goto free_mm; goto free_pt;
return 0; return 0;
free_mm: free_mm:
mm->pgd = NULL;
free_pt:
tsk->mm = NULL; tsk->mm = NULL;
mmput(mm); mmput(mm);
fail_nomem: fail_nomem:
...@@ -376,7 +379,13 @@ static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk ...@@ -376,7 +379,13 @@ static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk
struct files_struct *oldf, *newf; struct files_struct *oldf, *newf;
struct file **old_fds, **new_fds; struct file **old_fds, **new_fds;
/*
* A background process may not have any files ...
*/
oldf = current->files; oldf = current->files;
if (!oldf)
return 0;
if (clone_flags & CLONE_FILES) { if (clone_flags & CLONE_FILES) {
oldf->count++; oldf->count++;
return 0; return 0;
...@@ -516,7 +525,9 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) ...@@ -516,7 +525,9 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
} }
++total_forks; ++total_forks;
error = p->pid; error = p->pid;
goto fork_out; bad_fork:
unlock_kernel();
return error;
bad_fork_cleanup_sighand: bad_fork_cleanup_sighand:
exit_sighand(p); exit_sighand(p);
...@@ -536,10 +547,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) ...@@ -536,10 +547,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
nr_tasks--; nr_tasks--;
bad_fork_free: bad_fork_free:
free_task_struct(p); free_task_struct(p);
bad_fork: goto bad_fork;
fork_out:
unlock_kernel();
return error;
} }
static void files_ctor(void *fp, kmem_cache_t *cachep, unsigned long flags) static void files_ctor(void *fp, kmem_cache_t *cachep, unsigned long flags)
......
...@@ -192,6 +192,8 @@ EXPORT_SYMBOL(posix_test_lock); ...@@ -192,6 +192,8 @@ EXPORT_SYMBOL(posix_test_lock);
EXPORT_SYMBOL(posix_block_lock); EXPORT_SYMBOL(posix_block_lock);
EXPORT_SYMBOL(posix_unblock_lock); EXPORT_SYMBOL(posix_unblock_lock);
EXPORT_SYMBOL(dput); EXPORT_SYMBOL(dput);
EXPORT_SYMBOL(get_cached_page);
EXPORT_SYMBOL(put_cached_page);
#if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE) #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
EXPORT_SYMBOL(do_nfsservctl); EXPORT_SYMBOL(do_nfsservctl);
...@@ -369,6 +371,8 @@ EXPORT_SYMBOL(read_ahead); ...@@ -369,6 +371,8 @@ EXPORT_SYMBOL(read_ahead);
EXPORT_SYMBOL(get_hash_table); EXPORT_SYMBOL(get_hash_table);
EXPORT_SYMBOL(get_empty_inode); EXPORT_SYMBOL(get_empty_inode);
EXPORT_SYMBOL(insert_inode_hash); EXPORT_SYMBOL(insert_inode_hash);
EXPORT_SYMBOL(make_bad_inode);
EXPORT_SYMBOL(is_bad_inode);
EXPORT_SYMBOL(event); EXPORT_SYMBOL(event);
EXPORT_SYMBOL(__down); EXPORT_SYMBOL(__down);
EXPORT_SYMBOL(__up); EXPORT_SYMBOL(__up);
......
...@@ -147,7 +147,7 @@ static ctl_table kern_table[] = { ...@@ -147,7 +147,7 @@ static ctl_table kern_table[] = {
0444, NULL, &proc_dointvec}, 0444, NULL, &proc_dointvec},
{KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int), {KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int),
0644, NULL, &proc_dointvec}, 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}, 0444, NULL, &proc_dointvec},
{KERN_MAXFILE, "file-max", &max_files, sizeof(int), {KERN_MAXFILE, "file-max", &max_files, sizeof(int),
0644, NULL, &proc_dointvec}, 0644, NULL, &proc_dointvec},
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -54,15 +54,16 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) ...@@ -54,15 +54,16 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt)
} }
clnt->cl_binding = 1; clnt->cl_binding = 1;
task->tk_status = 0; task->tk_status = -EACCES; /* why set this? returns -EIO below */
if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) { if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot)))
task->tk_status = -EACCES;
goto bailout; goto bailout;
} task->tk_status = 0;
if (!(child = rpc_new_child(pmap_clnt, task))) {
rpc_destroy_client(pmap_clnt); /*
* Note: rpc_new_child will release client after a failure.
*/
if (!(child = rpc_new_child(pmap_clnt, task)))
goto bailout; goto bailout;
}
/* Setup the call info struct */ /* Setup the call info struct */
rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0); rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment