X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fproc%2Fgeneric.c;h=38c85408176c6dbcf32eff87d9b5cc961369e12a;hb=987b0145d94eecf292d8b301228356f44611ab7c;hp=f42a81260a4242df3a6d8820f68be5389e099297;hpb=887a12fc42875cae0e9bf095b812aa8e6992e1f3;p=linux-2.6.git diff --git a/fs/proc/generic.c b/fs/proc/generic.c index f42a81260..38c854081 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -18,12 +18,12 @@ #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); static ssize_t proc_file_write(struct file *file, const char __user *buffer, @@ -57,13 +57,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) { @@ -205,30 +217,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) @@ -252,6 +251,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, }; @@ -289,7 +300,7 @@ static int xlate_proc_name(const char *name, } 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 @@ -332,10 +343,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 = { @@ -354,15 +365,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, }; @@ -492,6 +496,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, }; @@ -537,7 +542,7 @@ 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; @@ -568,6 +573,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); @@ -704,7 +714,7 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) parent->nlink--; proc_kill_inodes(de); de->nlink = 0; - BUG_ON(de->subdir); + WARN_ON(de->subdir); if (!atomic_read(&de->count)) free_proc_entry(de); else {