aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--fs/hfsplus/catalog.c2
-rw-r--r--fs/hfsplus/dir.c6
-rw-r--r--fs/hfsplus/hfsplus_fs.h8
-rw-r--r--fs/hfsplus/inode.c14
4 files changed, 18 insertions, 12 deletions
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
index 48979c4e8fa5..9d1594b0a07a 100644
--- a/fs/hfsplus/catalog.c
+++ b/fs/hfsplus/catalog.c
@@ -134,7 +134,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i
134 file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR); 134 file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR);
135 file->user_info.fdFlags = cpu_to_be16(0x100); 135 file->user_info.fdFlags = cpu_to_be16(0x100);
136 file->create_date = HFSPLUS_I(sbi->hidden_dir)->create_date; 136 file->create_date = HFSPLUS_I(sbi->hidden_dir)->create_date;
137 file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode)->dev); 137 file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode)->linkid);
138 } 138 }
139 return sizeof(*file); 139 return sizeof(*file);
140 } 140 }
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);
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 5cda96366acf..d92f590d6633 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -178,7 +178,11 @@ struct hfsplus_inode_info {
178 */ 178 */
179 struct inode *rsrc_inode; 179 struct inode *rsrc_inode;
180 __be32 create_date; 180 __be32 create_date;
181 u32 dev; 181
182 /*
183 * Protected by sbi->vh_mutex.
184 */
185 u32 linkid;
182 186
183 /* 187 /*
184 * Protected by i_mutex. 188 * Protected by i_mutex.
@@ -427,6 +431,4 @@ static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode)
427#define hfsp_ut2mt(t) __hfsp_ut2mt((t).tv_sec) 431#define hfsp_ut2mt(t) __hfsp_ut2mt((t).tv_sec)
428#define hfsp_now2mt() __hfsp_ut2mt(get_seconds()) 432#define hfsp_now2mt() __hfsp_ut2mt(get_seconds())
429 433
430#define kdev_t_to_nr(x) (x)
431
432#endif 434#endif
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index a05b3afa7230..746e0ee20717 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -267,7 +267,13 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms)
267 perms->mode = cpu_to_be16(inode->i_mode); 267 perms->mode = cpu_to_be16(inode->i_mode);
268 perms->owner = cpu_to_be32(inode->i_uid); 268 perms->owner = cpu_to_be32(inode->i_uid);
269 perms->group = cpu_to_be32(inode->i_gid); 269 perms->group = cpu_to_be32(inode->i_gid);
270 perms->dev = cpu_to_be32(HFSPLUS_I(inode)->dev); 270
271 if (S_ISREG(inode->i_mode))
272 perms->dev = cpu_to_be32(inode->i_nlink);
273 else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode))
274 perms->dev = cpu_to_be32(inode->i_rdev);
275 else
276 perms->dev = 0;
271} 277}
272 278
273static int hfsplus_file_open(struct inode *inode, struct file *file) 279static int hfsplus_file_open(struct inode *inode, struct file *file)
@@ -491,7 +497,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
491 497
492 type = hfs_bnode_read_u16(fd->bnode, fd->entryoffset); 498 type = hfs_bnode_read_u16(fd->bnode, fd->entryoffset);
493 499
494 HFSPLUS_I(inode)->dev = 0; 500 HFSPLUS_I(inode)->linkid = 0;
495 if (type == HFSPLUS_FOLDER) { 501 if (type == HFSPLUS_FOLDER) {
496 struct hfsplus_cat_folder *folder = &entry.folder; 502 struct hfsplus_cat_folder *folder = &entry.folder;
497 503
@@ -595,10 +601,6 @@ int hfsplus_cat_write_inode(struct inode *inode)
595 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, 601 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
596 sizeof(struct hfsplus_cat_file)); 602 sizeof(struct hfsplus_cat_file));
597 hfsplus_inode_write_fork(inode, &file->data_fork); 603 hfsplus_inode_write_fork(inode, &file->data_fork);
598 if (S_ISREG(inode->i_mode))
599 HFSPLUS_I(inode)->dev = inode->i_nlink;
600 if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
601 HFSPLUS_I(inode)->dev = kdev_t_to_nr(inode->i_rdev);
602 hfsplus_set_perms(inode, &file->permissions); 604 hfsplus_set_perms(inode, &file->permissions);
603 if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE) 605 if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE)
604 file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED); 606 file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED);