Merge tag 'configfs-5.15' of git://git.infradead.org/users/hch/configfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Sep 2021 17:27:17 +0000 (10:27 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Sep 2021 17:27:17 +0000 (10:27 -0700)
Pull configfs updates from Christoph Hellwig:

 - fix a race in configfs_lookup (Sishuai Gong)

 - minor cleanups (me)

* tag 'configfs-5.15' of git://git.infradead.org/users/hch/configfs:
  configfs: fix a race in configfs_lookup()
  configfs: fold configfs_attach_attr into configfs_lookup
  configfs: simplify the configfs_dirent_is_ready
  configfs: return -ENAMETOOLONG earlier in configfs_lookup

fs/configfs/dir.c

index ac5e0c0..1466b5d 100644 (file)
@@ -45,7 +45,7 @@ static void configfs_d_iput(struct dentry * dentry,
                /*
                 * Set sd->s_dentry to null only when this dentry is the one
                 * that is going to be killed.  Otherwise configfs_d_iput may
-                * run just after configfs_attach_attr and set sd->s_dentry to
+                * run just after configfs_lookup and set sd->s_dentry to
                 * NULL even it's still in use.
                 */
                if (sd->s_dentry == dentry)
@@ -417,44 +417,16 @@ static void configfs_remove_dir(struct config_item * item)
        dput(dentry);
 }
 
-
-/* attaches attribute's configfs_dirent to the dentry corresponding to the
- * attribute file
- */
-static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * dentry)
-{
-       struct configfs_attribute * attr = sd->s_element;
-       struct inode *inode;
-
-       spin_lock(&configfs_dirent_lock);
-       dentry->d_fsdata = configfs_get(sd);
-       sd->s_dentry = dentry;
-       spin_unlock(&configfs_dirent_lock);
-
-       inode = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG);
-       if (IS_ERR(inode)) {
-               configfs_put(sd);
-               return PTR_ERR(inode);
-       }
-       if (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) {
-               inode->i_size = 0;
-               inode->i_fop = &configfs_bin_file_operations;
-       } else {
-               inode->i_size = PAGE_SIZE;
-               inode->i_fop = &configfs_file_operations;
-       }
-       d_add(dentry, inode);
-       return 0;
-}
-
 static struct dentry * configfs_lookup(struct inode *dir,
                                       struct dentry *dentry,
                                       unsigned int flags)
 {
        struct configfs_dirent * parent_sd = dentry->d_parent->d_fsdata;
        struct configfs_dirent * sd;
-       int found = 0;
-       int err;
+       struct inode *inode = NULL;
+
+       if (dentry->d_name.len > NAME_MAX)
+               return ERR_PTR(-ENAMETOOLONG);
 
        /*
         * Fake invisibility if dir belongs to a group/default groups hierarchy
@@ -464,36 +436,39 @@ static struct dentry * configfs_lookup(struct inode *dir,
         * not complete their initialization, since the dentries of the
         * attributes won't be instantiated.
         */
-       err = -ENOENT;
        if (!configfs_dirent_is_ready(parent_sd))
-               goto out;
+               return ERR_PTR(-ENOENT);
 
+       spin_lock(&configfs_dirent_lock);
        list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
-               if (sd->s_type & CONFIGFS_NOT_PINNED) {
-                       const unsigned char * name = configfs_get_name(sd);
+               if ((sd->s_type & CONFIGFS_NOT_PINNED) &&
+                   !strcmp(configfs_get_name(sd), dentry->d_name.name)) {
+                       struct configfs_attribute *attr = sd->s_element;
+                       umode_t mode = (attr->ca_mode & S_IALLUGO) | S_IFREG;
 
-                       if (strcmp(name, dentry->d_name.name))
-                               continue;
+                       dentry->d_fsdata = configfs_get(sd);
+                       sd->s_dentry = dentry;
+                       spin_unlock(&configfs_dirent_lock);
 
-                       found = 1;
-                       err = configfs_attach_attr(sd, dentry);
-                       break;
+                       inode = configfs_create(dentry, mode);
+                       if (IS_ERR(inode)) {
+                               configfs_put(sd);
+                               return ERR_CAST(inode);
+                       }
+                       if (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) {
+                               inode->i_size = 0;
+                               inode->i_fop = &configfs_bin_file_operations;
+                       } else {
+                               inode->i_size = PAGE_SIZE;
+                               inode->i_fop = &configfs_file_operations;
+                       }
+                       goto done;
                }
        }
-
-       if (!found) {
-               /*
-                * If it doesn't exist and it isn't a NOT_PINNED item,
-                * it must be negative.
-                */
-               if (dentry->d_name.len > NAME_MAX)
-                       return ERR_PTR(-ENAMETOOLONG);
-               d_add(dentry, NULL);
-               return NULL;
-       }
-
-out:
-       return ERR_PTR(err);
+       spin_unlock(&configfs_dirent_lock);
+done:
+       d_add(dentry, inode);
+       return NULL;
 }
 
 /*