Commit 4fb7d86f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents d10a88ce ebebb04b
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -196,8 +196,8 @@ static int hfs_create(struct mnt_idmap *idmap, struct inode *dir,
	int res;

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

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

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

	res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode);
	if (res) {
@@ -254,11 +254,18 @@ static struct dentry *hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
 */
static int hfs_remove(struct inode *dir, struct dentry *dentry)
{
	struct super_block *sb = dir->i_sb;
	struct inode *inode = d_inode(dentry);
	int res;

	if (S_ISDIR(inode->i_mode) && inode->i_size != 2)
		return -ENOTEMPTY;

	if (unlikely(!is_hfs_cnid_counts_valid(sb))) {
	    pr_err("cannot remove file/folder\n");
	    return -ERANGE;
	}

	res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
	if (res)
		return res;
+1 −0
Original line number Diff line number Diff line
@@ -199,6 +199,7 @@ extern void hfs_delete_inode(struct inode *inode);
extern const struct xattr_handler * const hfs_xattr_handlers[];

/* mdb.c */
extern bool is_hfs_cnid_counts_valid(struct super_block *sb);
extern int hfs_mdb_get(struct super_block *sb);
extern void hfs_mdb_commit(struct super_block *sb);
extern void hfs_mdb_close(struct super_block *sb);
+24 −6
Original line number Diff line number Diff line
@@ -187,16 +187,23 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
	s64 next_id;
	s64 file_count;
	s64 folder_count;
	int err = -ENOMEM;

	if (!inode)
		return NULL;
		goto out_err;

	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);
	BUG_ON(next_id > U32_MAX);
	if (next_id > U32_MAX) {
		atomic64_dec(&HFS_SB(sb)->next_id);
		pr_err("cannot create new inode: next CNID exceeds limit\n");
		goto out_discard;
	}
	inode->i_ino = (u32)next_id;
	inode->i_mode = mode;
	inode->i_uid = current_fsuid();
@@ -210,7 +217,11 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
	if (S_ISDIR(mode)) {
		inode->i_size = 2;
		folder_count = atomic64_inc_return(&HFS_SB(sb)->folder_count);
		BUG_ON(folder_count > U32_MAX);
		if (folder_count> U32_MAX) {
			atomic64_dec(&HFS_SB(sb)->folder_count);
			pr_err("cannot create new inode: folder count exceeds limit\n");
			goto out_discard;
		}
		if (dir->i_ino == HFS_ROOT_CNID)
			HFS_SB(sb)->root_dirs++;
		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
	} else if (S_ISREG(mode)) {
		HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks;
		file_count = atomic64_inc_return(&HFS_SB(sb)->file_count);
		BUG_ON(file_count > U32_MAX);
		if (file_count > U32_MAX) {
			atomic64_dec(&HFS_SB(sb)->file_count);
			pr_err("cannot create new inode: file count exceeds limit\n");
			goto out_discard;
		}
		if (dir->i_ino == HFS_ROOT_CNID)
			HFS_SB(sb)->root_files++;
		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
	hfs_mark_mdb_dirty(sb);

	return inode;

	out_discard:
		iput(inode);
	out_err:
		return ERR_PTR(err);
}

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

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

	BUG_ON(atomic64_read(&HFS_SB(sb)->file_count) > U32_MAX);
	atomic64_dec(&HFS_SB(sb)->file_count);
	if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID))
		HFS_SB(sb)->root_files--;
+41 −25
Original line number Diff line number Diff line
@@ -64,6 +64,27 @@ static int hfs_get_last_session(struct super_block *sb,
	return 0;
}

bool is_hfs_cnid_counts_valid(struct super_block *sb)
{
	struct hfs_sb_info *sbi = HFS_SB(sb);
	bool corrupted = false;

	if (unlikely(atomic64_read(&sbi->next_id) > U32_MAX)) {
		pr_warn("next CNID exceeds limit\n");
		corrupted = true;
	}
	if (unlikely(atomic64_read(&sbi->file_count) > U32_MAX)) {
		pr_warn("file count exceeds limit\n");
		corrupted = true;
	}
	if (unlikely(atomic64_read(&sbi->folder_count) > U32_MAX)) {
		pr_warn("folder count exceeds limit\n");
		corrupted = true;
	}

	return !corrupted;
}

