aboutsummaryrefslogtreecommitdiffstats
path: root/fs/hfsplus/dir.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@tuxera.com>2010-10-14 09:54:28 -0400
committerChristoph Hellwig <hch@lst.de>2010-10-14 09:54:28 -0400
commitf6089ff87d309a8ddb7b0d4dd92a570f1b0f689b (patch)
tree9b2c6b43885899e055e7b64b88e12bf3759e3794 /fs/hfsplus/dir.c
parent13571a6977f821fab7d9c3cc5f75da52b7732e40 (diff)
hfsplus: fix link corruption
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 <hch@tuxera.com>
Diffstat (limited to 'fs/hfsplus/dir.c')
-rw-r--r--fs/hfsplus/dir.c6
1 files changed, 4 insertions, 2 deletions
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index 33aab211695a..c05c8776e836 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -102,7 +102,7 @@ again:
102 if (IS_ERR(inode)) 102 if (IS_ERR(inode))
103 return ERR_CAST(inode); 103 return ERR_CAST(inode);
104 if (S_ISREG(inode->i_mode)) 104 if (S_ISREG(inode->i_mode))
105 HFSPLUS_I(inode)->dev = linkid; 105 HFSPLUS_I(inode)->linkid = linkid;
106out: 106out:
107 d_add(dentry, inode); 107 d_add(dentry, inode);
108 return NULL; 108 return NULL;
@@ -252,6 +252,8 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
252 252
253 if (HFSPLUS_IS_RSRC(inode)) 253 if (HFSPLUS_IS_RSRC(inode))
254 return -EPERM; 254 return -EPERM;
255 if (!S_ISREG(inode->i_mode))
256 return -EPERM;
255 257
256 mutex_lock(&sbi->vh_mutex); 258 mutex_lock(&sbi->vh_mutex);
257 if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { 259 if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
@@ -268,7 +270,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
268 if (res != -EEXIST) 270 if (res != -EEXIST)
269 goto out; 271 goto out;
270 } 272 }
271 HFSPLUS_I(inode)->dev = id; 273 HFSPLUS_I(inode)->linkid = id;
272 cnid = sbi->next_cnid++; 274 cnid = sbi->next_cnid++;
273 src_dentry->d_fsdata = (void *)(unsigned long)cnid; 275 src_dentry->d_fsdata = (void *)(unsigned long)cnid;
274 res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); 276 res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode);