Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * fs/sysfs/dir.c - sysfs core and dir operation implementation
0004  *
0005  * Copyright (c) 2001-3 Patrick Mochel
0006  * Copyright (c) 2007 SUSE Linux Products GmbH
0007  * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
0008  *
0009  * Please see Documentation/filesystems/sysfs.rst for more information.
0010  */
0011 
0012 #define pr_fmt(fmt) "sysfs: " fmt
0013 
0014 #include <linux/fs.h>
0015 #include <linux/kobject.h>
0016 #include <linux/slab.h>
0017 #include "sysfs.h"
0018 
0019 DEFINE_SPINLOCK(sysfs_symlink_target_lock);
0020 
0021 void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
0022 {
0023     char *buf;
0024 
0025     buf = kzalloc(PATH_MAX, GFP_KERNEL);
0026     if (buf)
0027         kernfs_path(parent, buf, PATH_MAX);
0028 
0029     pr_warn("cannot create duplicate filename '%s/%s'\n", buf, name);
0030     dump_stack();
0031 
0032     kfree(buf);
0033 }
0034 
0035 /**
0036  * sysfs_create_dir_ns - create a directory for an object with a namespace tag
0037  * @kobj: object we're creating directory for
0038  * @ns: the namespace tag to use
0039  */
0040 int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
0041 {
0042     struct kernfs_node *parent, *kn;
0043     kuid_t uid;
0044     kgid_t gid;
0045 
0046     if (WARN_ON(!kobj))
0047         return -EINVAL;
0048 
0049     if (kobj->parent)
0050         parent = kobj->parent->sd;
0051     else
0052         parent = sysfs_root_kn;
0053 
0054     if (!parent)
0055         return -ENOENT;
0056 
0057     kobject_get_ownership(kobj, &uid, &gid);
0058 
0059     kn = kernfs_create_dir_ns(parent, kobject_name(kobj), 0755, uid, gid,
0060                   kobj, ns);
0061     if (IS_ERR(kn)) {
0062         if (PTR_ERR(kn) == -EEXIST)
0063             sysfs_warn_dup(parent, kobject_name(kobj));
0064         return PTR_ERR(kn);
0065     }
0066 
0067     kobj->sd = kn;
0068     return 0;
0069 }
0070 
0071 /**
0072  *  sysfs_remove_dir - remove an object's directory.
0073  *  @kobj:  object.
0074  *
0075  *  The only thing special about this is that we remove any files in
0076  *  the directory before we remove the directory, and we've inlined
0077  *  what used to be sysfs_rmdir() below, instead of calling separately.
0078  */
0079 void sysfs_remove_dir(struct kobject *kobj)
0080 {
0081     struct kernfs_node *kn = kobj->sd;
0082 
0083     /*
0084      * In general, kboject owner is responsible for ensuring removal
0085      * doesn't race with other operations and sysfs doesn't provide any
0086      * protection; however, when @kobj is used as a symlink target, the
0087      * symlinking entity usually doesn't own @kobj and thus has no
0088      * control over removal.  @kobj->sd may be removed anytime
0089      * and symlink code may end up dereferencing an already freed node.
0090      *
0091      * sysfs_symlink_target_lock synchronizes @kobj->sd
0092      * disassociation against symlink operations so that symlink code
0093      * can safely dereference @kobj->sd.
0094      */
0095     spin_lock(&sysfs_symlink_target_lock);
0096     kobj->sd = NULL;
0097     spin_unlock(&sysfs_symlink_target_lock);
0098 
0099     if (kn) {
0100         WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR);
0101         kernfs_remove(kn);
0102     }
0103 }
0104 
0105 int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
0106             const void *new_ns)
0107 {
0108     struct kernfs_node *parent;
0109     int ret;
0110 
0111     parent = kernfs_get_parent(kobj->sd);
0112     ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
0113     kernfs_put(parent);
0114     return ret;
0115 }
0116 
0117 int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
0118               const void *new_ns)
0119 {
0120     struct kernfs_node *kn = kobj->sd;
0121     struct kernfs_node *new_parent;
0122 
0123     new_parent = new_parent_kobj && new_parent_kobj->sd ?
0124         new_parent_kobj->sd : sysfs_root_kn;
0125 
0126     return kernfs_rename_ns(kn, new_parent, kn->name, new_ns);
0127 }
0128 
0129 /**
0130  * sysfs_create_mount_point - create an always empty directory
0131  * @parent_kobj:  kobject that will contain this always empty directory
0132  * @name: The name of the always empty directory to add
0133  */
0134 int sysfs_create_mount_point(struct kobject *parent_kobj, const char *name)
0135 {
0136     struct kernfs_node *kn, *parent = parent_kobj->sd;
0137 
0138     kn = kernfs_create_empty_dir(parent, name);
0139     if (IS_ERR(kn)) {
0140         if (PTR_ERR(kn) == -EEXIST)
0141             sysfs_warn_dup(parent, name);
0142         return PTR_ERR(kn);
0143     }
0144 
0145     return 0;
0146 }
0147 EXPORT_SYMBOL_GPL(sysfs_create_mount_point);
0148 
0149 /**
0150  *  sysfs_remove_mount_point - remove an always empty directory.
0151  *  @parent_kobj: kobject that will contain this always empty directory
0152  *  @name: The name of the always empty directory to remove
0153  *
0154  */
0155 void sysfs_remove_mount_point(struct kobject *parent_kobj, const char *name)
0156 {
0157     struct kernfs_node *parent = parent_kobj->sd;
0158 
0159     kernfs_remove_by_name_ns(parent, name, NULL);
0160 }
0161 EXPORT_SYMBOL_GPL(sysfs_remove_mount_point);