1
0
Fork 0

kernfs: allow creating kernfs objects with arbitrary uid/gid

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>
hifive-unleashed-5.1
Dmitry Torokhov 2018-07-20 21:56:47 +00:00 committed by David S. Miller
parent 7c4ec749a3
commit 488dee96bb
10 changed files with 76 additions and 21 deletions

View File

@ -146,6 +146,7 @@ static int rdtgroup_add_file(struct kernfs_node *parent_kn, struct rftype *rft)
int ret; int ret;
kn = __kernfs_create_file(parent_kn, rft->name, rft->mode, kn = __kernfs_create_file(parent_kn, rft->name, rft->mode,
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
0, rft->kf_ops, rft, NULL, NULL); 0, rft->kf_ops, rft, NULL, NULL);
if (IS_ERR(kn)) if (IS_ERR(kn))
return PTR_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; struct kernfs_node *kn;
int ret = 0; 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); &kf_mondata_ops, priv, NULL, NULL);
if (IS_ERR(kn)) if (IS_ERR(kn))
return PTR_ERR(kn); return PTR_ERR(kn);

View 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, static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
const char *name, umode_t mode, const char *name, umode_t mode,
kuid_t uid, kgid_t gid,
unsigned flags) unsigned flags)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
@ -661,8 +662,22 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
kn->mode = mode; kn->mode = mode;
kn->flags = flags; 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; return kn;
err_out3:
idr_remove(&root->ino_idr, kn->id.ino);
err_out2: err_out2:
kmem_cache_free(kernfs_node_cache, kn); kmem_cache_free(kernfs_node_cache, kn);
err_out1: 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, struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
const char *name, umode_t mode, const char *name, umode_t mode,
kuid_t uid, kgid_t gid,
unsigned flags) unsigned flags)
{ {
struct kernfs_node *kn; 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) { if (kn) {
kernfs_get(parent); kernfs_get(parent);
kn->parent = parent; kn->parent = parent;
@ -946,6 +963,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
root->next_generation = 1; root->next_generation = 1;
kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO, kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO,
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
KERNFS_DIR); KERNFS_DIR);
if (!kn) { if (!kn) {
idr_destroy(&root->ino_idr); 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 * @parent: parent in which to create a new directory
* @name: name of the new directory * @name: name of the new directory
* @mode: mode 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 * @priv: opaque data associated with the new directory
* @ns: optional namespace tag of the 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, struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
const char *name, umode_t mode, const char *name, umode_t mode,
kuid_t uid, kgid_t gid,
void *priv, const void *ns) void *priv, const void *ns)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
int rc; int rc;
/* allocate */ /* 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) if (!kn)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -1028,7 +1050,8 @@ struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
int rc; int rc;
/* allocate */ /* 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) if (!kn)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);

View File

@ -965,6 +965,8 @@ const struct file_operations kernfs_file_fops = {
* @parent: directory to create the file in * @parent: directory to create the file in
* @name: name of the file * @name: name of the file
* @mode: mode of the file * @mode: mode of the file
* @uid: uid of the file
* @gid: gid of the file
* @size: size of the file * @size: size of the file
* @ops: kernfs operations for the file * @ops: kernfs operations for the file
* @priv: private data 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, struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
const char *name, 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, const struct kernfs_ops *ops,
void *priv, const void *ns, void *priv, const void *ns,
struct lock_class_key *key) struct lock_class_key *key)
@ -986,7 +989,8 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
flags = KERNFS_FILE; 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) if (!kn)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);

View File

@ -63,7 +63,7 @@ out_unlock:
return ret; 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 kernfs_iattrs *attrs;
struct iattr *iattrs; struct iattr *iattrs;

View 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, int kernfs_iop_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int query_flags); u32 request_mask, unsigned int query_flags);
ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size); 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 * dir.c
@ -104,6 +105,7 @@ void kernfs_put_active(struct kernfs_node *kn);
int kernfs_add_one(struct kernfs_node *kn); int kernfs_add_one(struct kernfs_node *kn);
struct kernfs_node *kernfs_new_node(struct kernfs_node *parent, struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
const char *name, umode_t mode, const char *name, umode_t mode,
kuid_t uid, kgid_t gid,
unsigned flags); unsigned flags);
struct kernfs_node *kernfs_find_and_get_node_by_ino(struct kernfs_root *root, struct kernfs_node *kernfs_find_and_get_node_by_ino(struct kernfs_root *root,
unsigned int ino); unsigned int ino);

View File

@ -21,6 +21,7 @@
* @target: target node for the symlink to point to * @target: target node for the symlink to point to
* *
* Returns the created node on success, ERR_PTR() value on error. * 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, struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
const char *name, const char *name,
@ -28,8 +29,16 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
int error; 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) if (!kn)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);

View File

@ -52,7 +52,9 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
return -ENOENT; return -ENOENT;
kn = kernfs_create_dir_ns(parent, kobject_name(kobj), 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 (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST) if (PTR_ERR(kn) == -EEXIST)
sysfs_warn_dup(parent, kobject_name(kobj)); sysfs_warn_dup(parent, kobject_name(kobj));

View File

@ -302,8 +302,9 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent,
if (!attr->ignore_lockdep) if (!attr->ignore_lockdep)
key = attr->key ?: (struct lock_class_key *)&attr->skey; key = attr->key ?: (struct lock_class_key *)&attr->skey;
#endif #endif
kn = __kernfs_create_file(parent, attr->name, mode & 0777, size, ops, kn = __kernfs_create_file(parent, attr->name,
(void *)attr, ns, key); mode & 0777, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
size, ops, (void *)attr, ns, key);
if (IS_ERR(kn)) { if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST) if (PTR_ERR(kn) == -EEXIST)
sysfs_warn_dup(parent, attr->name); sysfs_warn_dup(parent, attr->name);

View File

@ -15,6 +15,7 @@
#include <linux/lockdep.h> #include <linux/lockdep.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/uidgid.h>
#include <linux/wait.h> #include <linux/wait.h>
struct file; 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, struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
const char *name, umode_t mode, const char *name, umode_t mode,
kuid_t uid, kgid_t gid,
void *priv, const void *ns); void *priv, const void *ns);
struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent, struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
const char *name); const char *name);
struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
const char *name, const char *name, umode_t mode,
umode_t mode, loff_t size, kuid_t uid, kgid_t gid,
loff_t size,
const struct kernfs_ops *ops, const struct kernfs_ops *ops,
void *priv, const void *ns, void *priv, const void *ns,
struct lock_class_key *key); struct lock_class_key *key);
@ -415,12 +418,14 @@ static inline void kernfs_destroy_root(struct kernfs_root *root) { }
static inline struct kernfs_node * static inline struct kernfs_node *
kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, 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); } { return ERR_PTR(-ENOSYS); }
static inline struct kernfs_node * static inline struct kernfs_node *
__kernfs_create_file(struct kernfs_node *parent, const char *name, __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) void *priv, const void *ns, struct lock_class_key *key)
{ return ERR_PTR(-ENOSYS); } { 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, kernfs_create_dir(struct kernfs_node *parent, const char *name, umode_t mode,
void *priv) 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 * static inline struct kernfs_node *
kernfs_create_file_ns(struct kernfs_node *parent, const char *name, 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) void *priv, const void *ns)
{ {
struct lock_class_key *key = NULL; 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 #ifdef CONFIG_DEBUG_LOCK_ALLOC
key = (struct lock_class_key *)&ops->lockdep_key; key = (struct lock_class_key *)&ops->lockdep_key;
#endif #endif
return __kernfs_create_file(parent, name, mode, size, ops, priv, ns, return __kernfs_create_file(parent, name, mode, uid, gid,
key); size, ops, priv, ns, key);
} }
static inline struct kernfs_node * static inline struct kernfs_node *
kernfs_create_file(struct kernfs_node *parent, const char *name, umode_t mode, kernfs_create_file(struct kernfs_node *parent, const char *name, umode_t mode,
loff_t size, const struct kernfs_ops *ops, void *priv) 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, static inline int kernfs_remove_by_name(struct kernfs_node *parent,

View File

@ -3557,7 +3557,9 @@ static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
key = &cft->lockdep_key; key = &cft->lockdep_key;
#endif #endif
kn = __kernfs_create_file(cgrp->kn, cgroup_file_name(cgrp, cft, name), 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); NULL, key);
if (IS_ERR(kn)) if (IS_ERR(kn))
return PTR_ERR(kn); return PTR_ERR(kn);