kernfs: allow creating kernfs objects with arbitrary uid/gid
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 20 Jul 2018 21:56:47 +0000 (21:56 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sat, 21 Jul 2018 06:44:35 +0000 (23:44 -0700)
This change allows creating kernfs files and directories with arbitrary
uid/gid instead of always using GLOBAL_ROOT_UID/GID by extending
kernfs_create_dir_ns() and kernfs_create_file_ns() with uid/gid arguments.
The "simple" kernfs_create_file() and kernfs_create_dir() are left alone
and always create objects belonging to the global root.

When creating symlinks ownership (uid/gid) is taken from the target kernfs
object.

Co-Developed-by: Tyler Hicks <tyhicks@canonical.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
arch/x86/kernel/cpu/intel_rdt_rdtgroup.c
fs/kernfs/dir.c
fs/kernfs/file.c
fs/kernfs/inode.c
fs/kernfs/kernfs-internal.h
fs/kernfs/symlink.c
fs/sysfs/dir.c
fs/sysfs/file.c
include/linux/kernfs.h
kernel/cgroup/cgroup.c

index 749856a2e736738feab416e3980054e5b5a699b3..9af1a21265d3b83995565fe3a2d2b4fe22a8a647 100644 (file)
@@ -146,6 +146,7 @@ static int rdtgroup_add_file(struct kernfs_node *parent_kn, struct rftype *rft)
        int ret;
 
        kn = __kernfs_create_file(parent_kn, rft->name, rft->mode,
+                                 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
                                  0, rft->kf_ops, rft, NULL, NULL);
        if (IS_ERR(kn))
                return PTR_ERR(kn);
@@ -1503,7 +1504,8 @@ static int mon_addfile(struct kernfs_node *parent_kn, const char *name,
        struct kernfs_node *kn;
        int ret = 0;
 
-       kn = __kernfs_create_file(parent_kn, name, 0444, 0,
+       kn = __kernfs_create_file(parent_kn, name, 0444,
+                                 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, 0,
                                  &kf_mondata_ops, priv, NULL, NULL);
        if (IS_ERR(kn))
                return PTR_ERR(kn);
index d66cc077730386e811679abcecbf7efebcdf11b4..4ca0b5c1819244970d0b6d20bbc0144d5f3d40be 100644 (file)
@@ -619,6 +619,7 @@ struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry)
 
 static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
                                             const char *name, umode_t mode,
+                                            kuid_t uid, kgid_t gid,
                                             unsigned flags)
 {
        struct kernfs_node *kn;
@@ -661,8 +662,22 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
        kn->mode = mode;
        kn->flags = flags;
 
+       if (!uid_eq(uid, GLOBAL_ROOT_UID) || !gid_eq(gid, GLOBAL_ROOT_GID)) {
+               struct iattr iattr = {
+                       .ia_valid = ATTR_UID | ATTR_GID,
+                       .ia_uid = uid,
+                       .ia_gid = gid,
+               };
+
+               ret = __kernfs_setattr(kn, &iattr);
+               if (ret < 0)
+                       goto err_out3;
+       }
+
        return kn;
 
+ err_out3:
+       idr_remove(&root->ino_idr, kn->id.ino);
  err_out2:
        kmem_cache_free(kernfs_node_cache, kn);
  err_out1:
@@ -672,11 +687,13 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
 
 struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
                                    const char *name, umode_t mode,
+                                   kuid_t uid, kgid_t gid,
                                    unsigned flags)
 {
        struct kernfs_node *kn;
 
-       kn = __kernfs_new_node(kernfs_root(parent), name, mode, flags);
+       kn = __kernfs_new_node(kernfs_root(parent),
+                              name, mode, uid, gid, flags);
        if (kn) {
                kernfs_get(parent);
                kn->parent = parent;
@@ -946,6 +963,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
        root->next_generation = 1;
 
        kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO,
+                              GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
                               KERNFS_DIR);
        if (!kn) {
                idr_destroy(&root->ino_idr);
@@ -984,6 +1002,8 @@ void kernfs_destroy_root(struct kernfs_root *root)
  * @parent: parent in which to create a new directory
  * @name: name of the new directory
  * @mode: mode of the new directory
+ * @uid: uid of the new directory
+ * @gid: gid of the new directory
  * @priv: opaque data associated with the new directory
  * @ns: optional namespace tag of the directory
  *
@@ -991,13 +1011,15 @@ void kernfs_destroy_root(struct kernfs_root *root)
  */
 struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
                                         const char *name, umode_t mode,
+                                        kuid_t uid, kgid_t gid,
                                         void *priv, const void *ns)
 {
        struct kernfs_node *kn;
        int rc;
 
        /* allocate */
-       kn = kernfs_new_node(parent, name, mode | S_IFDIR, KERNFS_DIR);
+       kn = kernfs_new_node(parent, name, mode | S_IFDIR,
+                            uid, gid, KERNFS_DIR);
        if (!kn)
                return ERR_PTR(-ENOMEM);
 
@@ -1028,7 +1050,8 @@ struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
        int rc;
 
        /* allocate */
-       kn = kernfs_new_node(parent, name, S_IRUGO|S_IXUGO|S_IFDIR, KERNFS_DIR);
+       kn = kernfs_new_node(parent, name, S_IRUGO|S_IXUGO|S_IFDIR,
+                            GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, KERNFS_DIR);
        if (!kn)
                return ERR_PTR(-ENOMEM);
 
index 2015d8c45e4a6db421505540be7b7fd055700cfd..dbf5bc250bfdf39076066707f381d84e2857b0b4 100644 (file)
@@ -965,6 +965,8 @@ const struct file_operations kernfs_file_fops = {
  * @parent: directory to create the file in
  * @name: name of the file
  * @mode: mode of the file
+ * @uid: uid of the file
+ * @gid: gid of the file
  * @size: size of the file
  * @ops: kernfs operations for the file
  * @priv: private data for the file
@@ -975,7 +977,8 @@ const struct file_operations kernfs_file_fops = {
  */
 struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
                                         const char *name,
-                                        umode_t mode, loff_t size,
+                                        umode_t mode, kuid_t uid, kgid_t gid,
+                                        loff_t size,
                                         const struct kernfs_ops *ops,
                                         void *priv, const void *ns,
                                         struct lock_class_key *key)
@@ -986,7 +989,8 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
 
        flags = KERNFS_FILE;
 
-       kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, flags);
+       kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG,
+                            uid, gid, flags);
        if (!kn)
                return ERR_PTR(-ENOMEM);
 
index 3d73fe9d56e235f51907eac11510aabae9fc3344..80cebcd94c9045add76d22037e05c466aa0829ff 100644 (file)
@@ -63,7 +63,7 @@ out_unlock:
        return ret;
 }
 
-static int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr)
+int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr)
 {
        struct kernfs_iattrs *attrs;
        struct iattr *iattrs;
index 0f260dcca1770427e7f292fe8ac1d2abc410e029..3d83b114bb08059bb52e9a1cf998e74523e08b1f 100644 (file)
@@ -90,6 +90,7 @@ int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr);
 int kernfs_iop_getattr(const struct path *path, struct kstat *stat,
                       u32 request_mask, unsigned int query_flags);
 ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size);
