X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fproc%2Fgeneric.c;h=c60d4538ffd3e85b53af54d23ed72ac3e85271e1;hb=refs%2Fheads%2Fvserver;hp=e77cf186e2083ecca38ccb70b4209efb306af70c;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/fs/proc/generic.c b/fs/proc/generic.c index e77cf186e..c60d4538f 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -18,10 +18,12 @@ #include #include #include -#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); @@ -29,6 +31,8 @@ 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) @@ -49,20 +53,32 @@ 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))) return -ENOMEM; while ((nbytes > 0) && !eof) { - count = min_t(ssize_t, PROC_BLOCK_SIZE, nbytes); + count = min_t(size_t, PROC_BLOCK_SIZE, nbytes); start = NULL; if (dp->get_info) { @@ -188,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); @@ -204,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) @@ -251,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, }; @@ -266,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, '/'); @@ -278,17 +295,21 @@ 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); -static spinlock_t proc_inum_lock = SPIN_LOCK_UNLOCKED; /* protects the above */ +static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */ #define PROC_DYNAMIC_FIRST 0xF0000000UL @@ -331,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 = { @@ -353,15 +374,8 @@ static int proc_delete_dentry(struct dentry * dentry) return 1; } -static int proc_revalidate_dentry(struct dentry *de, struct nameidata *nd) -{ - /* maybe add a check if it's really necessary? */ - return 0; -} - static struct dentry_operations proc_dentry_operations = { - .d_revalidate = proc_revalidate_dentry, .d_delete = proc_delete_dentry, }; @@ -376,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) { @@ -386,13 +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); - inode->i_xid = vx_current_xid(); + /* generic proc entries belong to the host */ + inode->i_tag = 0; + spin_lock(&proc_subdir_lock); break; } } } + spin_unlock(&proc_subdir_lock); unlock_kernel(); if (inode) { @@ -418,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(); @@ -439,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) @@ -462,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(); @@ -490,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, }; @@ -501,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; @@ -535,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; @@ -566,6 +596,11 @@ static struct proc_dir_entry *proc_create(struct proc_dir_entry **parent, if (!(*parent) && xlate_proc_name(name, parent, &fn) != 0) goto out; + + /* At this point there must not be any '/' characters beyond *fn */ + if (strchr(fn, '/')) + goto out; + len = strlen(fn); ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL); @@ -692,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; @@ -712,6 +749,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) } break; } + spin_unlock(&proc_subdir_lock); out: return; }