hfsplus: add per-superblock lock for volume header updates
Lock updates to the mutal fields in the volume header, and document the locing in the hfsplus_sb_info structure. Signed-off-by: Christoph Hellwig <hch@tuxera.com>
This commit is contained in:
parent
58a818f532
commit
7ac9fb9c2a
|
@ -251,6 +251,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
|
|||
if (HFSPLUS_IS_RSRC(inode))
|
||||
return -EPERM;
|
||||
|
||||
mutex_lock(&sbi->vh_mutex);
|
||||
if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
|
||||
for (;;) {
|
||||
get_random_bytes(&id, sizeof(cnid));
|
||||
|
@ -263,7 +264,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
|
|||
if (!res)
|
||||
break;
|
||||
if (res != -EEXIST)
|
||||
return res;
|
||||
goto out;
|
||||
}
|
||||
HFSPLUS_I(inode)->dev = id;
|
||||
cnid = sbi->next_cnid++;
|
||||
|
@ -271,13 +272,13 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
|
|||
res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode);
|
||||
if (res)
|
||||
/* panic? */
|
||||
return res;
|
||||
goto out;
|
||||
sbi->file_count++;
|
||||
}
|
||||
cnid = sbi->next_cnid++;
|
||||
res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
|
||||
if (res)
|
||||
return res;
|
||||
goto out;
|
||||
|
||||
inc_nlink(inode);
|
||||
hfsplus_instantiate(dst_dentry, inode, cnid);
|
||||
|
@ -286,8 +287,9 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
|
|||
mark_inode_dirty(inode);
|
||||
sbi->file_count++;
|
||||
dst_dir->i_sb->s_dirt = 1;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
mutex_unlock(&sbi->vh_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
|
||||
|
@ -302,6 +304,7 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
|
|||
if (HFSPLUS_IS_RSRC(inode))
|
||||
return -EPERM;
|
||||
|
||||
mutex_lock(&sbi->vh_mutex);
|
||||
cnid = (u32)(unsigned long)dentry->d_fsdata;
|
||||
if (inode->i_ino == cnid &&
|
||||
atomic_read(&HFSPLUS_I(inode)->opencnt)) {
|
||||
|
@ -312,11 +315,11 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
|
|||
sbi->hidden_dir, &str);
|
||||
if (!res)
|
||||
inode->i_flags |= S_DEAD;
|
||||
return res;
|
||||
goto out;
|
||||
}
|
||||
res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
|
||||
if (res)
|
||||
return res;
|
||||
goto out;
|
||||
|
||||
if (inode->i_nlink > 0)
|
||||
drop_nlink(inode);
|
||||
|
@ -339,37 +342,44 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
|
|||
sbi->file_count--;
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
out:
|
||||
mutex_unlock(&sbi->vh_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int res;
|
||||
|
||||
inode = dentry->d_inode;
|
||||
if (inode->i_size != 2)
|
||||
return -ENOTEMPTY;
|
||||
|
||||
mutex_lock(&sbi->vh_mutex);
|
||||
res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
|
||||
if (res)
|
||||
return res;
|
||||
goto out;
|
||||
clear_nlink(inode);
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
hfsplus_delete_inode(inode);
|
||||
mark_inode_dirty(inode);
|
||||
return 0;
|
||||
out:
|
||||
mutex_unlock(&sbi->vh_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
|
||||
const char *symname)
|
||||
{
|
||||
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
|
||||
struct inode *inode;
|
||||
int res;
|
||||
int res = -ENOSPC;
|
||||
|
||||
mutex_lock(&sbi->vh_mutex);
|
||||
inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO);
|
||||
if (!inode)
|
||||
return -ENOSPC;
|
||||
goto out;
|
||||
|
||||
res = page_symlink(inode, symname, strlen(symname) + 1);
|
||||
if (res)
|
||||
|
@ -381,31 +391,35 @@ static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
hfsplus_instantiate(dentry, inode, inode->i_ino);
|
||||
mark_inode_dirty(inode);
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
out_err:
|
||||
inode->i_nlink = 0;
|
||||
hfsplus_delete_inode(inode);
|
||||
iput(inode);
|
||||
out:
|
||||
mutex_unlock(&sbi->vh_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
|
||||
int mode, dev_t rdev)
|
||||
{
|
||||
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
|
||||
struct inode *inode;
|
||||
int res;
|
||||
int res = -ENOSPC;
|
||||
|
||||
mutex_lock(&sbi->vh_mutex);
|
||||
inode = hfsplus_new_inode(dir->i_sb, mode);
|
||||
if (!inode)
|
||||
return -ENOSPC;
|
||||
goto out;
|
||||
|
||||
res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
|
||||
if (res) {
|
||||
inode->i_nlink = 0;
|
||||
hfsplus_delete_inode(inode);
|
||||
iput(inode);
|
||||
return res;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
|
||||
|
@ -413,7 +427,9 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
hfsplus_instantiate(dentry, inode, inode->i_ino);
|
||||
mark_inode_dirty(inode);
|
||||
return 0;
|
||||
out:
|
||||
mutex_unlock(&sbi->vh_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode,
|
||||
|
|
|
@ -116,23 +116,26 @@ struct hfsplus_sb_info {
|
|||
struct inode *hidden_dir;
|
||||
struct nls_table *nls;
|
||||
|
||||
/* synchronize block allocations */
|
||||
struct mutex alloc_mutex;
|
||||
|
||||
/* Runtime variables */
|
||||
u32 blockoffset;
|
||||
u32 sect_count;
|
||||
int fs_shift;
|
||||
|
||||
/* Stuff in host order from Vol Header */
|
||||
/* immutable data from the volume header */
|
||||
u32 alloc_blksz;
|
||||
int alloc_blksz_shift;
|
||||
u32 total_blocks;
|
||||
u32 data_clump_blocks, rsrc_clump_blocks;
|
||||
|
||||
/* mutable data from the volume header, protected by alloc_mutex */
|
||||
u32 free_blocks;
|
||||
struct mutex alloc_mutex;
|
||||
|
||||
/* mutable data from the volume header, protected by vh_mutex */
|
||||
u32 next_cnid;
|
||||
u32 file_count;
|
||||
u32 folder_count;
|
||||
u32 data_clump_blocks, rsrc_clump_blocks;
|
||||
struct mutex vh_mutex;
|
||||
|
||||
/* Config options */
|
||||
u32 creator;
|
||||
|
|
|
@ -160,6 +160,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
|
|||
|
||||
dprint(DBG_SUPER, "hfsplus_write_super\n");
|
||||
|
||||
mutex_lock(&sbi->vh_mutex);
|
||||
mutex_lock(&sbi->alloc_mutex);
|
||||
sb->s_dirt = 0;
|
||||
|
||||
|
@ -194,6 +195,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
|
|||
sbi->flags &= ~HFSPLUS_SB_WRITEBACKUP;
|
||||
}
|
||||
mutex_unlock(&sbi->alloc_mutex);
|
||||
mutex_unlock(&sbi->vh_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -319,6 +321,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
|
|||
|
||||
sb->s_fs_info = sbi;
|
||||
mutex_init(&sbi->alloc_mutex);
|
||||
mutex_init(&sbi->vh_mutex);
|
||||
hfsplus_fill_defaults(sbi);
|
||||
if (!hfsplus_parse_options(data, sbi)) {
|
||||
printk(KERN_ERR "hfs: unable to parse mount options\n");
|
||||
|
@ -453,9 +456,13 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
|
|||
|
||||
if (!sbi->hidden_dir) {
|
||||
printk(KERN_DEBUG "hfs: create hidden dir...\n");
|
||||
|
||||
mutex_lock(&sbi->vh_mutex);
|
||||
sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
|
||||
hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode,
|
||||
&str, sbi->hidden_dir);
|
||||
mutex_unlock(&sbi->vh_mutex);
|
||||
|
||||
mark_inode_dirty(sbi->hidden_dir);
|
||||
}
|
||||
out:
|
||||
|
|
Loading…
Reference in a new issue