+int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr);
 
 /*
  * dir.c
@@ -104,6 +105,7 @@ void kernfs_put_active(struct kernfs_node *kn);
 int kernfs_add_one(struct kernfs_node *kn);
 struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
                                    const char *name, umode_t mode,
+                                   kuid_t uid, kgid_t gid,
                                    unsigned flags);
 struct kernfs_node *kernfs_find_and_get_node_by_ino(struct kernfs_root *root,
                                                    unsigned int ino);
index 08ccabd7047f390151b8ab31f72baa21efeefb6f..5ffed48f3d0e7cebb0f58cd7b9923ae75c8e7c92 100644 (file)
@@ -21,6 +21,7 @@
  * @target: target node for the symlink to point to
  *
  * Returns the created node on success, ERR_PTR() value on error.
+ * Ownership of the link matches ownership of the target.
  */
 struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
                                       const char *name,
@@ -28,8 +29,16 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
 {
        struct kernfs_node *kn;
        int error;
+       kuid_t uid = GLOBAL_ROOT_UID;
+       kgid_t gid = GLOBAL_ROOT_GID;
 
-       kn = kernfs_new_node(parent, name, S_IFLNK|S_IRWXUGO, KERNFS_LINK);
+       if (target->iattr) {
+               uid = target->iattr->ia_iattr.ia_uid;
+               gid = target->iattr->ia_iattr.ia_gid;
+       }
+
+       kn = kernfs_new_node(parent, name, S_IFLNK|S_IRWXUGO, uid, gid,
+                            KERNFS_LINK);
        if (!kn)
                return ERR_PTR(-ENOMEM);
 
index 58eba92a0e41fed0d5da68345be1c452815afb09..e39b884f0867d32eb4ffb322121d59ba3df0981d 100644 (file)
@@ -52,7 +52,9 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
                return -ENOENT;
 
        kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
-                                 S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns);
+                                 S_IRWXU | S_IRUGO | S_IXUGO,
+                                 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+                                 kobj, ns);
        if (IS_ERR(kn)) {
                if (PTR_ERR(kn) == -EEXIST)
                        sysfs_warn_dup(parent, kobject_name(kobj));
index 5c13f29bfcdb72024bdee5828a453d73e6dd2b5a..513fa691ecbdffd94677da85055798d4e4eac9f1 100644 (file)
@@ -302,8 +302,9 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent,
        if (!attr->ignore_lockdep)
                key = attr->key ?: (struct lock_class_key *)&attr->skey;
 #endif
-       kn = __kernfs_create_file(parent, attr->name, mode & 0777, size, ops,
-                                 (void *)attr, ns, key);
+       kn = __kernfs_create_file(parent, attr->name,
+                                 mode & 0777, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+                                 size, ops, (void *)attr, ns, key);
        if (IS_ERR(kn)) {
                if (PTR_ERR(kn) == -EEXIST)
                        sysfs_warn_dup(parent, attr->name);
index ab25c8b6d9e33d84bbaca9410af75339fa94385b..814643f7ee529eb6c9e27ec3e1653487d818595e 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/lockdep.h>
 #include <linux/rbtree.h>
 #include <linux/atomic.h>
+#include <linux/uidgid.h>
 #include <linux/wait.h>
 
 struct file;
@@ -325,12 +326,14 @@ void kernfs_destroy_root(struct kernfs_root *root);
 
 struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
                                         const char *name, umode_t mode,
+                                        kuid_t uid, kgid_t gid,
                                         void *priv, const void *ns);
 struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
                                            const char *name);
 struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
-                                        const char *name,
-                                        umode_t mode, loff_t size,
+                                        const char *name, umode_t mode,
+                                        kuid_t uid, kgid_t gid,
+                                        loff_t size,
                                         const struct kernfs_ops *ops,
                                         void *priv, const void *ns,
                                         struct lock_class_key *key);
@@ -415,12 +418,14 @@ static inline void kernfs_destroy_root(struct kernfs_root *root) { }
 
 static inline struct kernfs_node *
 kernfs_create_dir_ns(struct kernfs_node *parent, const char *name,
-                    umode_t mode, void *priv, const void *ns)
+                    umode_t mode, kuid_t uid, kgid_t gid,
+                    void *priv, const void *ns)
 { return ERR_PTR(-ENOSYS); }
 
 static inline struct kernfs_node *
 __kernfs_create_file(struct kernfs_node *parent, const char *name,
-                    umode_t mode, loff_t size, const struct kernfs_ops *ops,
+                    umode_t mode, kuid_t uid, kgid_t gid,
+                    loff_t size, const struct kernfs_ops *ops,
                     void *priv, const void *ns, struct lock_class_key *key)
 { return ERR_PTR(-ENOSYS); }
 
@@ -498,12 +503,15 @@ static inline struct kernfs_node *
 kernfs_create_dir(struct kernfs_node *parent, const char *name, umode_t mode,
                  void *priv)
 {
-       return kernfs_create_dir_ns(parent, name, mode, priv, NULL);
+       return kernfs_create_dir_ns(parent, name, mode,
+                                   GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+                                   priv, NULL);
 }
 
 static inline struct kernfs_node *
 kernfs_create_file_ns(struct kernfs_node *parent, const char *name,
-                     umode_t mode, loff_t size, const struct kernfs_ops *ops,
+                     umode_t mode, kuid_t uid, kgid_t gid,
+                     loff_t size, const struct kernfs_ops *ops,
                      void *priv, const void *ns)
 {
        struct lock_class_key *key = NULL;
@@ -511,15 +519,17 @@ kernfs_create_file_ns(struct kernfs_node *parent, const char *name,
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
        key = (struct lock_class_key *)&ops->lockdep_key;
 #endif
-       return __kernfs_create_file(parent, name, mode, size, ops, priv, ns,
-                                   key);
+       return __kernfs_create_file(parent, name, mode, uid, gid,
+                                   size, ops, priv, ns, key);
 }
 
 static inline struct kernfs_node *
 kernfs_create_file(struct kernfs_node *parent, const char *name, umode_t mode,
                   loff_t size, const struct kernfs_ops *ops, void *priv)
 {
-       return kernfs_create_file_ns(parent, name, mode, size, ops, priv, NULL);
+       return kernfs_create_file_ns(parent, name, mode,
+                                    GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+                                    size, ops, priv, NULL);
 }
 
 static inline int kernfs_remove_by_name(struct kernfs_node *parent,
index 077370bf89643db6967b9c7571271eacd6ddcc08..35cf3d71f8aaf4b7efcdc241953abe2108b58f6a 100644 (file)
@@ -3557,7 +3557,9 @@ static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
        key = &cft->lockdep_key;
 #endif
        kn = __kernfs_create_file(cgrp->kn, cgroup_file_name(cgrp, cft, name),
-                                 cgroup_file_mode(cft), 0, cft->kf_ops, cft,
+                                 cgroup_file_mode(cft),
+                                 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+                                 0, cft->kf_ops, cft,
                                  NULL, key);
        if (IS_ERR(kn))
                return PTR_ERR(kn);