/*
 * hfs_mdb_get()
 *
@@ -92,7 +113,7 @@ int hfs_mdb_get(struct super_block *sb)
		/* See if this is an HFS filesystem */
		bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
		if (!bh)
			goto out;
			return -EIO;

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

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

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

	bh = sb_bread512(sb, part_start + HFS_MDB_BLK, mdb);
	if (!bh)
		goto out;
	if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC))
		goto out_bh;
		return -EIO;
	if (mdb->drSigWord != cpu_to_be16(HFS_SUPER_MAGIC)) {
		brelse(bh);
		return -EIO;
	}

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

	if (!is_hfs_cnid_counts_valid(sb)) {
		pr_warn("filesystem possibly corrupted, running fsck.hfs is recommended. Mounting read-only.\n");
		sb->s_flags |= SB_RDONLY;
	}

	/* TRY to get the alternate (backup) MDB. */
	sect = part_start + part_size - 2;
	bh = sb_bread512(sb, sect, mdb2);
@@ -174,7 +203,7 @@ int hfs_mdb_get(struct super_block *sb)

	HFS_SB(sb)->bitmap = kzalloc(8192, GFP_KERNEL);
	if (!HFS_SB(sb)->bitmap)
		goto out;
		return -EIO;

	/* read in the bitmap */
	block = be16_to_cpu(mdb->drVBMSt) + part_start;
@@ -185,7 +214,7 @@ int hfs_mdb_get(struct super_block *sb)
		bh = sb_bread(sb, off >> sb->s_blocksize_bits);
		if (!bh) {
			pr_err("unable to read volume bitmap\n");
			goto out;
			return -EIO;
		}
		off2 = off & (sb->s_blocksize - 1);
		len = min((int)sb->s_blocksize - off2, size);
@@ -199,17 +228,17 @@ int hfs_mdb_get(struct super_block *sb)
	HFS_SB(sb)->ext_tree = hfs_btree_open(sb, HFS_EXT_CNID, hfs_ext_keycmp);
	if (!HFS_SB(sb)->ext_tree) {
		pr_err("unable to open extent tree\n");
		goto out;
		return -EIO;
	}
	HFS_SB(sb)->cat_tree = hfs_btree_open(sb, HFS_CAT_CNID, hfs_cat_keycmp);
	if (!HFS_SB(sb)->cat_tree) {
		pr_err("unable to open catalog tree\n");
		goto out;
		return -EIO;
	}

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

	return 0;

out_bh:
	brelse(bh);
out:
	hfs_mdb_put(sb);
	return -EIO;
}

/*
@@ -273,15 +296,12 @@ void hfs_mdb_commit(struct super_block *sb)
		/* These parameters may have been modified, so write them back */
		mdb->drLsMod = hfs_mtime();
		mdb->drFreeBks = cpu_to_be16(HFS_SB(sb)->free_ablocks);
		BUG_ON(atomic64_read(&HFS_SB(sb)->next_id) > U32_MAX);
		mdb->drNxtCNID =
			cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->next_id));
		mdb->drNmFls = cpu_to_be16(HFS_SB(sb)->root_files);
		mdb->drNmRtDirs = cpu_to_be16(HFS_SB(sb)->root_dirs);
		BUG_ON(atomic64_read(&HFS_SB(sb)->file_count) > U32_MAX);
		mdb->drFilCnt =
			cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->file_count));
		BUG_ON(atomic64_read(&HFS_SB(sb)->folder_count) > U32_MAX);
		mdb->drDirCnt =
			cpu_to_be32((u32)atomic64_read(&HFS_SB(sb)->folder_count));

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

	kfree(HFS_SB(sb)->bitmap);
	kfree(HFS_SB(sb));
	sb->s_fs_info = NULL;
}
+12 −1
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ MODULE_LICENSE("GPL");

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

	is_hfs_cnid_counts_valid(sb);

	hfs_mdb_commit(sb);
}

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

static void hfs_kill_super(struct super_block *sb)
{
	struct hfs_sb_info *hsb = HFS_SB(sb);

	kill_block_super(sb);
	kfree(hsb);
}

static struct file_system_type hfs_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "hfs",
	.kill_sb	= kill_block_super,
	.kill_sb	= hfs_kill_super,
	.fs_flags	= FS_REQUIRES_DEV,
	.init_fs_context = hfs_init_fs_context,
};
Loading