configfs_readdir(): make safe under shared lock
authorAl Viro <viro@zeniv.linux.org.uk>
Wed, 20 Apr 2016 23:50:05 +0000 (19:50 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 9 May 2016 15:41:13 +0000 (11:41 -0400)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/configfs/dir.c

index 48929c408e04c83a489180cbe8e4efb7950badd1..56fb26127fef2544c52838674b2e8b84ae873807 100644 (file)
@@ -1633,11 +1633,9 @@ static int configfs_readdir(struct file *file, struct dir_context *ctx)
 
        if (!dir_emit_dots(file, ctx))
                return 0;
-       if (ctx->pos == 2) {
-               spin_lock(&configfs_dirent_lock);
+       spin_lock(&configfs_dirent_lock);
+       if (ctx->pos == 2)
                list_move(q, &parent_sd->s_children);
-               spin_unlock(&configfs_dirent_lock);
-       }
        for (p = q->next; p != &parent_sd->s_children; p = p->next) {
                struct configfs_dirent *next;
                const char *name;
@@ -1648,9 +1646,6 @@ static int configfs_readdir(struct file *file, struct dir_context *ctx)
                if (!next->s_element)
                        continue;
 
-               name = configfs_get_name(next);
-               len = strlen(name);
-
                /*
                 * We'll have a dentry and an inode for
                 * PINNED items and for open attribute
@@ -1664,7 +1659,6 @@ static int configfs_readdir(struct file *file, struct dir_context *ctx)
                 * they close it.  Beyond that, we don't
                 * care.
                 */
-               spin_lock(&configfs_dirent_lock);
                dentry = next->s_dentry;
                if (dentry)
                        inode = d_inode(dentry);
@@ -1674,15 +1668,18 @@ static int configfs_readdir(struct file *file, struct dir_context *ctx)
                if (!inode)
                        ino = iunique(sb, 2);
 
+               name = configfs_get_name(next);
+               len = strlen(name);
+
                if (!dir_emit(ctx, name, len, ino, dt_type(next)))
                        return 0;
 
                spin_lock(&configfs_dirent_lock);
                list_move(q, p);
-               spin_unlock(&configfs_dirent_lock);
                p = q;
                ctx->pos++;
        }
+       spin_unlock(&configfs_dirent_lock);
        return 0;
 }
 
@@ -1690,7 +1687,6 @@ static loff_t configfs_dir_lseek(struct file *file, loff_t offset, int whence)
 {
        struct dentry * dentry = file->f_path.dentry;
 
-       inode_lock(d_inode(dentry));
        switch (whence) {
                case 1:
                        offset += file->f_pos;
@@ -1698,7 +1694,6 @@ static loff_t configfs_dir_lseek(struct file *file, loff_t offset, int whence)
                        if (offset >= 0)
                                break;
                default:
-                       inode_unlock(d_inode(dentry));
                        return -EINVAL;
        }
        if (offset != file->f_pos) {
@@ -1724,7 +1719,6 @@ static loff_t configfs_dir_lseek(struct file *file, loff_t offset, int whence)
                        spin_unlock(&configfs_dirent_lock);
                }
        }
-       inode_unlock(d_inode(dentry));
        return offset;
 }
 
@@ -1733,7 +1727,7 @@ const struct file_operations configfs_dir_operations = {
        .release        = configfs_dir_close,
        .llseek         = configfs_dir_lseek,
        .read           = generic_read_dir,
-       .iterate        = configfs_readdir,
+       .iterate_shared = configfs_readdir,
 };
 
 /**