Commit f59dfe26 authored by Linus Torvalds's avatar Linus Torvalds

Import 2.1.58

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