Merge branch 'topic/usb' into for-linus
[pandora-kernel.git] / fs / sysfs / group.c
index 4779049..fe61194 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)
 {
@@ -78,9 +134,8 @@ void sysfs_remove_group(struct kobject * kobj,
        if (grp->name) {
                sd = sysfs_get_dirent(dir_sd, grp->name);
                if (!sd) {
-                       printk(KERN_WARNING "sysfs group %p not found for "
+                       WARN(!sd, KERN_WARNING "sysfs group %p not found for "
                                "kobject '%s'\n", grp, kobject_name(kobj));
-                       WARN_ON(!sd);
                        return;
                }
        } else
@@ -95,4 +150,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);