Skip to content

Commit

Permalink
hfsplus: fix link corruption
Browse files Browse the repository at this point in the history
HFS implements hardlink by using indirect catalog entries that refer to a hidden
directly.  The link target is cached in the dev field in the HFS+ specific
inode, which is also used for the device number for device files, and inside
for passing the nlink value of the indirect node from hfsplus_cat_write_inode
to a helper function.  Now if we happen to write out the indirect node while
hfsplus_link is creating the catalog entry we'll get a link pointing to the
linkid of the current nlink value.  This can easily be reproduced by a large
enough loop of local git-clone operations.

Stop abusing the dev field in the HFS+ inode for short term storage by
refactoring the way the permission structure in the catalog entry is
set up, and rename the dev field to linkid to avoid any confusion.

While we're at it also prevent creating hard links to special files, as
the HFS+ dev and linkid share the same space in the on-disk structure.

Signed-off-by: Christoph Hellwig <[email protected]>
  • Loading branch information
Christoph Hellwig authored and Christoph Hellwig committed Oct 14, 2010
1 parent 13571a6 commit f6089ff
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 12 deletions.
2 changes: 1 addition & 1 deletion fs/hfsplus/catalog.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i
file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR);
file->user_info.fdFlags = cpu_to_be16(0x100);
file->create_date = HFSPLUS_I(sbi->hidden_dir)->create_date;
file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode)->dev);
file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode)->linkid);
}
return sizeof(*file);
}
Expand Down
6 changes: 4 additions & 2 deletions fs/hfsplus/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
if (IS_ERR(inode))
return ERR_CAST(inode);
if (S_ISREG(inode->i_mode))
HFSPLUS_I(inode)->dev = linkid;
HFSPLUS_I(inode)->linkid = linkid;
out:
d_add(dentry, inode);
return NULL;
Expand Down Expand Up @@ -252,6 +252,8 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,

if (HFSPLUS_IS_RSRC(inode))
return -EPERM;
if (!S_ISREG(inode->i_mode))
return -EPERM;

mutex_lock(&sbi->vh_mutex);
if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
Expand All @@ -268,7 +270,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
if (res != -EEXIST)
goto out;
}
HFSPLUS_I(inode)->dev = id;
HFSPLUS_I(inode)->linkid = 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);
Expand Down
8 changes: 5 additions & 3 deletions fs/hfsplus/hfsplus_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,11 @@ struct hfsplus_inode_info {
*/
struct inode *rsrc_inode;
__be32 create_date;
u32 dev;

/*
* Protected by sbi->vh_mutex.
*/
u32 linkid;

/*
* Protected by i_mutex.
Expand Down Expand Up @@ -427,6 +431,4 @@ static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode)
#define hfsp_ut2mt(t) __hfsp_ut2mt((t).tv_sec)
#define hfsp_now2mt() __hfsp_ut2mt(get_seconds())

#define kdev_t_to_nr(x) (x)

#endif
14 changes: 8 additions & 6 deletions fs/hfsplus/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,13 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms)
perms->mode = cpu_to_be16(inode->i_mode);
perms->owner = cpu_to_be32(inode->i_uid);
perms->group = cpu_to_be32(inode->i_gid);
perms->dev = cpu_to_be32(HFSPLUS_I(inode)->dev);

if (S_ISREG(inode->i_mode))
perms->dev = cpu_to_be32(inode->i_nlink);
else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode))
perms->dev = cpu_to_be32(inode->i_rdev);
else
perms->dev = 0;
}

static int hfsplus_file_open(struct inode *inode, struct file *file)
Expand Down Expand Up @@ -491,7 +497,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)

type = hfs_bnode_read_u16(fd->bnode, fd->entryoffset);

HFSPLUS_I(inode)->dev = 0;
HFSPLUS_I(inode)->linkid = 0;
if (type == HFSPLUS_FOLDER) {
struct hfsplus_cat_folder *folder = &entry.folder;

Expand Down Expand Up @@ -595,10 +601,6 @@ int hfsplus_cat_write_inode(struct inode *inode)
hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file));
hfsplus_inode_write_fork(inode, &file->data_fork);
if (S_ISREG(inode->i_mode))
HFSPLUS_I(inode)->dev = inode->i_nlink;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
HFSPLUS_I(inode)->dev = kdev_t_to_nr(inode->i_rdev);
hfsplus_set_perms(inode, &file->permissions);
if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE)
file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED);
Expand Down

0 comments on commit f6089ff

Please sign in to comment.