diff options
| author | Sergei Antonov <saproj@gmail.com> | 2014-03-10 18:49:51 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-03-10 20:26:21 -0400 |
| commit | d7d673a591701f131e53d4fd4e2b9352f1316642 (patch) | |
| tree | 0aa921b14417385e422f01237a6dad23d163c2da /fs/hfsplus | |
| parent | 53942232369ec317f77dc8c2d5a0637bb85a6e84 (diff) | |
hfsplus: add HFSX subfolder count support
Adds support for HFSX 'HasFolderCount' flag and a corresponding
'folderCount' field in folder records. (For reference see
HFS_FOLDERCOUNT and kHFSHasFolderCountBit/kHFSHasFolderCountMask in
Apple's source code.)
Ignoring subfolder count leads to fs errors found by Mac:
...
Checking catalog hierarchy.
HasFolderCount flag needs to be set (id = 105)
(It should be 0x10 instead of 0)
Incorrect folder count in a directory (id = 2)
(It should be 7 instead of 6)
...
Steps to reproduce:
Format with "newfs_hfs -s /dev/diskXXX".
Mount in Linux.
Create a new directory in root.
Unmount.
Run "fsck_hfs /dev/diskXXX".
The patch handles directory creation, deletion, and rename.
Signed-off-by: Sergei Antonov <saproj@gmail.com>
Reviewed-by: Vyacheslav Dubeyko <slava@dubeyko.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/hfsplus')
| -rw-r--r-- | fs/hfsplus/catalog.c | 41 | ||||
| -rw-r--r-- | fs/hfsplus/hfsplus_fs.h | 1 | ||||
| -rw-r--r-- | fs/hfsplus/hfsplus_raw.h | 6 | ||||
| -rw-r--r-- | fs/hfsplus/inode.c | 9 |
4 files changed, 55 insertions, 2 deletions
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 968ce411db53..32602c667b4a 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c | |||
| @@ -103,6 +103,8 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, | |||
| 103 | folder = &entry->folder; | 103 | folder = &entry->folder; |
| 104 | memset(folder, 0, sizeof(*folder)); | 104 | memset(folder, 0, sizeof(*folder)); |
| 105 | folder->type = cpu_to_be16(HFSPLUS_FOLDER); | 105 | folder->type = cpu_to_be16(HFSPLUS_FOLDER); |
| 106 | if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) | ||
| 107 | folder->flags |= cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT); | ||
| 106 | folder->id = cpu_to_be32(inode->i_ino); | 108 | folder->id = cpu_to_be32(inode->i_ino); |
| 107 | HFSPLUS_I(inode)->create_date = | 109 | HFSPLUS_I(inode)->create_date = |
| 108 | folder->create_date = | 110 | folder->create_date = |
| @@ -203,6 +205,36 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid, | |||
| 203 | return hfs_brec_find(fd, hfs_find_rec_by_key); | 205 | return hfs_brec_find(fd, hfs_find_rec_by_key); |
| 204 | } | 206 | } |
| 205 | 207 | ||
| 208 | static void hfsplus_subfolders_inc(struct inode *dir) | ||
| 209 | { | ||
| 210 | struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); | ||
| 211 | |||
| 212 | if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) { | ||
| 213 | /* | ||
| 214 | * Increment subfolder count. Note, the value is only meaningful | ||
| 215 | * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set. | ||
| 216 | */ | ||
| 217 | HFSPLUS_I(dir)->subfolders++; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | static void hfsplus_subfolders_dec(struct inode *dir) | ||
| 222 | { | ||
| 223 | struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); | ||
| 224 | |||
| 225 | if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) { | ||
| 226 | /* | ||
| 227 | * Decrement subfolder count. Note, the value is only meaningful | ||
| 228 | * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set. | ||
| 229 | * | ||
| 230 | * Check for zero. Some subfolders may have been created | ||
| 231 | * by an implementation ignorant of this counter. | ||
| 232 | */ | ||
| 233 | if (HFSPLUS_I(dir)->subfolders) | ||
| 234 | HFSPLUS_I(dir)->subfolders--; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 206 | int hfsplus_create_cat(u32 cnid, struct inode *dir, | 238 | int hfsplus_create_cat(u32 cnid, struct inode *dir, |
| 207 | struct qstr *str, struct inode *inode) | 239 | struct qstr *str, struct inode *inode) |
| 208 | { | 240 | { |
| @@ -247,6 +279,8 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir, | |||
| 247 | goto err1; | 279 | goto err1; |
| 248 | 280 | ||
| 249 | dir->i_size++; | 281 | dir->i_size++; |
| 282 | if (S_ISDIR(inode->i_mode)) | ||
| 283 | hfsplus_subfolders_inc(dir); | ||
| 250 | dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; | 284 | dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; |
| 251 | hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); | 285 | hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); |
| 252 | 286 | ||
| @@ -336,6 +370,8 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) | |||
| 336 | goto out; | 370 | goto out; |
| 337 | 371 | ||
| 338 | dir->i_size--; | 372 | dir->i_size--; |
| 373 | if (type == HFSPLUS_FOLDER) | ||
| 374 | hfsplus_subfolders_dec(dir); | ||
| 339 | dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; | 375 | dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; |
| 340 | hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); | 376 | hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); |
| 341 | 377 | ||
| @@ -380,6 +416,7 @@ int hfsplus_rename_cat(u32 cnid, | |||
| 380 | 416 | ||
| 381 | hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset, | 417 | hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset, |
| 382 | src_fd.entrylength); | 418 | src_fd.entrylength); |
| 419 | type = be16_to_cpu(entry.type); | ||
| 383 | 420 | ||
| 384 | /* create new dir entry with the data from the old entry */ | 421 | /* create new dir entry with the data from the old entry */ |
| 385 | hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name); | 422 | hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name); |
| @@ -394,6 +431,8 @@ int hfsplus_rename_cat(u32 cnid, | |||
| 394 | if (err) | 431 | if (err) |
| 395 | goto out; | 432 | goto out; |
| 396 | dst_dir->i_size++; | 433 | dst_dir->i_size++; |
| 434 | if (type == HFSPLUS_FOLDER) | ||
| 435 | hfsplus_subfolders_inc(dst_dir); | ||
| 397 | dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC; | 436 | dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC; |
| 398 | 437 | ||
| 399 | /* finally remove the old entry */ | 438 | /* finally remove the old entry */ |
| @@ -405,6 +444,8 @@ int hfsplus_rename_cat(u32 cnid, | |||
| 405 | if (err) | 444 | if (err) |
| 406 | goto out; | 445 | goto out; |
| 407 | src_dir->i_size--; | 446 | src_dir->i_size--; |
| 447 | if (type == HFSPLUS_FOLDER) | ||
| 448 | hfsplus_subfolders_dec(src_dir); | ||
| 408 | src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC; | 449 | src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC; |
| 409 | 450 | ||
| 410 | /* remove old thread entry */ | 451 | /* remove old thread entry */ |
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 08846425b67f..62d571eb69ba 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h | |||
| @@ -242,6 +242,7 @@ struct hfsplus_inode_info { | |||
| 242 | */ | 242 | */ |
| 243 | sector_t fs_blocks; | 243 | sector_t fs_blocks; |
| 244 | u8 userflags; /* BSD user file flags */ | 244 | u8 userflags; /* BSD user file flags */ |
| 245 | u32 subfolders; /* Subfolder count (HFSX only) */ | ||
| 245 | struct list_head open_dir_list; | 246 | struct list_head open_dir_list; |
| 246 | loff_t phys_size; | 247 | loff_t phys_size; |
| 247 | 248 | ||
diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h index 8ffb3a8ffe75..5a126828d85e 100644 --- a/fs/hfsplus/hfsplus_raw.h +++ b/fs/hfsplus/hfsplus_raw.h | |||
| @@ -261,7 +261,7 @@ struct hfsplus_cat_folder { | |||
| 261 | struct DInfo user_info; | 261 | struct DInfo user_info; |
| 262 | struct DXInfo finder_info; | 262 | struct DXInfo finder_info; |
| 263 | __be32 text_encoding; | 263 | __be32 text_encoding; |
| 264 | u32 reserved; | 264 | __be32 subfolders; /* Subfolder count in HFSX. Reserved in HFS+. */ |
| 265 | } __packed; | 265 | } __packed; |
| 266 | 266 | ||
| 267 | /* HFS file info (stolen from hfs.h) */ | 267 | /* HFS file info (stolen from hfs.h) */ |
| @@ -301,11 +301,13 @@ struct hfsplus_cat_file { | |||
| 301 | struct hfsplus_fork_raw rsrc_fork; | 301 | struct hfsplus_fork_raw rsrc_fork; |
| 302 | } __packed; | 302 | } __packed; |
| 303 | 303 | ||
| 304 | /* File attribute bits */ | 304 | /* File and folder flag bits */ |
| 305 | #define HFSPLUS_FILE_LOCKED 0x0001 | 305 | #define HFSPLUS_FILE_LOCKED 0x0001 |
| 306 | #define HFSPLUS_FILE_THREAD_EXISTS 0x0002 | 306 | #define HFSPLUS_FILE_THREAD_EXISTS 0x0002 |
| 307 | #define HFSPLUS_XATTR_EXISTS 0x0004 | 307 | #define HFSPLUS_XATTR_EXISTS 0x0004 |
| 308 | #define HFSPLUS_ACL_EXISTS 0x0008 | 308 | #define HFSPLUS_ACL_EXISTS 0x0008 |
| 309 | #define HFSPLUS_HAS_FOLDER_COUNT 0x0010 /* Folder has subfolder count | ||
| 310 | * (HFSX only) */ | ||
| 309 | 311 | ||
| 310 | /* HFS+ catalog thread (part of a cat_entry) */ | 312 | /* HFS+ catalog thread (part of a cat_entry) */ |
| 311 | struct hfsplus_cat_thread { | 313 | struct hfsplus_cat_thread { |
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index fa929f325f87..a4f45bd88a63 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c | |||
| @@ -375,6 +375,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode) | |||
| 375 | hip->extent_state = 0; | 375 | hip->extent_state = 0; |
| 376 | hip->flags = 0; | 376 | hip->flags = 0; |
| 377 | hip->userflags = 0; | 377 | hip->userflags = 0; |
| 378 | hip->subfolders = 0; | ||
| 378 | memset(hip->first_extents, 0, sizeof(hfsplus_extent_rec)); | 379 | memset(hip->first_extents, 0, sizeof(hfsplus_extent_rec)); |
| 379 | memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec)); | 380 | memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec)); |
| 380 | hip->alloc_blocks = 0; | 381 | hip->alloc_blocks = 0; |
| @@ -494,6 +495,10 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) | |||
| 494 | inode->i_ctime = hfsp_mt2ut(folder->attribute_mod_date); | 495 | inode->i_ctime = hfsp_mt2ut(folder->attribute_mod_date); |
| 495 | HFSPLUS_I(inode)->create_date = folder->create_date; | 496 | HFSPLUS_I(inode)->create_date = folder->create_date; |
| 496 | HFSPLUS_I(inode)->fs_blocks = 0; | 497 | HFSPLUS_I(inode)->fs_blocks = 0; |
| 498 | if (folder->flags & cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT)) { | ||
| 499 | HFSPLUS_I(inode)->subfolders = | ||
| 500 | be32_to_cpu(folder->subfolders); | ||
| 501 | } | ||
| 497 | inode->i_op = &hfsplus_dir_inode_operations; | 502 | inode->i_op = &hfsplus_dir_inode_operations; |
| 498 | inode->i_fop = &hfsplus_dir_operations; | 503 | inode->i_fop = &hfsplus_dir_operations; |
| 499 | } else if (type == HFSPLUS_FILE) { | 504 | } else if (type == HFSPLUS_FILE) { |
| @@ -566,6 +571,10 @@ int hfsplus_cat_write_inode(struct inode *inode) | |||
| 566 | folder->content_mod_date = hfsp_ut2mt(inode->i_mtime); | 571 | folder->content_mod_date = hfsp_ut2mt(inode->i_mtime); |
| 567 | folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime); | 572 | folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime); |
| 568 | folder->valence = cpu_to_be32(inode->i_size - 2); | 573 | folder->valence = cpu_to_be32(inode->i_size - 2); |
| 574 | if (folder->flags & cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT)) { | ||
| 575 | folder->subfolders = | ||
| 576 | cpu_to_be32(HFSPLUS_I(inode)->subfolders); | ||
| 577 | } | ||
| 569 | hfs_bnode_write(fd.bnode, &entry, fd.entryoffset, | 578 | hfs_bnode_write(fd.bnode, &entry, fd.entryoffset, |
| 570 | sizeof(struct hfsplus_cat_folder)); | 579 | sizeof(struct hfsplus_cat_folder)); |
| 571 | } else if (HFSPLUS_IS_RSRC(inode)) { | 580 | } else if (HFSPLUS_IS_RSRC(inode)) { |
