[PATCH 15/22] sysfs: implement sysfs_dirent based link interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



sysfs_add_link() takes parent sd, name, mode and the target sd and
creates a link accordingly.  Currently, sysfs links can only point to
directories but this limitiation is artificial to avoid inflating the
sysfs_dirent structure by one pointer with future changes and can be
easily removed.

Kobject based sysfs_create_link() is reimplemented using
sysfs_add_link().

This patch doesn't introduce any behavior change to the original API.

Signed-off-by: Tejun Heo <[email protected]>
---
 fs/sysfs/kobject.c    |   47 +++++++++++++++++++++++++++
 fs/sysfs/symlink.c    |   83 +++++++++++++++++-------------------------------
 include/linux/sysfs.h |   10 ++++++
 3 files changed, 87 insertions(+), 53 deletions(-)

diff --git a/fs/sysfs/kobject.c b/fs/sysfs/kobject.c
index a34c54e..7400575 100644
--- a/fs/sysfs/kobject.c
+++ b/fs/sysfs/kobject.c
@@ -355,6 +355,53 @@ void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
 }
 EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
 
+/*
+ * kobject link interface
+ */
+
+/**
+ *	sysfs_create_link - create symlink between two objects.
+ *	@kobj:	object whose directory we're creating the link in.
+ *	@target:	object we're pointing to.
+ *	@name:		name of the symlink.
+ */
+int sysfs_create_link(struct kobject *kobj, struct kobject *target,
+		      const char *name)
+{
+	struct sysfs_dirent *parent_sd, *target_sd, *sd;
+
+	BUG_ON(!name);
+
+	if (!kobj)
+		parent_sd = sysfs_root;
+	else
+		parent_sd = kobj->sd;
+
+	if (!parent_sd)
+		return -EFAULT;
+
+	/* target->sd can go away beneath us but is protected with
+	 * sysfs_assoc_lock.  Fetch target_sd from it.
+	 */
+	spin_lock(&sysfs_assoc_lock);
+	target_sd = NULL;
+	if (target->sd)
+		target_sd = sysfs_get(target->sd);
+	spin_unlock(&sysfs_assoc_lock);
+
+	if (!target_sd)
+		return -ENOENT;
+
+	sd = sysfs_add_link(parent_sd, name, SYSFS_COPY_NAME, target_sd);
+
+	sysfs_put(target_sd);
+
+	if (IS_ERR(sd))
+		return PTR_ERR(sd);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sysfs_create_link);
+
 /**
  *	sysfs_remove_link - remove symlink in object's directory.
  *	@kobj:	object we're acting for.
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index 2c3e4f7..42ecb69 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -5,7 +5,6 @@
 #include <linux/fs.h>
 #include <linux/mount.h>
 #include <linux/module.h>
-#include <linux/kobject.h>
 #include <linux/namei.h>
 #include <linux/mutex.h>
 
@@ -45,64 +44,45 @@ static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length)
 }
 
 /**
- *	sysfs_create_link - create symlink between two objects.
- *	@kobj:	object whose directory we're creating the link in.
- *	@target:	object we're pointing to.
- *	@name:		name of the symlink.
+ *	sysfs_add_link - add a new sysfs symlink
+ *	@parent: sysfs_dirent to add symlink under
+ *	@name: name of the symlink
+ *	@mode: SYSFS_* flags for the new symlink
+ *	@target: target of the symlink
+ *
+ *	Add a new symlink which points to @target under @parent with
+ *	the specified parameters.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ *
+ *	RETURNS:
+ *	Pointer to the new sysfs_dirent on success, ERR_PTR() value on
+ *	error.
  */
