Skip to content

Commit

Permalink
hfsplus: add HFSX subfolder count support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sergei Antonov authored and Linus Torvalds committed Mar 11, 2014
1 parent 5394223 commit d7d673a
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 2 deletions.
41 changes: 41 additions & 0 deletions fs/hfsplus/catalog.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry,
folder = &entry->folder;
memset(folder, 0, sizeof(*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);
HFSPLUS_I(inode)->create_date =
folder->create_date =
Expand Down Expand Up @@ -203,6 +205,36 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid,
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,
struct qstr *str, struct inode *inode)
{
Expand Down Expand Up @@ -247,6 +279,8 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
goto err1;

dir->i_size++;
if (S_ISDIR(inode->i_mode))
hfsplus_subfolders_inc(dir);
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);

Expand Down Expand Up @@ -336,6 +370,8 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
goto out;

dir->i_size--;
if (type == HFSPLUS_FOLDER)
hfsplus_subfolders_dec(dir);
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);

Expand Down Expand Up @@ -380,6 +416,7 @@ int hfsplus_rename_cat(u32 cnid,

hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
src_fd.entrylength);
type = be16_to_cpu(entry.type);

/* 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);
Expand All @@ -394,6 +431,8 @@ int hfsplus_rename_cat(u32 cnid,
if (err)
goto out;
dst_dir->i_size++;
if (type == HFSPLUS_FOLDER)
hfsplus_subfolders_inc(dst_dir);
dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;

/* finally remove the old entry */
Expand All @@ -405,6 +444,8 @@ int hfsplus_rename_cat(u32 cnid,
if (err)
goto out;
src_dir->i_size--;
if (type == HFSPLUS_FOLDER)
hfsplus_subfolders_dec(src_dir);
src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;

/* remove old thread entry */
Expand Down
1 change: 1 addition & 0 deletions fs/hfsplus/hfsplus_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ struct hfsplus_inode_info {
*/
sector_t fs_blocks;
u8 userflags; /* BSD user file flags */
u32 subfolders; /* Subfolder count (HFSX only) */
struct list_head open_dir_list;
loff_t phys_size;

Expand Down
6 changes: 4 additions & 2 deletions fs/hfsplus/hfsplus_raw.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ struct hfsplus_cat_folder {
struct DInfo user_info;
struct DXInfo finder_info;
__be32 text_encoding;
u32 reserved;
__be32 subfolders; /* Subfolder count in HFSX. Reserved in HFS+. */
} __packed;

/* HFS file info (stolen from hfs.h) */
Expand Down Expand Up @@ -301,11 +301,13 @@ struct hfsplus_cat_file {
struct hfsplus_fork_raw rsrc_fork;
} __packed;

/* File attribute bits */
/* File and folder flag bits */
#define HFSPLUS_FILE_LOCKED 0x0001
#define HFSPLUS_FILE_THREAD_EXISTS 0x0002
#define HFSPLUS_XATTR_EXISTS 0x0004
#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) */
struct hfsplus_cat_thread {
Expand Down
9 changes: 9 additions & 0 deletions fs/hfsplus/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
hip->extent_state = 0;
hip->flags = 0;
hip->userflags = 0;
hip->subfolders = 0;
memset(hip->first_extents, 0, sizeof(hfsplus_extent_rec));
memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
hip->alloc_blocks = 0;
Expand Down Expand Up @@ -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);
HFSPLUS_I(inode)->create_date = folder->create_date;
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_fop = &hfsplus_dir_operations;
} else if (type == HFSPLUS_FILE) {
Expand Down Expand Up @@ -566,6 +571,10 @@ int hfsplus_cat_write_inode(struct inode *inode)
folder->content_mod_date = hfsp_ut2mt(inode->i_mtime);
folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime);
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,
sizeof(struct hfsplus_cat_folder));
} else if (HFSPLUS_IS_RSRC(inode)) {
Expand Down

0 comments on commit d7d673a

Please sign in to comment.