]> git.neil.brown.name Git - history.git/commitdiff
sysfs: do permission checking on open.
authorPatrick Mochel <mochel@osdl.org>
Tue, 19 Nov 2002 07:33:36 +0000 (01:33 -0600)
committerPatrick Mochel <mochel@osdl.org>
Tue, 19 Nov 2002 07:33:36 +0000 (01:33 -0600)
sysfs has always had a bug that would allow a read-only file to be opened
for writing. It has also returned 0 on write when there was no store method
defined for the file.

This addresses both via sysfs_open_file(). It checks the flags the file was
opened with and compares them with the mode of the inode. If the mode does
not support the flags passed, -EPERM is returned.

If the sysfs_ops for the object does not have the correct method for the
flags, -EACCESS is returned.

Since all checks happen on open(), the corresponding checks in the read()
and write() methods have been removed.

fs/sysfs/inode.c

index 8f35216643fefc4689e3ded9ca86951264834191..209d527b6a8c542612530fc182e9a6a8beb165c9 100644 (file)
@@ -175,17 +175,11 @@ static ssize_t
 sysfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos)
 {
        struct attribute * attr = file->f_dentry->d_fsdata;
-       struct sysfs_ops * ops = NULL;
-       struct kobject * kobj;
+       struct sysfs_ops * ops = file->private_data;
+       struct kobject * kobj = file->f_dentry->d_parent->d_fsdata;
        unsigned char *page;
        ssize_t retval = 0;
 
-       kobj = file->f_dentry->d_parent->d_fsdata;
-       if (kobj && kobj->subsys)
-               ops = kobj->subsys->sysfs_ops;
-       if (!ops || !ops->show)
-               return 0;
-
        if (count > PAGE_SIZE)
                count = PAGE_SIZE;
 
@@ -236,17 +230,11 @@ static ssize_t
 sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
        struct attribute * attr = file->f_dentry->d_fsdata;
-       struct sysfs_ops * ops = NULL;
-       struct kobject * kobj;
+       struct sysfs_ops * ops = file->private_data;
+       struct kobject * kobj = file->f_dentry->d_parent->d_fsdata;
        ssize_t retval = 0;
        char * page;
 
-       kobj = file->f_dentry->d_parent->d_fsdata;
-       if (kobj && kobj->subsys)
-               ops = kobj->subsys->sysfs_ops;
-       if (!ops || !ops->store)
-               return 0;
-
        page = (char *)__get_free_page(GFP_KERNEL);
        if (!page)
                return -ENOMEM;
@@ -277,21 +265,72 @@ sysfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
        return retval;
 }
 
-static int sysfs_open_file(struct inode * inode, struct file * filp)
+static int check_perm(struct inode * inode, struct file * file)
 {
-       struct kobject * kobj;
+       struct kobject * kobj = kobject_get(file->f_dentry->d_parent->d_fsdata);
+       struct attribute * attr = file->f_dentry->d_fsdata;
+       struct sysfs_ops * ops = NULL;
        int error = 0;
 
-       kobj = filp->f_dentry->d_parent->d_fsdata;
-       if ((kobj = kobject_get(kobj))) {
-               struct attribute * attr = filp->f_dentry->d_fsdata;
-               if (!attr)
-                       error = -EINVAL;
-       } else
-               error = -EINVAL;
+       if (!kobj || !attr)
+               goto Einval;
+       
+       if (kobj->subsys)
+               ops = kobj->subsys->sysfs_ops;
+
+       /* No sysfs operations, either from having no subsystem,
+        * or the subsystem have no operations.
+        */
+       if (!ops)
+               goto Eaccess;
+
+       /* File needs write support.
+        * The inode's perms must say it's ok, 
+        * and we must have a store method.
+        */
+       if (file->f_mode & FMODE_WRITE) {
+
+               if (!(inode->i_mode & S_IWUGO))
+                       goto Eperm;
+               if (!ops->store)
+                       goto Eaccess;
+
+       }
+
+       /* File needs read support.
+        * The inode's perms must say it's ok, and we there
+        * must be a show method for it.
+        */
+       if (file->f_mode & FMODE_READ) {
+               if (!(inode->i_mode & S_IRUGO))
+                       goto Eperm;
+               if (!ops->show)
+                       goto Eaccess;
+       }
+
+       /* No error? Great, store the ops in file->private_data
+        * for easy access in the read/write functions.
+        */
+       file->private_data = ops;
+       goto Done;
+
+ Einval:
+       error = -EINVAL;
+       goto Done;
+ Eaccess:
+       error = -EACCES;
+       goto Done;
+ Eperm:
+       error = -EPERM;
+ Done:
        return error;
 }
 
+static int sysfs_open_file(struct inode * inode, struct file * filp)
+{
+       return check_perm(inode,filp);
+}
+
 static int sysfs_release(struct inode * inode, struct file * filp)
 {
        struct kobject * kobj = filp->f_dentry->d_parent->d_fsdata;