]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - fs/sysfs/symlink.c
UBUNTU: Ubuntu-4.13.0-45.50
[mirror_ubuntu-artful-kernel.git] / fs / sysfs / symlink.c
index 3ae3f1bf1a0957075bec9d36d8834550000c3b14..aecb15f8455723ed68182b3f139e03226f00694e 100644 (file)
  */
 
 #include <linux/fs.h>
-#include <linux/gfp.h>
-#include <linux/mount.h>
 #include <linux/module.h>
 #include <linux/kobject.h>
-#include <linux/namei.h>
 #include <linux/mutex.h>
 #include <linux/security.h>
 
 #include "sysfs.h"
 
-static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd,
-                                  struct kobject *target,
+static int sysfs_do_create_link_sd(struct kernfs_node *parent,
+                                  struct kobject *target_kobj,
                                   const char *name, int warn)
 {
-       struct sysfs_dirent *target_sd = NULL;
-       struct sysfs_dirent *sd = NULL;
-       struct sysfs_addrm_cxt acxt;
-       enum kobj_ns_type ns_type;
-       int error;
+       struct kernfs_node *kn, *target = NULL;
 
-       BUG_ON(!name || !parent_sd);
+       BUG_ON(!name || !parent);
 
        /*
-        * We don't own @target and it may be removed at any time.
+        * We don't own @target_kobj and it may be removed at any time.
         * Synchronize using sysfs_symlink_target_lock.  See
         * sysfs_remove_dir() for details.
         */
        spin_lock(&sysfs_symlink_target_lock);
-       if (target->sd)
-               target_sd = sysfs_get(target->sd);
+       if (target_kobj->sd) {
+               target = target_kobj->sd;
+               kernfs_get(target);
+       }
        spin_unlock(&sysfs_symlink_target_lock);
 
-       error = -ENOENT;
-       if (!target_sd)
-               goto out_put;
-
-       error = -ENOMEM;
-       sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
-       if (!sd)
-               goto out_put;
+       if (!target)
+               return -ENOENT;
 
-       ns_type = sysfs_ns_type(parent_sd);
-       if (ns_type)
-               sd->s_ns = target_sd->s_ns;
-       sd->s_symlink.target_sd = target_sd;
-       target_sd = NULL;       /* reference is now owned by the symlink */
-
-       sysfs_addrm_start(&acxt);
-       /* Symlinks must be between directories with the same ns_type */
-       if (!ns_type ||
-           (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) {
-               if (warn)
-                       error = sysfs_add_one(&acxt, sd, parent_sd);
-               else
-                       error = __sysfs_add_one(&acxt, sd, parent_sd);
-       } else {
-               error = -EINVAL;
-               WARN(1, KERN_WARNING
-                       "sysfs: symlink across ns_types %s/%s -> %s/%s\n",
-                       parent_sd->s_name,
-                       sd->s_name,
-                       sd->s_symlink.target_sd->s_parent->s_name,
-                       sd->s_symlink.target_sd->s_name);
-       }
-       sysfs_addrm_finish(&acxt);
+       kn = kernfs_create_link(parent, name, target);
+       kernfs_put(target);
 
-       if (error)
-               goto out_put;
+       if (!IS_ERR(kn))
+               return 0;
 
-       return 0;
-
- out_put:
-       sysfs_put(target_sd);
-       sysfs_put(sd);
-       return error;
+       if (warn && PTR_ERR(kn) == -EEXIST)
+               sysfs_warn_dup(parent, name);
+       return PTR_ERR(kn);
 }
 
 /**
  *     sysfs_create_link_sd - create symlink to a given object.
- *     @sd:            directory we're creating the link in.
+ *     @kn:            directory we're creating the link in.
  *     @target:        object we're pointing to.
  *     @name:          name of the symlink.
  */
-int sysfs_create_link_sd(struct sysfs_dirent *sd, struct kobject *target,
+int sysfs_create_link_sd(struct kernfs_node *kn, struct kobject *target,
                         const char *name)
 {
-       return sysfs_do_create_link_sd(sd, target, name, 1);
+       return sysfs_do_create_link_sd(kn, target, name, 1);
 }
 
 static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
                                const char *name, int warn)
 {
-       struct sysfs_dirent *parent_sd = NULL;
+       struct kernfs_node *parent = NULL;
 
        if (!kobj)
-               parent_sd = &sysfs_root;
+               parent = sysfs_root_kn;
        else
-               parent_sd = kobj->sd;
+               parent = kobj->sd;
 
-       if (!parent_sd)
+       if (!parent)
                return -EFAULT;
 
-       return sysfs_do_create_link_sd(parent_sd, target, name, warn);
+       return sysfs_do_create_link_sd(parent, target, name, warn);
 }
 
 /**
@@ -164,10 +128,10 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ,
         * sysfs_remove_dir() for details.
         */
        spin_lock(&sysfs_symlink_target_lock);
-       if (targ->sd && sysfs_ns_type(kobj->sd))
-               ns = targ->sd->s_ns;
+       if (targ->sd && kernfs_ns_enabled(kobj->sd))
+               ns = targ->sd->ns;
        spin_unlock(&sysfs_symlink_target_lock);
-       sysfs_hash_and_remove(kobj->sd, name, ns);
+       kernfs_remove_by_name_ns(kobj->sd, name, ns);
 }
 
 /**
@@ -177,14 +141,14 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ,
  */
 void sysfs_remove_link(struct kobject *kobj, const char *name)
 {
-       struct sysfs_dirent *parent_sd = NULL;
+       struct kernfs_node *parent = NULL;
 
        if (!kobj)
-               parent_sd = &sysfs_root;
+               parent = sysfs_root_kn;
        else
-               parent_sd = kobj->sd;
+               parent = kobj->sd;
 
-       sysfs_hash_and_remove(parent_sd, name, NULL);
+       kernfs_remove_by_name(parent, name);
 }
 EXPORT_SYMBOL_GPL(sysfs_remove_link);
 
@@ -201,130 +165,33 @@ EXPORT_SYMBOL_GPL(sysfs_remove_link);
 int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ,
                         const char *old, const char *new, const void *new_ns)
 {
-       struct sysfs_dirent *parent_sd, *sd = NULL;
+       struct kernfs_node *parent, *kn = NULL;
        const void *old_ns = NULL;
        int result;
 
        if (!kobj)
-               parent_sd = &sysfs_root;
+               parent = sysfs_root_kn;
        else
-               parent_sd = kobj->sd;
+               parent = kobj->sd;
 
        if (targ->sd)
-               old_ns = targ->sd->s_ns;
+               old_ns = targ->sd->ns;
 
        result = -ENOENT;
-       sd = sysfs_get_dirent_ns(parent_sd, old, old_ns);
-       if (!sd)
+       kn = kernfs_find_and_get_ns(parent, old, old_ns);
+       if (!kn)
                goto out;
 
        result = -EINVAL;
-       if (sysfs_type(sd) != SYSFS_KOBJ_LINK)
+       if (kernfs_type(kn) != KERNFS_LINK)
                goto out;
-       if (sd->s_symlink.target_sd->s_dir.kobj != targ)
+       if (kn->symlink.target_kn->priv != targ)
                goto out;
 
-       result = sysfs_rename(sd, parent_sd, new, new_ns);
+       result = kernfs_rename_ns(kn, parent, new, new_ns);
 
 out:
-       sysfs_put(sd);
+       kernfs_put(kn);
        return result;
 }
 EXPORT_SYMBOL_GPL(sysfs_rename_link_ns);
-
-static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,
-                                struct sysfs_dirent *target_sd, char *path)
-{
-       struct sysfs_dirent *base, *sd;
-       char *s = path;
-       int len = 0;
-
-       /* go up to the root, stop at the base */
-       base = parent_sd;
-       while (base->s_parent) {
-               sd = target_sd->s_parent;
-               while (sd->s_parent && base != sd)
-                       sd = sd->s_parent;
-
-               if (base == sd)
-                       break;
-
-               strcpy(s, "../");
-               s += 3;
-               base = base->s_parent;
-       }
-
-       /* determine end of target string for reverse fillup */
-       sd = target_sd;
-       while (sd->s_parent && sd != base) {
-               len += strlen(sd->s_name) + 1;
-               sd = sd->s_parent;
-       }
-
-       /* check limits */
-       if (len < 2)
-               return -EINVAL;
-       len--;
-       if ((s - path) + len > PATH_MAX)
-               return -ENAMETOOLONG;
-
-       /* reverse fillup of target string from target to base */
-       sd = target_sd;
-       while (sd->s_parent && sd != base) {
-               int slen = strlen(sd->s_name);
-
-               len -= slen;
-               strncpy(s + len, sd->s_name, slen);
-               if (len)
-                       s[--len] = '/';
-
-               sd = sd->s_parent;
-       }
-
-       return 0;
-}
-
-static int sysfs_getlink(struct dentry *dentry, char *path)
-{
-       struct sysfs_dirent *sd = dentry->d_fsdata;
-       struct sysfs_dirent *parent_sd = sd->s_parent;
-       struct sysfs_dirent *target_sd = sd->s_symlink.target_sd;
-       int error;
-
-       mutex_lock(&sysfs_mutex);
-       error = sysfs_get_target_path(parent_sd, target_sd, path);
-       mutex_unlock(&sysfs_mutex);
-
-       return error;
-}
-
-static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       int error = -ENOMEM;
-       unsigned long page = get_zeroed_page(GFP_KERNEL);
-       if (page) {
-               error = sysfs_getlink(dentry, (char *) page);
-               if (error < 0)
-                       free_page((unsigned long)page);
-       }
-       nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
-       return NULL;
-}
-
-static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd,
-                          void *cookie)
-{
-       char *page = nd_get_link(nd);
-       if (!IS_ERR(page))
-               free_page((unsigned long)page);
-}
-
-const struct inode_operations sysfs_symlink_inode_operations = {
-       .setxattr       = sysfs_setxattr,
-       .readlink       = generic_readlink,
-       .follow_link    = sysfs_follow_link,
-       .put_link       = sysfs_put_link,
-       .setattr        = sysfs_setattr,
-       .getattr        = sysfs_getattr,
-       .permission     = sysfs_permission,
-};