| 1 | |
| 2 | |
| 3 | * fs/sysfs/dir.c - sysfs core and dir operation implementation
|
| 4 | *
|
| 5 | * Copyright (c) 2001-3 Patrick Mochel
|
| 6 | * Copyright (c) 2007 SUSE Linux Products GmbH
|
| 7 | * Copyright (c) 2007 Tejun Heo <[email protected]>
|
| 8 | *
|
| 9 | * Please see Documentation/filesystems/sysfs.rst for more information.
|
| 10 |
|
| 11 |
|
| 12 | #define pr_fmt(fmt) "sysfs: " fmt
|
| 13 |
|
| 14 | #include <linux/fs.h>
|
| 15 | #include <linux/kobject.h>
|
| 16 | #include <linux/slab.h>
|
| 17 | #include "sysfs.h"
|
| 18 |
|
| 19 | DEFINE_SPINLOCK(sysfs_symlink_target_lock);
|
| 20 |
|
| 21 | void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
|
| 22 | {
|
| 23 | char *buf;
|
| 24 |
|
| 25 | buf = kzalloc(PATH_MAX, GFP_KERNEL);
|
| 26 | if (buf)
|
| 27 | kernfs_path(parent, buf, PATH_MAX);
|
| 28 |
|
| 29 | pr_warn("cannot create duplicate filename '%s/%s'\n", buf, name);
|
| 30 | dump_stack();
|
| 31 |
|
| 32 | kfree(buf);
|
| 33 | }
|
| 34 |
|
| 35 | |
| 36 | * sysfs_create_dir_ns - create a directory for an object with a namespace tag
|
| 37 | * @kobj: object we're creating directory for
|
| 38 | * @ns: the namespace tag to use
|
| 39 |
|
| 40 | int sysfs_create_dir_ns(struct kobject *kobj, const struct ns_common *ns)
|
| 41 | {
|
| 42 | struct kernfs_node *parent, *kn;
|
| 43 | kuid_t uid;
|
| 44 | kgid_t gid;
|
| 45 |
|
| 46 | if (WARN_ON(!kobj))
|
| 47 | return -EINVAL;
|
| 48 |
|
| 49 | if (kobj->parent)
|
| 50 | parent = kobj->parent->sd;
|
| 51 | else
|
| 52 | parent = sysfs_root_kn;
|
| 53 |
|
| 54 | if (!parent)
|
| 55 | return -ENOENT;
|
| 56 |
|
| 57 | kobject_get_ownership(kobj, &uid, &gid);
|
| 58 |
|
| 59 | kn = kernfs_create_dir_ns(parent, kobject_name(kobj), 0755, uid, gid,
|
| 60 | kobj, ns);
|
| 61 | if (IS_ERR(kn)) {
|
| 62 | if (PTR_ERR(kn) == -EEXIST)
|
| 63 | sysfs_warn_dup(parent, kobject_name(kobj));
|
| 64 | return PTR_ERR(kn);
|
| 65 | }
|
| 66 |
|
| 67 | kobj->sd = kn;
|
| 68 | return 0;
|
| 69 | }
|
| 70 |
|
| 71 | |
| 72 | * sysfs_remove_dir - remove an object's directory.
|
| 73 | * @kobj: object.
|
| 74 | *
|
| 75 | * The only thing special about this is that we remove any files in
|
| 76 | * the directory before we remove the directory, and we've inlined
|
| 77 | * what used to be sysfs_rmdir() below, instead of calling separately.
|
| 78 |
|
| 79 | void sysfs_remove_dir(struct kobject *kobj)
|
| 80 | {
|
| 81 | struct kernfs_node *kn = kobj->sd;
|
| 82 |
|
| 83 | |
| 84 | * In general, kobject owner is responsible for ensuring removal
|
| 85 | * doesn't race with other operations and sysfs doesn't provide any
|
| 86 | * protection; however, when @kobj is used as a symlink target, the
|
| 87 | * symlinking entity usually doesn't own @kobj and thus has no
|
| 88 | * control over removal. @kobj->sd may be removed anytime
|
| 89 | * and symlink code may end up dereferencing an already freed node.
|
| 90 | *
|
| 91 | * sysfs_symlink_target_lock synchronizes @kobj->sd
|
| 92 | * disassociation against symlink operations so that symlink code
|
| 93 | * can safely dereference @kobj->sd.
|
| 94 |
|
| 95 | spin_lock(&sysfs_symlink_target_lock);
|
| 96 | kobj->sd = NULL;
|
| 97 | spin_unlock(&sysfs_symlink_target_lock);
|
| 98 |
|
| 99 | if (kn) {
|
| 100 | WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR);
|
| 101 | kernfs_remove(kn);
|
| 102 | }
|
| 103 | }
|
| 104 |
|
| 105 | int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
|
| 106 | const struct ns_common *new_ns)
|
| 107 | {
|
| 108 | struct kernfs_node *parent;
|
| 109 | int ret;
|
| 110 |
|
| 111 | parent = kernfs_get_parent(kobj->sd);
|
| 112 | ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
|
| 113 | kernfs_put(parent);
|
| 114 | return ret;
|
| 115 | }
|
| 116 |
|
| 117 | int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
|
| 118 | const struct ns_common *new_ns)
|
| 119 | {
|
| 120 | struct kernfs_node *kn = kobj->sd;
|
| 121 | struct kernfs_node *new_parent;
|
| 122 |
|
| 123 | new_parent = new_parent_kobj && new_parent_kobj->sd ?
|
| 124 | new_parent_kobj->sd : sysfs_root_kn;
|
| 125 |
|
| 126 | return kernfs_rename_ns(kn, new_parent, NULL, new_ns);
|
| 127 | }
|
| 128 |
|
| 129 | |
| 130 | * sysfs_create_mount_point - create an always empty directory
|
| 131 | * @parent_kobj: kobject that will contain this always empty directory
|
| 132 | * @name: The name of the always empty directory to add
|
| 133 |
|
| 134 | int sysfs_create_mount_point(struct kobject *parent_kobj, const char *name)
|
| 135 | {
|
| 136 | struct kernfs_node *kn, *parent = parent_kobj->sd;
|
| 137 |
|
| 138 | kn = kernfs_create_empty_dir(parent, name);
|
| 139 | if (IS_ERR(kn)) {
|
| 140 | if (PTR_ERR(kn) == -EEXIST)
|
| 141 | sysfs_warn_dup(parent, name);
|
| 142 | return PTR_ERR(kn);
|
| 143 | }
|
| 144 |
|
| 145 | return 0;
|
| 146 | }
|
| 147 | EXPORT_SYMBOL_GPL(sysfs_create_mount_point);
|
| 148 |
|
| 149 | |
| 150 | * sysfs_remove_mount_point - remove an always empty directory.
|
| 151 | * @parent_kobj: kobject that will contain this always empty directory
|
| 152 | * @name: The name of the always empty directory to remove
|
| 153 | *
|
| 154 |
|
| 155 | void sysfs_remove_mount_point(struct kobject *parent_kobj, const char *name)
|
| 156 | {
|
| 157 | struct kernfs_node *parent = parent_kobj->sd;
|
| 158 |
|
| 159 | kernfs_remove_by_name_ns(parent, name, NULL);
|
| 160 | }
|
| 161 | EXPORT_SYMBOL_GPL(sysfs_remove_mount_point);
|