sysfs: Don't allow the creation of symlinks we can't remove
[firefly-linux-kernel-4.4.55.git] / fs / sysfs / symlink.c
index b6ebdaa00f379ef0fc0db36beec81e781e279285..44bca5f49cd5837e695d71d6a9983940e1522ce2 100644 (file)
@@ -28,6 +28,7 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
        struct sysfs_dirent *target_sd = NULL;
        struct sysfs_dirent *sd = NULL;
        struct sysfs_addrm_cxt acxt;
+       enum kobj_ns_type ns_type;
        int error;
 
        BUG_ON(!name);
@@ -58,16 +59,28 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
        if (!sd)
                goto out_put;
 
-       if (sysfs_ns_type(parent_sd))
+       ns_type = sysfs_ns_type(parent_sd);
+       if (ns_type)
                sd->s_ns = target->ktype->namespace(target);
        sd->s_symlink.target_sd = target_sd;
        target_sd = NULL;       /* reference is now owned by the symlink */
 
        sysfs_addrm_start(&acxt, parent_sd);
-       if (warn)
-               error = sysfs_add_one(&acxt, sd);
-       else
-               error = __sysfs_add_one(&acxt, sd);
+       /* Symlinks must be between directories with the same ns_type */
+       if (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent)) {
+               if (warn)
+                       error = sysfs_add_one(&acxt, sd);
+               else
+                       error = __sysfs_add_one(&acxt, 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);
 
        if (error)
@@ -108,6 +121,26 @@ int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target,
        return sysfs_do_create_link(kobj, target, name, 0);
 }
 
+/**
+ *     sysfs_delete_link - remove symlink in object's directory.
+ *     @kobj:  object we're acting for.
+ *     @targ:  object we're pointing to.
+ *     @name:  name of the symlink to remove.
+ *
+ *     Unlike sysfs_remove_link sysfs_delete_link has enough information
+ *     to successfully delete symlinks in tagged directories.
+ */
+void sysfs_delete_link(struct kobject *kobj, struct kobject *targ,
+                       const char *name)
+{
+       const void *ns = NULL;
+       spin_lock(&sysfs_assoc_lock);
+       if (targ->sd)
+               ns = targ->sd->s_ns;
+       spin_unlock(&sysfs_assoc_lock);
+       sysfs_hash_and_remove(kobj->sd, ns, name);
+}
+
 /**
  *     sysfs_remove_link - remove symlink in object's directory.
  *     @kobj:  object we're acting for.