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:
Christoph Hellwig 2010-10-01 05:45:08 +02:00 committed by Christoph Hellwig
parent 58a818f532
commit 7ac9fb9c2a
3 changed files with 50 additions and 24 deletions

View file

@ -251,6 +251,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
if (HFSPLUS_IS_RSRC(inode)) if (HFSPLUS_IS_RSRC(inode))
return -EPERM; return -EPERM;
mutex_lock(&sbi->vh_mutex);
if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
for (;;) { for (;;) {
get_random_bytes(&id, sizeof(cnid)); get_random_bytes(&id, sizeof(cnid));
@ -263,7 +264,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
if (!res) if (!res)
break; break;
if (res != -EEXIST) if (res != -EEXIST)
return res; goto out;
} }
HFSPLUS_I(inode)->dev = id; HFSPLUS_I(inode)->dev = id;
cnid = sbi->next_cnid++; 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); res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode);
if (res) if (res)
/* panic? */ /* panic? */
return res; goto out;
sbi->file_count++; sbi->file_count++;
} }
cnid = sbi->next_cnid++; cnid = sbi->next_cnid++;
res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
if (res) if (res)
return res; goto out;
inc_nlink(inode); inc_nlink(inode);
hfsplus_instantiate(dst_dentry, inode, cnid); 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); mark_inode_dirty(inode);
sbi->file_count++; sbi->file_count++;
dst_dir->i_sb->s_dirt = 1; dst_dir->i_sb->s_dirt = 1;
out:
return 0; mutex_unlock(&sbi->vh_mutex);
return res;
} }
static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) 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)) if (HFSPLUS_IS_RSRC(inode))
return -EPERM; return -EPERM;
mutex_lock(&sbi->vh_mutex);
cnid = (u32)(unsigned long)dentry->d_fsdata; cnid = (u32)(unsigned long)dentry->d_fsdata;
if (inode->i_ino == cnid && if (inode->i_ino == cnid &&
atomic_read(&HFSPLUS_I(inode)->opencnt)) { atomic_read(&HFSPLUS_I(inode)->opencnt)) {
@ -312,11 +315,11 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
sbi->hidden_dir, &str); sbi->hidden_dir, &str);
if (!res) if (!res)
inode->i_flags |= S_DEAD; inode->i_flags |= S_DEAD;
return res; goto out;
} }
res = hfsplus_delete_cat(cnid, dir, &dentry->d_name); res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
if (res) if (res)
return res; goto out;
if (inode->i_nlink > 0) if (inode->i_nlink > 0)
drop_nlink(inode); drop_nlink(inode);
@ -339,37 +342,44 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
sbi->file_count--; sbi->file_count--;
inode->i_ctime = CURRENT_TIME_SEC; inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode); mark_inode_dirty(inode);
out:
mutex_unlock(&sbi->vh_mutex);
return res; return res;
} }
static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) 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; int res;
inode = dentry->d_inode;
if (inode->i_size != 2) if (inode->i_size != 2)
return -ENOTEMPTY; return -ENOTEMPTY;
mutex_lock(&sbi->vh_mutex);
res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
if (res) if (res)
return res; goto out;
clear_nlink(inode); clear_nlink(inode);
inode->i_ctime = CURRENT_TIME_SEC; inode->i_ctime = CURRENT_TIME_SEC;
hfsplus_delete_inode(inode); hfsplus_delete_inode(inode);
mark_inode_dirty(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, static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
const char *symname) const char *symname)
{ {
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
struct inode *inode; struct inode *inode;
int res; int res = -ENOSPC;
mutex_lock(&sbi->vh_mutex);
inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO); inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO);
if (!inode) if (!inode)
return -ENOSPC; goto out;
res = page_symlink(inode, symname, strlen(symname) + 1); res = page_symlink(inode, symname, strlen(symname) + 1);
if (res) if (res)
@ -381,31 +391,35 @@ static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
hfsplus_instantiate(dentry, inode, inode->i_ino); hfsplus_instantiate(dentry, inode, inode->i_ino);
mark_inode_dirty(inode); mark_inode_dirty(inode);
return 0; goto out;
out_err: out_err:
inode->i_nlink = 0; inode->i_nlink = 0;
hfsplus_delete_inode(inode); hfsplus_delete_inode(inode);
iput(inode); iput(inode);
out:
mutex_unlock(&sbi->vh_mutex);
return res; return res;
} }
static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
int mode, dev_t rdev) int mode, dev_t rdev)
{ {
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
struct inode *inode; struct inode *inode;
int res; int res = -ENOSPC;
mutex_lock(&sbi->vh_mutex);
inode = hfsplus_new_inode(dir->i_sb, mode); inode = hfsplus_new_inode(dir->i_sb, mode);
if (!inode) if (!inode)
return -ENOSPC; goto out;
res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
if (res) { if (res) {
inode->i_nlink = 0; inode->i_nlink = 0;
hfsplus_delete_inode(inode); hfsplus_delete_inode(inode);
iput(inode); iput(inode);
return res; goto out;
} }
if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) 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); hfsplus_instantiate(dentry, inode, inode->i_ino);
mark_inode_dirty(inode); 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, static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode,

