X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fproc%2Fgeneric.c;h=c60d4538ffd3e85b53af54d23ed72ac3e85271e1;hb=refs%2Fheads%2Fvserver;hp=72ddf0faa71813ad5c34e4aeec87166560fe488f;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 72ddf0faa..c60d4538f 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -19,15 +19,20 @@ #include #include #include +#include #include #include +#include "internal.h" + static ssize_t proc_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos); static ssize_t proc_file_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); static loff_t proc_file_lseek(struct file *, loff_t, int); +DEFINE_SPINLOCK(proc_subdir_lock); + int proc_match(int len, const char *name, struct proc_dir_entry *de) { if (de->namelen != len) @@ -48,13 +53,25 @@ static ssize_t proc_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { - struct inode * inode = file->f_dentry->d_inode; + struct inode * inode = file->f_path.dentry->d_inode; char *page; ssize_t retval=0; int eof=0; ssize_t n, count; char *start; struct proc_dir_entry * dp; + unsigned long long pos; + + /* + * Gaah, please just use "seq_file" instead. The legacy /proc + * interfaces cut loff_t down to off_t for reads, and ignore + * the offset entirely for writes.. + */ + pos = *ppos; + if (pos > MAX_NON_LFS) + return 0; + if (nbytes > MAX_NON_LFS - pos) + nbytes = MAX_NON_LFS - pos; dp = PDE(inode); if (!(page = (char*) __get_free_page(GFP_KERNEL))) @@ -187,7 +204,7 @@ static ssize_t proc_file_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; struct proc_dir_entry * dp; dp = PDE(inode); @@ -203,30 +220,17 @@ proc_file_write(struct file *file, const char __user *buffer, static loff_t proc_file_lseek(struct file *file, loff_t offset, int orig) { - lock_kernel(); - - switch (orig) { - case 0: - if (offset < 0) - goto out; - file->f_pos = offset; - unlock_kernel(); - return(file->f_pos); - case 1: - if (offset + file->f_pos < 0) - goto out; - file->f_pos += offset; - unlock_kernel(); - return(file->f_pos); - case 2: - goto out; - default: - goto out; - } - -out: - unlock_kernel(); - return -EINVAL; + loff_t retval = -EINVAL; + switch (orig) { + case 1: + offset += file->f_pos; + /* fallthrough */ + case 0: + if (offset < 0 || offset > MAX_NON_LFS) + break; + file->f_pos = retval = offset; + } + return retval; } static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) @@ -250,6 +254,18 @@ out: return error; } +static int proc_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + struct proc_dir_entry *de = PROC_I(inode)->pde; + if (de && de->nlink) + inode->i_nlink = de->nlink; + + generic_fillattr(inode, stat); + return 0; +} + static struct inode_operations proc_file_inode_operations = { .setattr = proc_notify_change, }; @@ -265,7 +281,9 @@ static int xlate_proc_name(const char *name, const char *cp = name, *next; struct proc_dir_entry *de; int len; + int rtn = 0; + spin_lock(&proc_subdir_lock); de = &proc_root; while (1) { next = strchr(cp, '/'); @@ -277,13 +295,17 @@ static int xlate_proc_name(const char *name, if (proc_match(len, cp, de)) break; } - if (!de) - return -ENOENT; + if (!de) { + rtn = -ENOENT; + goto out; + } cp += len + 1; } *residual = cp; *ret = de; - return 0; +out: + spin_unlock(&proc_subdir_lock); + return rtn; } static DEFINE_IDR(proc_inum_idr); @@ -330,10 +352,10 @@ static void release_inode_number(unsigned int inum) spin_unlock(&proc_inum_lock); } -static int proc_follow_link(struct dentry *dentry, struct nameidata *nd) +static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) { nd_set_link(nd, PDE(dentry->d_inode)->data); - return 0; + return NULL; } static struct inode_operations proc_link_inode_operations = { @@ -368,6 +390,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam int error = -ENOENT; lock_kernel(); + spin_lock(&proc_subdir_lock); de = PDE(dir); if (de) { for (de = de->subdir; de ; de = de->next) { @@ -378,14 +401,17 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { unsigned int ino = de->low_ino; + spin_unlock(&proc_subdir_lock); error = -EINVAL; inode = proc_get_inode(dir->i_sb, ino, de); /* generic proc entries belong to the host */ - inode->i_xid = 0; + inode->i_tag = 0; + spin_lock(&proc_subdir_lock); break; } } } + spin_unlock(&proc_subdir_lock); unlock_kernel(); if (inode) { @@ -411,7 +437,7 @@ int proc_readdir(struct file * filp, struct proc_dir_entry * de; unsigned int ino; int i; - struct inode *inode = filp->f_dentry->d_inode; + struct inode *inode = filp->f_path.dentry->d_inode; int ret = 0; lock_kernel(); @@ -432,18 +458,20 @@ int proc_readdir(struct file * filp, /* fall through */ case 1: if (filldir(dirent, "..", 2, i, - parent_ino(filp->f_dentry), + parent_ino(filp->f_path.dentry), DT_DIR) < 0) goto out; i++; filp->f_pos++; /* fall through */ default: + spin_lock(&proc_subdir_lock); de = de->subdir; i -= 2; for (;;) { if (!de) { ret = 1; + spin_unlock(&proc_subdir_lock); goto out; } if (!i) @@ -455,13 +483,17 @@ int proc_readdir(struct file * filp, do { if (!vx_hide_check(0, de->vx_flags)) goto skip; + /* filldir passes info to user space */ + spin_unlock(&proc_subdir_lock); if (filldir(dirent, de->name, de->namelen, filp->f_pos, de->low_ino, de->mode >> 12) < 0) goto out; + spin_lock(&proc_subdir_lock); skip: filp->f_pos++; de = de->next; } while (de); + spin_unlock(&proc_subdir_lock); } ret = 1; out: unlock_kernel(); @@ -483,6 +515,7 @@ static struct file_operations proc_dir_operations = { */ static struct inode_operations proc_dir_inode_operations = { .lookup = proc_lookup, + .getattr = proc_getattr, .setattr = proc_notify_change, }; @@ -494,9 +527,13 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp if (i == 0) return -EAGAIN; dp->low_ino = i; + + spin_lock(&proc_subdir_lock); dp->next = dir->subdir; dp->parent = dir; dir->subdir = dp; + spin_unlock(&proc_subdir_lock); + if (S_ISDIR(dp->mode)) { if (dp->proc_iops == NULL) { dp->proc_fops = &proc_dir_operations; @@ -528,10 +565,10 @@ static void proc_kill_inodes(struct proc_dir_entry *de) */ file_list_lock(); list_for_each(p, &sb->s_files) { - struct file * filp = list_entry(p, struct file, f_list); - struct dentry * dentry = filp->f_dentry; + struct file * filp = list_entry(p, struct file, f_u.fu_list); + struct dentry * dentry = filp->f_path.dentry; struct inode * inode; - struct file_operations *fops; + const struct file_operations *fops; if (dentry->d_op != &proc_dentry_operations) continue; @@ -690,6 +727,8 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) if (!parent && xlate_proc_name(name, &parent, &fn) != 0) goto out; len = strlen(fn); + + spin_lock(&proc_subdir_lock); for (p = &parent->subdir; *p; p=&(*p)->next ) { if (!proc_match(len, fn, *p)) continue; @@ -710,6 +749,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) } break; } + spin_unlock(&proc_subdir_lock); out: return; }