[SCSI] sysfs: make group is_valid return a mode_t
authorJames Bottomley <James.Bottomley@HansenPartnership.com>
Fri, 21 Mar 2008 01:47:52 +0000 (20:47 -0500)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Tue, 22 Apr 2008 20:16:31 +0000 (15:16 -0500)
We have a problem in scsi_transport_spi in that we need to customise
not only the visibility of the attributes, but also their mode.  Fix
this by making the is_visible() callback return a mode, with 0
indicating is not visible.

Also add a sysfs_update_group() API to allow us to change either the
visibility or mode of the files at any time on the fly.

Acked-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
fs/sysfs/file.c
fs/sysfs/group.c
fs/sysfs/sysfs.h
include/linux/sysfs.h

index ade9a7e..dbdfabb 100644 (file)
@@ -477,11 +477,10 @@ const struct file_operations sysfs_file_operations = {
        .poll           = sysfs_poll,
 };
 
-
-int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
-                  int type)
+int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
+                       const struct attribute *attr, int type, mode_t amode)
 {
-       umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;
+       umode_t mode = (amode & S_IALLUGO) | S_IFREG;
        struct sysfs_addrm_cxt acxt;
        struct sysfs_dirent *sd;
        int rc;
@@ -502,6 +501,13 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
 }
 
 
+int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
+                  int type)
+{
+       return sysfs_add_file_mode(dir_sd, attr, type, attr->mode);
+}
+
+
 /**
  *     sysfs_create_file - create an attribute file for an object.
  *     @kobj:  object we're creating for. 
index 4779049..eeba384 100644 (file)
@@ -23,35 +23,50 @@ static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
        int i;
 
        for (i = 0, attr = grp->attrs; *attr; i++, attr++)
-               if (!grp->is_visible ||
-                   grp->is_visible(kobj, *attr, i))
-                       sysfs_hash_and_remove(dir_sd, (*attr)->name);
+               sysfs_hash_and_remove(dir_sd, (*attr)->name);
 }
 
 static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
-                       const struct attribute_group *grp)
+                       const struct attribute_group *grp, int update)
 {
        struct attribute *const* attr;
        int error = 0, i;
 
-       for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++)
-               if (!grp->is_visible ||
-                   grp->is_visible(kobj, *attr, i))
-                       error |=
-                               sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR);
+       for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
+               mode_t mode = 0;
+
+               /* in update mode, we're changing the permissions or
+                * visibility.  Do this by first removing then
+                * re-adding (if required) the file */
+               if (update)
+                       sysfs_hash_and_remove(dir_sd, (*attr)->name);
+               if (grp->is_visible) {
+                       mode = grp->is_visible(kobj, *attr, i);
+                       if (!mode)
+                               continue;
+               }
+               error = sysfs_add_file_mode(dir_sd, *attr, SYSFS_KOBJ_ATTR,
+                                           (*attr)->mode | mode);
+               if (unlikely(error))
+                       break;
+       }
        if (error)
                remove_files(dir_sd, kobj, grp);
        return error;
 }
 
 
-int sysfs_create_group(struct kobject * kobj, 
-                      const struct attribute_group * grp)
+static int internal_create_group(struct kobject *kobj, int update,
+                                const struct attribute_group *grp)
 {
        struct sysfs_dirent *sd;
        int error;
 
-       BUG_ON(!kobj || !kobj->sd);
+       BUG_ON(!kobj || (!update && !kobj->sd));
+
+       /* Updates may happen before the object has been instantiated */
+       if (unlikely(update && !kobj->sd))
+               return -EINVAL;
 
        if (grp->name) {
                error = sysfs_create_subdir(kobj, grp->name, &sd);
@@ -60,7 +75,7 @@ int sysfs_create_group(struct kobject * kobj,
        } else
                sd = kobj->sd;
        sysfs_get(sd);
-       error = create_files(sd, kobj, grp);
+       error = create_files(sd, kobj, grp, update);
        if (error) {
                if (grp->name)
                        sysfs_remove_subdir(sd);
@@ -69,6 +84,47 @@ int sysfs_create_group(struct kobject * kobj,
        return error;
 }
 
+/**
+ * sysfs_create_group - given a directory kobject, create an attribute group
+ * @kobj:      The kobject to create the group on
+ * @grp:       The attribute group to create
+ *
+ * This function creates a group for the first time.  It will explicitly
+ * warn and error if any of the attribute files being created already exist.
+ *
+ * Returns 0 on success or error.
+ */
+int sysfs_create_group(struct kobject *kobj,
+                      const struct attribute_group *grp)
+{
+       return internal_create_group(kobj, 0, grp);
+}
+
+/**
+ * sysfs_update_group - given a directory kobject, create an attribute group
+ * @kobj:      The kobject to create the group on
+ * @grp:       The attribute group to create
+ *
+ * This function updates an attribute group.  Unlike
+ * sysfs_create_group(), it will explicitly not warn or error if any
+ * of the attribute files being created already exist.  Furthermore,
+ * if the visibility of the files has changed through the is_visible()
+ * callback, it will update the permissions and add or remove the
+ * relevant files.
+ *
+ * The primary use for this function is to call it after making a change
+ * that affects group visibility.
+ *
+ * Returns 0 on success or error.
+ */
+int sysfs_update_group(struct kobject *kobj,
+                      const struct attribute_group *grp)
+{
+       return internal_create_group(kobj, 1, grp);
+}
+
+
+
 void sysfs_remove_group(struct kobject * kobj, 
                        const struct attribute_group * grp)
 {
@@ -95,4 +151,5 @@ void sysfs_remove_group(struct kobject * kobj,
 
 
 EXPORT_SYMBOL_GPL(sysfs_create_group);
+EXPORT_SYMBOL_GPL(sysfs_update_group);
 EXPORT_SYMBOL_GPL(sysfs_remove_group);
index ff17f8d..ce4e15f 100644 (file)
@@ -154,6 +154,8 @@ extern const struct file_operations sysfs_file_operations;
 int sysfs_add_file(struct sysfs_dirent *dir_sd,
                   const struct attribute *attr, int type);
 
+int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
+                       const struct attribute *attr, int type, mode_t amode);
 /*
  * bin.c
  */
index 03378e3..add3c5a 100644 (file)
@@ -32,7 +32,7 @@ struct attribute {
 
 struct attribute_group {
        const char              *name;
-       int                     (*is_visible)(struct kobject *,
+       mode_t                  (*is_visible)(struct kobject *,
                                              struct attribute *, int);
        struct attribute        **attrs;
 };
@@ -105,6 +105,8 @@ void sysfs_remove_link(struct kobject *kobj, const char *name);
 
 int __must_check sysfs_create_group(struct kobject *kobj,
                                    const struct attribute_group *grp);
+int sysfs_update_group(struct kobject *kobj,
+                      const struct attribute_group *grp);
 void sysfs_remove_group(struct kobject *kobj,
                        const struct attribute_group *grp);
 int sysfs_add_file_to_group(struct kobject *kobj,