-int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
+struct sysfs_dirent *sysfs_add_link(struct sysfs_dirent *parent,
+				    const char *name, mode_t mode,
+				    struct sysfs_dirent *target)
 {
-	struct sysfs_dirent *parent_sd = NULL;
-	struct sysfs_dirent *target_sd = NULL;
-	struct sysfs_dirent *sd = NULL;
-	struct sysfs_addrm_cxt acxt;
-	int error;
-
-	BUG_ON(!name);
-
-	if (!kobj)
-		parent_sd = sysfs_root;
-	else
-		parent_sd = kobj->sd;
+	struct sysfs_dirent *sd;
 
-	error = -EFAULT;
-	if (!parent_sd)
-		goto out_put;
-
-	/* target->sd can go away beneath us but is protected with
-	 * sysfs_assoc_lock.  Fetch target_sd from it.
+	/* Only symlink to directories are allowed.  This is an
+	 * artificial limitation.  If ever needed, allowing symlinks
+	 * to point to other types of sysfs nodes isn't difficult.
 	 */
-	spin_lock(&sysfs_assoc_lock);
-	if (target->sd)
-		target_sd = sysfs_get(target->sd);
-	spin_unlock(&sysfs_assoc_lock);
-
-	error = -ENOENT;
-	if (!target_sd)
-		goto out_put;
+	if (sysfs_type(target) != SYSFS_DIR)
+		return ERR_PTR(-EINVAL);
 
-	error = -ENOMEM;
-	sd = sysfs_new_dirent(name, S_IRWXUGO | SYSFS_COPY_NAME, SYSFS_LINK);
+	/* allocate & initialize */
+	sd = sysfs_new_dirent(name, mode | S_IRWXUGO, SYSFS_LINK);
 	if (!sd)
-		goto out_put;
-
-	sd->s_link.target = target_sd;
-	target_sd = NULL;	/* reference is now owned by the symlink */
+		return ERR_PTR(-ENOMEM);
 
-	sysfs_addrm_start(&acxt);
-	error = sysfs_add_one(&acxt, parent_sd, sd);
-	sysfs_addrm_finish(&acxt);
+	sd->s_link.target = sysfs_get(target);
 
-	if (error)
-		goto out_put;
-
-	return 0;
-
- out_put:
-	sysfs_put(target_sd);
-	sysfs_put(sd);
-	return error;
+	return sysfs_insert_one(parent, sd);
 }
+EXPORT_SYMBOL_GPL(sysfs_add_link);
 
 static int sysfs_get_target_path(struct sysfs_dirent * parent_sd,
 				 struct sysfs_dirent * target_sd, char *path)
@@ -162,6 +142,3 @@ const struct inode_operations sysfs_link_inode_operations = {
 	.follow_link = sysfs_follow_link,
 	.put_link = sysfs_put_link,
 };
-
-
-EXPORT_SYMBOL_GPL(sysfs_create_link);
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index dfb6bd7..08ed1b0 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -60,6 +60,9 @@ struct sysfs_dirent *sysfs_add_file(struct sysfs_dirent *parent,
 struct sysfs_dirent *sysfs_add_bin(struct sysfs_dirent *parent,
 			const char *name, mode_t mode, size_t size,
 			const struct sysfs_bin_ops *bops, void *data);
+struct sysfs_dirent *sysfs_add_link(struct sysfs_dirent *parent,
+			const char *name, mode_t mode,
+			struct sysfs_dirent *target);
 
 struct sysfs_dirent *sysfs_find_child(struct sysfs_dirent *parent,
 				      const char *name);
@@ -94,6 +97,13 @@ static inline struct sysfs_dirent *sysfs_add_bin(struct sysfs_dirent *parent,
 	return NULL;
 }
 
+static inline struct sysfs_dirent *sysfs_add_link(struct sysfs_dirent *parent,
+			const char *name, mode_t mode,
+			struct sysfs_dirent *target)
+{
+	return NULL;
+}
+
 static inline struct sysfs_dirent *sysfs_find_child(struct sysfs_dirent *parent,
 						    const char *name)
 {
-- 
1.5.0.3


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux