hfsplus: switch to ->iterate_shared()
authorAl Viro <viro@zeniv.linux.org.uk>
Fri, 13 May 2016 00:02:09 +0000 (20:02 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 13 May 2016 00:08:40 +0000 (20:08 -0400)
We need to protect the list of hfsplus_readdir_data against parallel
insertions (in readdir) and removals (in release).  Add a spinlock
for that.  Note that it has nothing to do with protection of
hfsplus_readdir_data->key - we have an exclusion between hfsplus_readdir()
and hfsplus_delete_cat() on directory lock and between several
hfsplus_readdir() for the same struct file on ->f_pos_lock.  The spinlock
is strictly for list changes.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/hfsplus/catalog.c
fs/hfsplus/dir.c
fs/hfsplus/hfsplus_fs.h
fs/hfsplus/inode.c
fs/hfsplus/super.c

index 022974ab6e3ccb13230b9a001cb3bbfb36d69d1c..fb707e8f423a521de65a188e507010a06a7849e8 100644 (file)
@@ -374,12 +374,15 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
                hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
        }
 
+       /* we only need to take spinlock for exclusion with ->release() */
+       spin_lock(&HFSPLUS_I(dir)->open_dir_lock);
        list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) {
                struct hfsplus_readdir_data *rd =
                        list_entry(pos, struct hfsplus_readdir_data, list);
                if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
                        rd->file->f_pos--;
        }
+       spin_unlock(&HFSPLUS_I(dir)->open_dir_lock);
 
        err = hfs_brec_remove(&fd);
        if (err)
index a4e867e089478ddd3edaf24ce9be7d485d66ad34..42e128661dc15e4a2593f8cf321fd71f997188bc 100644 (file)
@@ -271,8 +271,14 @@ next:
                }
                file->private_data = rd;
                rd->file = file;
+               spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
                list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
+               spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
        }
+       /*
+        * Can be done after the list insertion; exclusion with
+        * hfsplus_delete_cat() is provided by directory lock.
+        */
        memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
 out:
        kfree(strbuf);
@@ -284,9 +290,9 @@ static int hfsplus_dir_release(struct inode *inode, struct file *file)
 {
        struct hfsplus_readdir_data *rd = file->private_data;
        if (rd) {
-               inode_lock(inode);
+               spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
                list_del(&rd->list);
-               inode_unlock(inode);
+               spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
                kfree(rd);
        }
        return 0;
@@ -569,7 +575,7 @@ const struct inode_operations hfsplus_dir_inode_operations = {
 const struct file_operations hfsplus_dir_operations = {
        .fsync          = hfsplus_file_fsync,
        .read           = generic_read_dir,
-       .iterate        = hfsplus_readdir,
+       .iterate_shared = hfsplus_readdir,
        .unlocked_ioctl = hfsplus_ioctl,
        .llseek         = generic_file_llseek,
        .release        = hfsplus_dir_release,
index f91a1faf819e9f06ae31b74f157e2c5627f4ebea..fdc3446d934ad17523a6fba3490343e85756702a 100644 (file)
@@ -244,6 +244,7 @@ struct hfsplus_inode_info {
        u8 userflags;           /* BSD user file flags */
        u32 subfolders;         /* Subfolder count (HFSX only) */
        struct list_head open_dir_list;
+       spinlock_t open_dir_lock;
        loff_t phys_size;
 
        struct inode vfs_inode;
index b28f39865c3accd94ab0576d95ddeef867c6a53f..037f738c5871ee4eee92f2e4627d1b78223bfb94 100644 (file)
@@ -374,6 +374,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
 
        hip = HFSPLUS_I(inode);
        INIT_LIST_HEAD(&hip->open_dir_list);
+       spin_lock_init(&hip->open_dir_lock);
        mutex_init(&hip->extents_lock);
        atomic_set(&hip->opencnt, 0);
        hip->extent_state = 0;
index c35911362ff9edaa839976cad64f1b9912430404..755bf30ba1ce5971df4c7693a0afbb2600cd2b00 100644 (file)
@@ -67,6 +67,7 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino)
                return inode;
 
        INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list);
+       spin_lock_init(&HFSPLUS_I(inode)->open_dir_lock);
        mutex_init(&HFSPLUS_I(inode)->extents_lock);
        HFSPLUS_I(inode)->flags = 0;
        HFSPLUS_I(inode)->extent_state = 0;