summaryrefslogtreecommitdiff
path: root/fs/hfs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-16 12:27:23 +0530
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-16 12:27:23 +0530
commitd29fd593e6836c96c6fd6df2b0cc6a47dda21b74 (patch)
tree27755b12b63354ab5955c5d9863fc815adf31b4b /fs/hfs
parent6f60a6033c7dfd02ba8d94111965189c9a7866d4 (diff)
parent7fde7e806657fbe0d33f489521b488eed94f9b39 (diff)
Merge tag 'hfs-v7.2-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs
Pull hfs/hfsplus updates from Viacheslav Dubeyko: "Several fixes in HFS/HFS+ of syzbot reported issues and HFS//HFS+ fixes of xfstests failures. - fix a null-ptr-deref issue reported by syzbot (Edward Adam Davis) If the attributes file is not loaded during system mount hfsplus_create_attributes_file can dereference a NULL pointer. Also, add a b-tree node size check in hfs_btree_open() with the goal to prevent an uninit-value bug reported by syzbot for the case of corrupted HFS+ image. - fix __hfs_bnode_create() by using kzalloc_flex() instead of kzalloc() (Rosen Penev) - fix early return in hfs_bnode_read() (Tristan Madani) hfs_bnode_read() can return early without writing to the output buffer when is_bnode_offset_valid() fails or when check_and_correct_requested_ length() corrects the length to zero. Callers such as hfs_bnode_read_ u16() and hfs_bnode_read_u8() pass stack-allocated buffers and use the result unconditionally, leading to KMSAN uninit-value reports. The rest fix (1) generic/637, generic/729 issue for the case of HFS+ file system, (2) generic/003, generic/637 for the case of HFS file system" * tag 'hfs-v7.2-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs: hfs: rework hfsplus_readdir() logic hfs: disable the updating of file access times (atime) hfs: fix incorrect inode ID assignment in hfs_new_inode() hfsplus: rework hfsplus_readdir() logic hfs/hfsplus: zero-initialize buffer in hfs_bnode_read hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length hfsplus: Add a sanity check for btree node size hfsplus: fix issue of direct writes beyond end-of-file hfs/hfxplus: use kzalloc_flex() hfsplus: Remove the duplicate attr inode dirty marking action
Diffstat (limited to 'fs/hfs')
-rw-r--r--fs/hfs/bnode.c10
-rw-r--r--fs/hfs/catalog.c9
-rw-r--r--fs/hfs/dir.c37
-rw-r--r--fs/hfs/hfs.h3
-rw-r--r--fs/hfs/hfs_fs.h2
-rw-r--r--fs/hfs/inode.c21
-rw-r--r--fs/hfs/super.c2
7 files changed, 36 insertions, 48 deletions
diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c
index 13d58c51fc46..da8e5342c91c 100644
--- a/fs/hfs/bnode.c
+++ b/fs/hfs/bnode.c
@@ -41,7 +41,7 @@ u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
node_size = node->tree->node_size;
- if ((off + len) > node_size) {
+ if ((u64)off + len > node_size) {
u32 new_len = node_size - off;
pr_err("requested length has been corrected: "
@@ -64,6 +64,8 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
u32 bytes_read;
u32 bytes_to_read;
+ memset(buf, 0, len);
+
if (!is_bnode_offset_valid(node, off))
return;
@@ -344,7 +346,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
struct hfs_bnode *node, *node2;
struct address_space *mapping;
struct page *page;
- int size, block, i, hash;
+ int block, i, hash;
loff_t off;
if (cnid >= tree->node_count) {
@@ -352,9 +354,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
return NULL;
}
- size = sizeof(struct hfs_bnode) + tree->pages_per_bnode *
- sizeof(struct page *);
- node = kzalloc(size, GFP_KERNEL);
+ node = kzalloc_flex(*node, page, tree->pages_per_bnode, GFP_KERNEL);
if (!node)
return NULL;
node->tree = tree;
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c
index 7f5339ee57c1..1bfa36d71e24 100644
--- a/fs/hfs/catalog.c
+++ b/fs/hfs/catalog.c
@@ -340,7 +340,6 @@ int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str)
{
struct super_block *sb;
struct hfs_find_data fd;
- struct hfs_readdir_data *rd;
int res, type;
hfs_dbg("name %s, cnid %u\n", str ? str->name : NULL, cnid);
@@ -366,14 +365,6 @@ int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str)
}
}
- /* we only need to take spinlock for exclusion with ->release() */
- spin_lock(&HFS_I(dir)->open_dir_lock);
- list_for_each_entry(rd, &HFS_I(dir)->open_dir_list, list) {
- if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
- rd->file->f_pos--;
- }
- spin_unlock(&HFS_I(dir)->open_dir_lock);
-
res = hfs_brec_remove(&fd);
if (res)
goto out;
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index c4c6e1623f55..e13450bb933e 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -97,7 +97,15 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
}
if (ctx->pos >= inode->i_size)
goto out;
- err = hfs_brec_goto(&fd, ctx->pos - 1);
+ rd = file->private_data;
+ if (rd && rd->pos == ctx->pos) {
+ memcpy(fd.search_key, &rd->key, sizeof(struct hfs_cat_key));
+ err = hfs_brec_find(&fd);
+ if (err == -ENOENT)
+ err = hfs_brec_goto(&fd, 1);
+ } else {
+ err = hfs_brec_goto(&fd, ctx->pos - 1);
+ }
if (err)
goto out;
@@ -146,7 +154,6 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
if (err)
goto out;
}
- rd = file->private_data;
if (!rd) {
rd = kmalloc_obj(struct hfs_readdir_data);
if (!rd) {
@@ -154,15 +161,8 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
goto out;
}
file->private_data = rd;
- rd->file = file;
- spin_lock(&HFS_I(inode)->open_dir_lock);
- list_add(&rd->list, &HFS_I(inode)->open_dir_list);
- spin_unlock(&HFS_I(inode)->open_dir_lock);
}
- /*
- * Can be done after the list insertion; exclusion with
- * hfs_delete_cat() is provided by directory lock.
- */
+ rd->pos = ctx->pos;
memcpy(&rd->key, &fd.key->cat, sizeof(struct hfs_cat_key));
out:
hfs_find_exit(&fd);
@@ -171,13 +171,7 @@ out:
static int hfs_dir_release(struct inode *inode, struct file *file)
{
- struct hfs_readdir_data *rd = file->private_data;
- if (rd) {
- spin_lock(&HFS_I(inode)->open_dir_lock);
- list_del(&rd->list);
- spin_unlock(&HFS_I(inode)->open_dir_lock);
- kfree(rd);
- }
+ kfree(file->private_data);
return 0;
}
@@ -306,10 +300,15 @@ static int hfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
res = hfs_cat_move(d_inode(old_dentry)->i_ino,
old_dir, &old_dentry->d_name,
new_dir, &new_dentry->d_name);
- if (!res)
+ if (!res) {
+ struct inode *inode = d_inode(old_dentry);
+
hfs_cat_build_key(old_dir->i_sb,
- (btree_key *)&HFS_I(d_inode(old_dentry))->cat_key,
+ (btree_key *)&HFS_I(inode)->cat_key,
new_dir->i_ino, &new_dentry->d_name);
+ inode_set_ctime_current(inode);
+ mark_inode_dirty(inode);
+ }
return res;
}
diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h
index 3f2293ff6fdd..2e958c46bc0a 100644
--- a/fs/hfs/hfs.h
+++ b/fs/hfs/hfs.h
@@ -14,8 +14,7 @@
/*======== Data structures kept in memory ========*/
struct hfs_readdir_data {
- struct list_head list;
- struct file *file;
+ loff_t pos;
struct hfs_cat_key key;
};
diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
index 1b23448c9a48..f3624514fcb0 100644
--- a/fs/hfs/hfs_fs.h
+++ b/fs/hfs/hfs_fs.h
@@ -36,8 +36,6 @@ struct hfs_inode_info {
struct hfs_cat_key cat_key;
- struct list_head open_dir_list;
- spinlock_t open_dir_lock;
struct inode *rsrc_inode;
struct mutex extents_lock;
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index f41cc261684d..ac4a9055c5c0 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -196,8 +196,6 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
err = -ERANGE;
mutex_init(&HFS_I(inode)->extents_lock);
- INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
- spin_lock_init(&HFS_I(inode)->open_dir_lock);
hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name);
next_id = atomic64_inc_return(&HFS_SB(sb)->next_id);
if (next_id > U32_MAX) {
@@ -205,7 +203,7 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
pr_err("cannot create new inode: next CNID exceeds limit\n");
goto out_discard;
}
- inode->i_ino = (u32)next_id;
+ inode->i_ino = (u32)next_id - 1;
inode->i_mode = mode;
inode->i_uid = current_fsuid();
inode->i_gid = current_fsgid();
@@ -349,12 +347,11 @@ static int hfs_read_inode(struct inode *inode, void *data)
struct hfs_iget_data *idata = data;
struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
hfs_cat_rec *rec;
+ struct timespec64 mtime;
HFS_I(inode)->flags = 0;
HFS_I(inode)->rsrc_inode = NULL;
mutex_init(&HFS_I(inode)->extents_lock);
- INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
- spin_lock_init(&HFS_I(inode)->open_dir_lock);
/* Initialize the inode */
inode->i_uid = hsb->s_uid;
@@ -384,8 +381,10 @@ static int hfs_read_inode(struct inode *inode, void *data)
inode->i_mode |= S_IWUGO;
inode->i_mode &= ~hsb->s_file_umask;
inode->i_mode |= S_IFREG;
- inode_set_mtime_to_ts(inode,
- inode_set_atime_to_ts(inode, inode_set_ctime_to_ts(inode, hfs_m_to_utime(rec->file.MdDat))));
+ mtime = hfs_m_to_utime(rec->file.MdDat);
+ inode_set_ctime_to_ts(inode, mtime);
+ inode_set_atime_to_ts(inode, mtime);
+ inode_set_mtime_to_ts(inode, mtime);
inode->i_op = &hfs_file_inode_operations;
inode->i_fop = &hfs_file_operations;
inode->i_mapping->a_ops = &hfs_aops;
@@ -395,8 +394,10 @@ static int hfs_read_inode(struct inode *inode, void *data)
inode->i_size = be16_to_cpu(rec->dir.Val) + 2;
HFS_I(inode)->fs_blocks = 0;
inode->i_mode = S_IFDIR | (S_IRWXUGO & ~hsb->s_dir_umask);
- inode_set_mtime_to_ts(inode,
- inode_set_atime_to_ts(inode, inode_set_ctime_to_ts(inode, hfs_m_to_utime(rec->dir.MdDat))));
+ mtime = hfs_m_to_utime(rec->dir.MdDat);
+ inode_set_ctime_to_ts(inode, mtime);
+ inode_set_atime_to_ts(inode, mtime);
+ inode_set_mtime_to_ts(inode, mtime);
inode->i_op = &hfs_dir_inode_operations;
inode->i_fop = &hfs_dir_operations;
break;
@@ -666,7 +667,7 @@ int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
truncate_setsize(inode, attr->ia_size);
hfs_file_truncate(inode);
- simple_inode_init_ts(inode);
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
}
setattr_copy(&nop_mnt_idmap, inode, attr);
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index a4f2a2bfa6d3..a466c401f6bb 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -338,7 +338,7 @@ static int hfs_fill_super(struct super_block *sb, struct fs_context *fc)
sbi->sb = sb;
sb->s_op = &hfs_super_operations;
sb->s_xattr = hfs_xattr_handlers;
- sb->s_flags |= SB_NODIRATIME;
+ sb->s_flags |= SB_NOATIME | SB_NODIRATIME;
mutex_init(&sbi->bitmap_lock);
res = hfs_mdb_get(sb);