X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fproc%2Fgeneric.c;h=153bc26a02395846cc97c42888bbbf23ccbe35d8;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=d2c88ebb1b374f94178177b3de0ca2135d58be17;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/proc/generic.c b/fs/proc/generic.c index d2c88ebb1..153bc26a0 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -17,8 +17,13 @@ #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); @@ -26,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) @@ -53,13 +60,25 @@ proc_file_read(struct file *file, char __user *buf, size_t nbytes, 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) { @@ -201,44 +220,50 @@ 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(); + 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) +{ + struct inode *inode = dentry->d_inode; + struct proc_dir_entry *de = PDE(inode); + int error; - 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; - } + error = inode_change_ok(inode, iattr); + if (error) + goto out; + error = inode_setattr(inode, iattr); + if (error) + goto out; + + de->uid = inode->i_uid; + de->gid = inode->i_gid; + de->mode = inode->i_mode; out: - unlock_kernel(); - return -EINVAL; + return error; } -static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) +static int proc_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) { struct inode *inode = dentry->d_inode; - int error = inode_setattr(inode, iattr); - if (!error) { - struct proc_dir_entry *de = PDE(inode); - de->uid = inode->i_uid; - de->gid = inode->i_gid; - de->mode = inode->i_mode; - } + struct proc_dir_entry *de = PROC_I(inode)->pde; + if (de && de->nlink) + inode->i_nlink = de->nlink; - return error; + generic_fillattr(inode, stat); + return 0; } static struct inode_operations proc_file_inode_operations = { @@ -256,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, '/'); @@ -268,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 @@ -288,18 +319,20 @@ static spinlock_t proc_inum_lock = SPIN_LOCK_UNLOCKED; /* protects the above */ */ static unsigned int get_inode_number(void) { - unsigned int i, inum = 0; + int i, inum = 0; + int error; retry: if (idr_pre_get(&proc_inum_idr, GFP_KERNEL) == 0) return 0; spin_lock(&proc_inum_lock); - i = idr_get_new(&proc_inum_idr, NULL); + error = idr_get_new(&proc_inum_idr, NULL, &i); spin_unlock(&proc_inum_lock); - - if (i == -1) + if (error == -EAGAIN) goto retry; + else if (error) + return 0; inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST; @@ -319,21 +352,14 @@ static void release_inode_number(unsigned int inum) spin_unlock(&proc_inum_lock); } -static int -proc_readlink(struct dentry *dentry, char __user *buffer, int buflen) -{ - char *s = PDE(dentry->d_inode)->data; - return vfs_readlink(dentry, buffer, buflen, s); -} - -static int proc_follow_link(struct dentry *dentry, struct nameidata *nd) +static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) { - char *s = PDE(dentry->d_inode)->data; - return vfs_follow_link(nd, s); + nd_set_link(nd, PDE(dentry->d_inode)->data); + return NULL; } static struct inode_operations proc_link_inode_operations = { - .readlink = proc_readlink, + .readlink = generic_readlink, .follow_link = proc_follow_link, }; @@ -364,20 +390,28 @@ 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) { if (de->namelen != dentry->d_name.len) continue; + if (!vx_hide_check(0, de->vx_flags)) + continue; 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; + spin_lock(&proc_subdir_lock); break; } } } + spin_unlock(&proc_subdir_lock); unlock_kernel(); if (inode) { @@ -431,11 +465,13 @@ int proc_readdir(struct file * filp, 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) @@ -445,12 +481,19 @@ 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(); @@ -472,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, }; @@ -483,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; @@ -517,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 file * filp = list_entry(p, struct file, f_u.fu_list); struct dentry * dentry = filp->f_dentry; struct inode * inode; - struct file_operations *fops; + const struct file_operations *fops; if (dentry->d_op != &proc_dentry_operations) continue; @@ -548,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); @@ -559,6 +612,7 @@ static struct proc_dir_entry *proc_create(struct proc_dir_entry **parent, ent->namelen = len; ent->mode = mode; ent->nlink = nlink; + ent->vx_flags = IATTR_PROC_DEFAULT; out: return ent; } @@ -579,7 +633,8 @@ struct proc_dir_entry *proc_symlink(const char *name, kfree(ent->data); kfree(ent); ent = NULL; - } + } else + ent->vx_flags = IATTR_PROC_SYMLINK; } else { kfree(ent); ent = NULL; @@ -672,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; @@ -692,6 +749,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) } break; } + spin_unlock(&proc_subdir_lock); out: return; }