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>
This commit is contained in:
parent
5394223236
commit
d7d673a591
|
@ -103,6 +103,8 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry,
|
||||||
folder = &entry->folder;
|
folder = &entry->folder;
|
||||||
memset(folder, 0, sizeof(*folder));
|
memset(folder, 0, sizeof(*folder));
|
||||||
folder->type = cpu_to_be16(HFSPLUS_FOLDER);
|
folder->type = cpu_to_be16(HFSPLUS_FOLDER);
|
||||||
|
if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags))
|
||||||
|
folder->flags |= cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT);
|
||||||
folder->id = cpu_to_be32(inode->i_ino);
|
folder->id = cpu_to_be32(inode->i_ino);
|
||||||
HFSPLUS_I(inode)->create_date =
|
HFSPLUS_I(inode)->create_date =
|
||||||
folder->create_date =
|
folder->create_date =
|
||||||
|
@ -203,6 +205,36 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid,
|
||||||
return hfs_brec_find(fd, hfs_find_rec_by_key);
|
return hfs_brec_find(fd, hfs_find_rec_by_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hfsplus_subfolders_inc(struct inode *dir)
|
||||||
|
{
|
||||||
|
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
|
||||||
|
|
||||||
|
if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
|
||||||
|
/*
|
||||||
|
* Increment subfolder count. Note, the value is only meaningful
|
||||||
|
* for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
|
||||||
|
*/
|
||||||
|
HFSPLUS_I(dir)->subfolders++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hfsplus_subfolders_dec(struct inode *dir)
|
||||||
|
{
|
||||||
|
struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
|
||||||
|
|
||||||
|
if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
|
||||||
|
/*
|
||||||
|
* Decrement subfolder count. Note, the value is only meaningful
|
||||||
|
* for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
|
||||||
|
*
|
||||||
|
* Check for zero. Some subfolders may have been created
|
||||||
|
* by an implementation ignorant of this counter.
|
||||||
|
*/
|
||||||
|
if (HFSPLUS_I(dir)->subfolders)
|
||||||
|
HFSPLUS_I(dir)->subfolders--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int hfsplus_create_cat(u32 cnid, struct inode *dir,
|
int hfsplus_create_cat(u32 cnid, struct inode *dir,
|
||||||
struct qstr *str, struct inode *inode)
|
struct qstr *str, struct inode *inode)
|
||||||
{
|
{
|
||||||
|
@ -247,6 +279,8 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
|
||||||
goto err1;
|
goto err1;
|
||||||
|
|
||||||
dir->i_size++;
|
dir->i_size++;
|
||||||
|
if (S_ISDIR(inode->i_mode))
|
||||||
|
hfsplus_subfolders_inc(dir);
|
||||||
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
|
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
|
||||||
hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
|
hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
|
||||||
|
|
||||||
|
@ -336,6 +370,8 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
dir->i_size--;
|
dir->i_size--;
|
||||||
|
if (type == HFSPLUS_FOLDER)
|
||||||
|
hfsplus_subfolders_dec(dir);
|
||||||
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
|
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
|
||||||
hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
|
hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
|
||||||
|
|
||||||
|
@ -380,6 +416,7 @@ int hfsplus_rename_cat(u32 cnid,
|
||||||
|
|
||||||
hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
|
hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
|
||||||
src_fd.entrylength);
|
src_fd.entrylength);
|
||||||
|
type = be16_to_cpu(entry.type);
|
||||||
|
|
||||||
/* create new dir entry with the data from the old entry */
|
/* create new dir entry with the data from the old entry */
|
||||||
hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name);
|
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,
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
dst_dir->i_size++;
|
dst_dir->i_size++;
|
||||||
|
if (type == HFSPLUS_FOLDER)
|
||||||
|
hfsplus_subfolders_inc(dst_dir);
|
||||||
dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
|
dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
|
||||||
|
|
||||||
/* finally remove the old entry */
|
/* finally remove the old entry */
|
||||||
|
@ -405,6 +444,8 @@ int hfsplus_rename_cat(u32 cnid,
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
src_dir->i_size--;
|
src_dir->i_size--;
|
||||||
|
if (type == HFSPLUS_FOLDER)
|
||||||
|
hfsplus_subfolders_dec(src_dir);
|
||||||
src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
|
src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
|
||||||
|
|
||||||
/* remove old thread entry */
|
/* remove old thread entry */
|
||||||
|
|
|
@ -242,6 +242,7 @@ struct hfsplus_inode_info {
|
||||||
*/
|
*/
|
||||||
sector_t fs_blocks;
|
sector_t fs_blocks;
|
||||||
u8 userflags; /* BSD user file flags */
|
u8 userflags; /* BSD user file flags */
|
||||||
|
u32 subfolders; /* Subfolder count (HFSX only) */
|
||||||
struct list_head open_dir_list;
|
struct list_head open_dir_list;
|
||||||
loff_t phys_size;
|
loff_t phys_size;
|
||||||
|
|
||||||
|
|
|
@ -261,7 +261,7 @@ struct hfsplus_cat_folder {
|
||||||
struct DInfo user_info;
|
struct DInfo user_info;
|
||||||
struct DXInfo finder_info;
|
struct DXInfo finder_info;
|
||||||
__be32 text_encoding;
|
__be32 text_encoding;
|
||||||
u32 reserved;
|
__be32 subfolders; /* Subfolder count in HFSX. Reserved in HFS+. */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
/* HFS file info (stolen from hfs.h) */
|
/* HFS file info (stolen from hfs.h) */
|
||||||
|
@ -301,11 +301,13 @@ struct hfsplus_cat_file {
|
||||||
struct hfsplus_fork_raw rsrc_fork;
|
struct hfsplus_fork_raw rsrc_fork;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
/* File attribute bits */
|
/* File and folder flag bits */
|
||||||
#define HFSPLUS_FILE_LOCKED 0x0001
|
#define HFSPLUS_FILE_LOCKED 0x0001
|
||||||
#define HFSPLUS_FILE_THREAD_EXISTS 0x0002
|
#define HFSPLUS_FILE_THREAD_EXISTS 0x0002
|
||||||
#define HFSPLUS_XATTR_EXISTS 0x0004
|
#define HFSPLUS_XATTR_EXISTS 0x0004
|
||||||
#define HFSPLUS_ACL_EXISTS 0x0008
|
#define HFSPLUS_ACL_EXISTS 0x0008
|
||||||
|
#define HFSPLUS_HAS_FOLDER_COUNT 0x0010 /* Folder has subfolder count
|
||||||
|
* (HFSX only) */
|
||||||
|
|
||||||
/* HFS+ catalog thread (part of a cat_entry) */
|
/* HFS+ catalog thread (part of a cat_entry) */
|
||||||
struct hfsplus_cat_thread {
|
struct hfsplus_cat_thread {
|
||||||
|
|
|
@ -375,6 +375,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
|
||||||
hip->extent_state = 0;
|
hip->extent_state = 0;
|
||||||
hip->flags = 0;
|
hip->flags = 0;
|
||||||
hip->userflags = 0;
|
hip->userflags = 0;
|
||||||
|
hip->subfolders = 0;
|
||||||
memset(hip->first_extents, 0, sizeof(hfsplus_extent_rec));
|
memset(hip->first_extents, 0, sizeof(hfsplus_extent_rec));
|
||||||
memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
|
memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
|
||||||
hip->alloc_blocks = 0;
|
hip->alloc_blocks = 0;
|
||||||
|
@ -494,6 +495,10 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
|
||||||
inode->i_ctime = hfsp_mt2ut(folder->attribute_mod_date);
|
inode->i_ctime = hfsp_mt2ut(folder->attribute_mod_date);
|
||||||
HFSPLUS_I(inode)->create_date = folder->create_date;
|
HFSPLUS_I(inode)->create_date = folder->create_date;
|
||||||
HFSPLUS_I(inode)->fs_blocks = 0;
|
HFSPLUS_I(inode)->fs_blocks = 0;
|
||||||
|
if (folder->flags & cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT)) {
|
||||||
|
HFSPLUS_I(inode)->subfolders =
|
||||||
|
be32_to_cpu(folder->subfolders);
|
||||||
|
}
|
||||||
inode->i_op = &hfsplus_dir_inode_operations;
|
inode->i_op = &hfsplus_dir_inode_operations;
|
||||||
inode->i_fop = &hfsplus_dir_operations;
|
inode->i_fop = &hfsplus_dir_operations;
|
||||||
} else if (type == HFSPLUS_FILE) {
|
} else if (type == HFSPLUS_FILE) {
|
||||||
|
@ -566,6 +571,10 @@ int hfsplus_cat_write_inode(struct inode *inode)
|
||||||
folder->content_mod_date = hfsp_ut2mt(inode->i_mtime);
|
folder->content_mod_date = hfsp_ut2mt(inode->i_mtime);
|
||||||
folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime);
|
folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime);
|
||||||
folder->valence = cpu_to_be32(inode->i_size - 2);
|
folder->valence = cpu_to_be32(inode->i_size - 2);
|
||||||
|
if (folder->flags & cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT)) {
|
||||||
|
folder->subfolders =
|
||||||
|
cpu_to_be32(HFSPLUS_I(inode)->subfolders);
|
||||||
|
}
|
||||||
hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
|
hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
|
||||||
sizeof(struct hfsplus_cat_folder));
|
sizeof(struct hfsplus_cat_folder));
|
||||||
} else if (HFSPLUS_IS_RSRC(inode)) {
|
} else if (HFSPLUS_IS_RSRC(inode)) {
|
||||||
|
|
Loading…
Reference in New Issue