View file

@ -116,23 +116,26 @@ struct hfsplus_sb_info {
struct inode *hidden_dir; struct inode *hidden_dir;
struct nls_table *nls; struct nls_table *nls;
/* synchronize block allocations */
struct mutex alloc_mutex;
/* Runtime variables */ /* Runtime variables */
u32 blockoffset; u32 blockoffset;
u32 sect_count; u32 sect_count;
int fs_shift; int fs_shift;
/* Stuff in host order from Vol Header */ /* immutable data from the volume header */
u32 alloc_blksz; u32 alloc_blksz;
int alloc_blksz_shift; int alloc_blksz_shift;
u32 total_blocks; u32 total_blocks;
u32 data_clump_blocks, rsrc_clump_blocks;
/* mutable data from the volume header, protected by alloc_mutex */
u32 free_blocks; u32 free_blocks;
struct mutex alloc_mutex;
/* mutable data from the volume header, protected by vh_mutex */
u32 next_cnid; u32 next_cnid;
u32 file_count; u32 file_count;
u32 folder_count; u32 folder_count;
u32 data_clump_blocks, rsrc_clump_blocks; struct mutex vh_mutex;
/* Config options */ /* Config options */
u32 creator; u32 creator;

View file

@ -160,6 +160,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
dprint(DBG_SUPER, "hfsplus_write_super\n"); dprint(DBG_SUPER, "hfsplus_write_super\n");
mutex_lock(&sbi->vh_mutex);
mutex_lock(&sbi->alloc_mutex); mutex_lock(&sbi->alloc_mutex);
sb->s_dirt = 0; sb->s_dirt = 0;
@ -194,6 +195,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
sbi->flags &= ~HFSPLUS_SB_WRITEBACKUP; sbi->flags &= ~HFSPLUS_SB_WRITEBACKUP;
} }
mutex_unlock(&sbi->alloc_mutex); mutex_unlock(&sbi->alloc_mutex);
mutex_unlock(&sbi->vh_mutex);
return 0; return 0;
} }
@ -319,6 +321,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
sb->s_fs_info = sbi; sb->s_fs_info = sbi;
mutex_init(&sbi->alloc_mutex); mutex_init(&sbi->alloc_mutex);
mutex_init(&sbi->vh_mutex);
hfsplus_fill_defaults(sbi); hfsplus_fill_defaults(sbi);
if (!hfsplus_parse_options(data, sbi)) { if (!hfsplus_parse_options(data, sbi)) {
printk(KERN_ERR "hfs: unable to parse mount options\n"); 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) { if (!sbi->hidden_dir) {
printk(KERN_DEBUG "hfs: create hidden dir...\n"); printk(KERN_DEBUG "hfs: create hidden dir...\n");
mutex_lock(&sbi->vh_mutex);
sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode, hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode,
&str, sbi->hidden_dir); &str, sbi->hidden_dir);
mutex_unlock(&sbi->vh_mutex);
mark_inode_dirty(sbi->hidden_dir); mark_inode_dirty(sbi->hidden_dir);
} }
out: out: