Skip to content

Commit

Permalink
hfsplus: add per-superblock lock for volume header updates
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Christoph Hellwig authored and Christoph Hellwig committed Oct 1, 2010
1 parent 58a818f commit 7ac9fb9
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 24 deletions.
54 changes: 35 additions & 19 deletions fs/hfsplus/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -263,21 +264,21 @@ 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++;
src_dentry->d_fsdata = (void *)(unsigned long)cnid;
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);
Expand All @@ -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)
Expand All @@ -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)) {
Expand All @@ -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);
Expand All @@ -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)
Expand All @@ -381,39 +391,45 @@ 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))
init_special_inode(inode, mode, rdev);

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,
Expand Down
13 changes: 8 additions & 5 deletions fs/hfsplus/hfsplus_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions fs/hfsplus/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 7ac9fb9

Please sign in to comment.