Skip to content

Commit 4fb7d86

Browse files
committed
Merge tag 'hfs-v7.0-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs
Pull hfs/hfsplus updates from Viacheslav Dubeyko: "This pull request contains several fixes of syzbot reported issues and HFS+ fixes of xfstests failures. - fix an issue reported by syzbot triggering BUG_ON() in the case of corrupted superblock, replacing the BUG_ON()s with proper error handling (Jori Koolstra) - fix memory leaks in the mount logic of HFS/HFS+ file systems. When HFS/HFS+ were converted to the new mount api a bug was introduced by changing the allocation pattern of sb->s_fs_info (Mehdi Ben Hadj Khelifa) - fix hfs_bnode_create() by returning ERR_PTR(-EEXIST) instead of the node pointer when it's already hashed. This avoids a double unload_nls() on mount failure (suggested by Shardul Bankar) - set inode's mode as regular file for system inodes (Tetsuo Handa) The rest fix failures in generic/020, generic/037, generic/062, generic/480, and generic/498 xfstests for the case of HFS+ file system. Currently, only 30 xfstests' test-cases experience failures for HFS+ file system (initially, it was around 100 failed xfstests)" * tag 'hfs-v7.0-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs: hfsplus: avoid double unload_nls() on mount failure hfsplus: fix warning issue in inode.c hfsplus: fix generic/062 xfstests failure hfsplus: fix generic/037 xfstests failure hfsplus: pretend special inodes as regular files hfsplus: return error when node already exists in hfs_bnode_create hfs: Replace BUG_ON with error handling for CNID count checks hfsplus: fix generic/020 xfstests failure hfsplus: fix volume corruption issue for generic/498 hfsplus: fix volume corruption issue for generic/480 hfsplus: ensure sb->s_fs_info is always cleaned up hfs: ensure sb->s_fs_info is always cleaned up
2 parents d10a88c + ebebb04 commit 4fb7d86

12 files changed

Lines changed: 407 additions & 122 deletions

File tree

fs/hfs/dir.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ static int hfs_create(struct mnt_idmap *idmap, struct inode *dir,
196196
int res;
197197

198198
inode = hfs_new_inode(dir, &dentry->d_name, mode);
199-
if (!inode)
200-
return -ENOMEM;
199+
if (IS_ERR(inode))
200+
return PTR_ERR(inode);
201201

202202
res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode);
203203
if (res) {
@@ -226,8 +226,8 @@ static struct dentry *hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
226226
int res;
227227

228228
inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode);
229-
if (!inode)
230-
return ERR_PTR(-ENOMEM);
229+
if (IS_ERR(inode))
230+
return ERR_CAST(inode);
231231

232232
res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode);
233233
if (res) {
@@ -254,11 +254,18 @@ static struct dentry *hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
254254
*/
255255
static int hfs_remove(struct inode *dir, struct dentry *dentry)
256256
{
257+
struct super_block *sb = dir->i_sb;
257258
struct inode *inode = d_inode(dentry);
258259
int res;
259260

260261
if (S_ISDIR(inode->i_mode) && inode->i_size != 2)
261262
return -ENOTEMPTY;
263+
264+
if (unlikely(!is_hfs_cnid_counts_valid(sb))) {
265+
pr_err("cannot remove file/folder\n");
266+
return -ERANGE;
267+
}
268+
262269
res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
263270
if (res)
264271
return res;

fs/hfs/hfs_fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ extern void hfs_delete_inode(struct inode *inode);
199199
extern const struct xattr_handler * const hfs_xattr_handlers[];
200200

201201
/* mdb.c */
202+
extern bool is_hfs_cnid_counts_valid(struct super_block *sb);
202203
extern int hfs_mdb_get(struct super_block *sb);
203204
extern void hfs_mdb_commit(struct super_block *sb);
204205
extern void hfs_mdb_close(struct super_block *sb);

fs/hfs/inode.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,16 +187,23 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
187187
s64 next_id;
188188
s64 file_count;
189189
s64 folder_count;
190+
int err = -ENOMEM;
190191

191192
if (!inode)
192-
return NULL;
193+
goto out_err;
194+
195+
err = -ERANGE;
193196

194197
mutex_init(&HFS_I(inode)->extents_lock);
195198
INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
196199
spin_lock_init(&HFS_I(inode)->open_dir_lock);
197200
hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name);
198201
next_id = atomic64_inc_return(&HFS_SB(sb)->next_id);
199-
BUG_ON(next_id > U32_MAX);
202+
if (next_id > U32_MAX) {
203+
atomic64_dec(&HFS_SB(sb)->next_id);
204+
pr_err("cannot create new inode: next CNID exceeds limit\n");
205+
goto out_discard;
206+
}
200207
inode->i_ino = (u32)next_id;
201208
inode->i_mode = mode;
202209
inode->i_uid = current_fsuid();
@@ -210,7 +217,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
210217
if (S_ISDIR(mode)) {
211218
inode->i_size = 2;
212219
folder_count = atomic64_inc_return(&HFS_SB(sb)->folder_count);
213-
BUG_ON(folder_count > U32_MAX);
220+
if (folder_count> U32_MAX) {
221+
atomic64_dec(&HFS_SB(sb)->folder_count);
222+
pr_err("cannot create new inode: folder count exceeds limit\n");
223+
goto out_discard;
224+
}
214225
if (dir->i_ino == HFS_ROOT_CNID)
215226
HFS_SB(sb)->root_dirs++;
216227
inode->i_op = &hfs_dir_inode_operations;
@@ -220,7 +231,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
220231
} else if (S_ISREG(mode)) {
221232
HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks;
222233
file_count = atomic64_inc_return(&HFS_SB(sb)->file_count);
223-
BUG_ON(file_count > U32_MAX);
234+
if (file_count > U32_MAX) {
235+
atomic64_dec(&HFS_SB(sb)->file_count);
236+
pr_err("cannot create new inode: file count exceeds limit\n");
237+
goto out_discard;
238+
}
224239
if (dir->i_ino == HFS_ROOT_CNID)
225240
HFS_SB(sb)->root_files++;
226241
inode->i_op = &hfs_file_inode_operations;
@@ -244,6 +259,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
244259
hfs_mark_mdb_dirty(sb);
245260

246261
return inode;
262+
263+
out_discard:
264+
iput(inode);
265+
out_err:
266+
return ERR_PTR(err);
247267
}
248268

249269
void hfs_delete_inode(struct inode *inode)
@@ -252,7 +272,6 @@ void hfs_delete_inode(struct inode *inode)
252272

253273
hfs_dbg("ino %lu\n", inode->i_ino);
254274
if (S_ISDIR(inode->i_mode)) {
255-
BUG_ON(atomic64_read(&HFS_SB(sb)->folder_count) > U32_MAX);
256275
atomic64_dec(&HFS_SB(sb)->folder_count);
257276
if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID))
258277
HFS_SB(sb)->root_dirs--;
@@ -261,7 +280,6 @@ void hfs_delete_inode(struct inode *inode)
261280
return;
262281
}
263282

264-
BUG_ON(atomic64_read(&HFS_SB(sb)->file_count) > U32_MAX);
265283
atomic64_dec(&HFS_SB(sb)->file_count);
266284
if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID))
267285
HFS_SB(sb)->root_files--;

fs/hfs/mdb.c

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,27 @@ static int hfs_get_last_session(struct super_block *sb,
6464
return 0;
6565
}
6666

67+
bool is_hfs_cnid_counts_valid(struct super_block *sb)
68+
{
69+
struct hfs_sb_info *sbi = HFS_SB(sb);
70+
bool corrupted = false;
71+
72+
if (unlikely(atomic64_read(&sbi->next_id) > U32_MAX)) {
73+
pr_warn("next CNID exceeds limit\n");
74+
corrupted = true;
75+
}
76+
if (unlikely(atomic64_read(&sbi->file_count) > U32_MAX)) {
77+
pr_warn("file count exceeds limit\n");
78+
corrupted = true;
79+
}
80+
if (unlikely(atomic64_read(&sbi->folder_count) > U32_MAX)) {
81+
pr_warn("folder count exceeds limit\n");
82+
corrupted = true;
83+
}
84+
85+
return !corrupted;
86+
}
87+
6788
/*
6889
* hfs_mdb_get()
6990
*
@@ -92,7 +113,7 @@ int hfs_mdb_get(struct super_block *sb)
92113
/* See if this is an HFS filesystem */
93114
bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
94115
if (!bh)
95-
goto out;
116+
return -EIO;
96117

97118
if (mdb->drSigWord == cpu_to_be16(HFS_SUPER_MAGIC))
98119
break;
@@ -102,13 +123,14 @@ int hfs_mdb_get(struct super_block *sb)
102123
* (should do this only for cdrom/loop though)
103124
*/
104125
if (hfs_part_find(sb, &part_start, &part_size))
105-
goto out;
126+
return -EIO;
106127
}
107128

108129
HFS_SB(sb)->alloc_blksz = size = be32_to_cpu(mdb->drAlBlkSiz);
109130
if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
110131
pr_err("bad allocation block size %d\n", size);
111-
goto out_bh;
132+
brelse(bh);
133+
return -EIO;
112134
}
113135

114136
size = min(HFS_SB(sb)->alloc_blksz, (u32)PAGE_SIZE);
@@ -125,14 +147,16 @@ int hfs_mdb_get(struct super_block *sb)
125147
brelse(bh);
126148
if (!sb_set_blocksize(sb, size)) {
127149
pr_err("unable to set blocksize to %u\n", size);
128-
goto out;
150+
return -EIO;
129151
}
130152

131153
bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
132154
if (!bh)
133-
goto out;
134-
if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC))
135-
goto out_bh;
155+
return -EIO;
156+
if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC)) {
157+
brelse(bh);
158+
return -EIO;
159+
}
136160

137161
HFS_SB(sb)->mdb_bh = bh;
138162
HFS_SB(sb)->mdb = mdb;
@@ -156,6 +180,11 @@ int hfs_mdb_get(struct super_block *sb)
156180
atomic64_set(&HFS_SB(sb)->file_count, be32_to_cpu(mdb->drFilCnt));
157181
atomic64_set(&HFS_SB(sb)->folder_count, be32_to_cpu(mdb->drDirCnt));
158182

183+
if (!is_hfs_cnid_counts_valid(sb)) {
184+
pr_warn("filesystem possibly corrupted, running fsck.hfs is recommended. Mounting read-only.\n");
185+
sb->s_flags |= SB_RDONLY;
186+
}
187+
159188
/* TRY to get the alternate (backup) MDB. */
160189
sect = part_start + part_size - 2;
161190
bh = sb_bread512(sb, sect, mdb2);
@@ -174,7 +203,7 @@ int hfs_mdb_get(struct super_block *sb)
174203

175204
HFS_SB(sb)->bitmap = kzalloc(8192, GFP_KERNEL);
176205
if (!HFS_SB(sb)->bitmap)
177-
goto out;
206+
return -EIO;
178207

179208
/* read in the bitmap */
180209
block = be16_to_cpu(mdb->drVBMSt) + part_start;
@@ -185,7 +214,7 @@ int hfs_mdb_get(struct super_block *sb)
185214
bh = sb_bread(sb, off >> sb->s_blocksize_bits);
186215
if (!bh) {
187216
pr_err("unable to read volume bitmap\n");
188-
goto out;
217+
return -EIO;
189218
}
190219
off2 = off & (sb->s_blocksize - 1);
191220
len = min((int)sb->s_blocksize - off2, size);
@@ -199,17 +228,17 @@ int hfs_mdb_get(struct super_block *sb)
199228
HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp);
200229
if (!HFS_SB(sb)->ext_tree) {
201230
pr_err("unable to open extent tree\n");
202-
goto out;
231+
return -EIO;
203232
}
204233
HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp);
205234
if (!HFS_SB(sb)->cat_tree) {
206235
pr_err("unable to open catalog tree\n");
207-
goto out;
236+
return -EIO;
208237
}
209238

210239
attrib = mdb->drAtrb;
211240
if (!(attrib & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) {
212-
pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended. mounting read-only.\n");
241+
pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended. Mounting read-only.\n");
213242
sb->s_flags |= SB_RDONLY;
214243
}
215244
if ((attrib & cpu_to_be16(HFS_SB_ATTRIB_SLOCK))) {
@@ -229,12 +258,6 @@ int hfs_mdb_get(struct super_block *sb)
229258
}
230259

231260
return 0;
232-
233-
out_bh:
234-
brelse(bh);
235-
out:
236-
hfs_mdb_put(sb);
237-
return -EIO;
238261
}
239262

240263
/*
@@ -273,15 +296,12 @@ void hfs_mdb_commit(struct super_block *sb)
273296
/* These parameters may have been modified, so write them back */
274297
mdb->drLsMod = hfs_mtime();
275298
mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks);
276-
BUG_ON(atomic64_read(&HFS_SB(sb)->next_id) > U32_MAX);
277299
mdb->drNxtCNID =
278300
cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->next_id));
279301
mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files);
280302
mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs);
281-
BUG_ON(atomic64_read(&HFS_SB(sb)->file_count) > U32_MAX);
282303
mdb->drFilCnt =
283304
cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->file_count));
284-
BUG_ON(atomic64_read(&HFS_SB(sb)->folder_count) > U32_MAX);
285305
mdb->drDirCnt =
286306
cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->folder_count));
287307

@@ -359,8 +379,6 @@ void hfs_mdb_close(struct super_block *sb)
359379
* Release the resources associated with the in-core MDB. */
360380
void hfs_mdb_put(struct super_block *sb)
361381
{
362-
if (!HFS_SB(sb))
363-
return;
364382
/* free the B-trees */
365383
hfs_btree_close(HFS_SB(sb)->ext_tree);
366384
hfs_btree_close(HFS_SB(sb)->cat_tree);
@@ -373,6 +391,4 @@ void hfs_mdb_put(struct super_block *sb)
373391
unload_nls(HFS_SB(sb)->nls_disk);
374392

375393
kfree(HFS_SB(sb)->bitmap);
376-
kfree(HFS_SB(sb));
377-
sb->s_fs_info = NULL;
378394
}

fs/hfs/super.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ MODULE_LICENSE("GPL");
3434

3535
static int hfs_sync_fs(struct super_block *sb, int wait)
3636
{
37+
is_hfs_cnid_counts_valid(sb);
3738
hfs_mdb_commit(sb);
3839
return 0;
3940
}
@@ -65,6 +66,8 @@ static void flush_mdb(struct work_struct *work)
6566
sbi->work_queued = 0;
6667
spin_unlock(&sbi->work_lock);
6768

69+
is_hfs_cnid_counts_valid(sb);
70+
6871
hfs_mdb_commit(sb);
6972
}
7073

@@ -431,10 +434,18 @@ static int hfs_init_fs_context(struct fs_context *fc)
431434
return 0;
432435
}
433436

437+
static void hfs_kill_super(struct super_block *sb)
438+
{
439+
struct hfs_sb_info *hsb = HFS_SB(sb);
440+
441+
kill_block_super(sb);
442+
kfree(hsb);
443+
}
444+
434445
static struct file_system_type hfs_fs_type = {
435446
.owner = THIS_MODULE,
436447
.name = "hfs",
437-
.kill_sb = kill_block_super,
448+
.kill_sb = hfs_kill_super,
438449
.fs_flags = FS_REQUIRES_DEV,
439450
.init_fs_context = hfs_init_fs_context,
440451
};

0 commit comments

Comments
 (0)