xref: /kernel/linux/linux-5.10/fs/hfsplus/catalog.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/hfsplus/catalog.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2001
68c2ecf20Sopenharmony_ci * Brad Boyer (flar@allandria.com)
78c2ecf20Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Handling of catalog records
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "hfsplus_fs.h"
148c2ecf20Sopenharmony_ci#include "hfsplus_raw.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciint hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1,
178c2ecf20Sopenharmony_ci			     const hfsplus_btree_key *k2)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	__be32 k1p, k2p;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	k1p = k1->cat.parent;
228c2ecf20Sopenharmony_ci	k2p = k2->cat.parent;
238c2ecf20Sopenharmony_ci	if (k1p != k2p)
248c2ecf20Sopenharmony_ci		return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	return hfsplus_strcasecmp(&k1->cat.name, &k2->cat.name);
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciint hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
308c2ecf20Sopenharmony_ci			    const hfsplus_btree_key *k2)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	__be32 k1p, k2p;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	k1p = k1->cat.parent;
358c2ecf20Sopenharmony_ci	k2p = k2->cat.parent;
368c2ecf20Sopenharmony_ci	if (k1p != k2p)
378c2ecf20Sopenharmony_ci		return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return hfsplus_strcmp(&k1->cat.name, &k2->cat.name);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Generates key for catalog file/folders record. */
438c2ecf20Sopenharmony_ciint hfsplus_cat_build_key(struct super_block *sb,
448c2ecf20Sopenharmony_ci		hfsplus_btree_key *key, u32 parent, const struct qstr *str)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	int len, err;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	key->cat.parent = cpu_to_be32(parent);
498c2ecf20Sopenharmony_ci	err = hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN,
508c2ecf20Sopenharmony_ci			str->name, str->len);
518c2ecf20Sopenharmony_ci	if (unlikely(err < 0))
528c2ecf20Sopenharmony_ci		return err;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	len = be16_to_cpu(key->cat.name.length);
558c2ecf20Sopenharmony_ci	key->key_len = cpu_to_be16(6 + 2 * len);
568c2ecf20Sopenharmony_ci	return 0;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* Generates key for catalog thread record. */
608c2ecf20Sopenharmony_civoid hfsplus_cat_build_key_with_cnid(struct super_block *sb,
618c2ecf20Sopenharmony_ci			hfsplus_btree_key *key, u32 parent)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	key->cat.parent = cpu_to_be32(parent);
648c2ecf20Sopenharmony_ci	key->cat.name.length = 0;
658c2ecf20Sopenharmony_ci	key->key_len = cpu_to_be16(6);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent,
698c2ecf20Sopenharmony_ci				      struct hfsplus_unistr *name)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	int ustrlen;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	ustrlen = be16_to_cpu(name->length);
748c2ecf20Sopenharmony_ci	key->cat.parent = cpu_to_be32(parent);
758c2ecf20Sopenharmony_ci	key->cat.name.length = cpu_to_be16(ustrlen);
768c2ecf20Sopenharmony_ci	ustrlen *= 2;
778c2ecf20Sopenharmony_ci	memcpy(key->cat.name.unicode, name->unicode, ustrlen);
788c2ecf20Sopenharmony_ci	key->key_len = cpu_to_be16(6 + ustrlen);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_civoid hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	if (inode->i_flags & S_IMMUTABLE)
848c2ecf20Sopenharmony_ci		perms->rootflags |= HFSPLUS_FLG_IMMUTABLE;
858c2ecf20Sopenharmony_ci	else
868c2ecf20Sopenharmony_ci		perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
878c2ecf20Sopenharmony_ci	if (inode->i_flags & S_APPEND)
888c2ecf20Sopenharmony_ci		perms->rootflags |= HFSPLUS_FLG_APPEND;
898c2ecf20Sopenharmony_ci	else
908c2ecf20Sopenharmony_ci		perms->rootflags &= ~HFSPLUS_FLG_APPEND;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	perms->userflags = HFSPLUS_I(inode)->userflags;
938c2ecf20Sopenharmony_ci	perms->mode = cpu_to_be16(inode->i_mode);
948c2ecf20Sopenharmony_ci	perms->owner = cpu_to_be32(i_uid_read(inode));
958c2ecf20Sopenharmony_ci	perms->group = cpu_to_be32(i_gid_read(inode));
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (S_ISREG(inode->i_mode))
988c2ecf20Sopenharmony_ci		perms->dev = cpu_to_be32(inode->i_nlink);
998c2ecf20Sopenharmony_ci	else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode))
1008c2ecf20Sopenharmony_ci		perms->dev = cpu_to_be32(inode->i_rdev);
1018c2ecf20Sopenharmony_ci	else
1028c2ecf20Sopenharmony_ci		perms->dev = 0;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int hfsplus_cat_build_record(hfsplus_cat_entry *entry,
1068c2ecf20Sopenharmony_ci		u32 cnid, struct inode *inode)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (S_ISDIR(inode->i_mode)) {
1118c2ecf20Sopenharmony_ci		struct hfsplus_cat_folder *folder;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		folder = &entry->folder;
1148c2ecf20Sopenharmony_ci		memset(folder, 0, sizeof(*folder));
1158c2ecf20Sopenharmony_ci		folder->type = cpu_to_be16(HFSPLUS_FOLDER);
1168c2ecf20Sopenharmony_ci		if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags))
1178c2ecf20Sopenharmony_ci			folder->flags |= cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT);
1188c2ecf20Sopenharmony_ci		folder->id = cpu_to_be32(inode->i_ino);
1198c2ecf20Sopenharmony_ci		HFSPLUS_I(inode)->create_date =
1208c2ecf20Sopenharmony_ci			folder->create_date =
1218c2ecf20Sopenharmony_ci			folder->content_mod_date =
1228c2ecf20Sopenharmony_ci			folder->attribute_mod_date =
1238c2ecf20Sopenharmony_ci			folder->access_date = hfsp_now2mt();
1248c2ecf20Sopenharmony_ci		hfsplus_cat_set_perms(inode, &folder->permissions);
1258c2ecf20Sopenharmony_ci		if (inode == sbi->hidden_dir)
1268c2ecf20Sopenharmony_ci			/* invisible and namelocked */
1278c2ecf20Sopenharmony_ci			folder->user_info.frFlags = cpu_to_be16(0x5000);
1288c2ecf20Sopenharmony_ci		return sizeof(*folder);
1298c2ecf20Sopenharmony_ci	} else {
1308c2ecf20Sopenharmony_ci		struct hfsplus_cat_file *file;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		file = &entry->file;
1338c2ecf20Sopenharmony_ci		memset(file, 0, sizeof(*file));
1348c2ecf20Sopenharmony_ci		file->type = cpu_to_be16(HFSPLUS_FILE);
1358c2ecf20Sopenharmony_ci		file->flags = cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS);
1368c2ecf20Sopenharmony_ci		file->id = cpu_to_be32(cnid);
1378c2ecf20Sopenharmony_ci		HFSPLUS_I(inode)->create_date =
1388c2ecf20Sopenharmony_ci			file->create_date =
1398c2ecf20Sopenharmony_ci			file->content_mod_date =
1408c2ecf20Sopenharmony_ci			file->attribute_mod_date =
1418c2ecf20Sopenharmony_ci			file->access_date = hfsp_now2mt();
1428c2ecf20Sopenharmony_ci		if (cnid == inode->i_ino) {
1438c2ecf20Sopenharmony_ci			hfsplus_cat_set_perms(inode, &file->permissions);
1448c2ecf20Sopenharmony_ci			if (S_ISLNK(inode->i_mode)) {
1458c2ecf20Sopenharmony_ci				file->user_info.fdType =
1468c2ecf20Sopenharmony_ci					cpu_to_be32(HFSP_SYMLINK_TYPE);
1478c2ecf20Sopenharmony_ci				file->user_info.fdCreator =
1488c2ecf20Sopenharmony_ci					cpu_to_be32(HFSP_SYMLINK_CREATOR);
1498c2ecf20Sopenharmony_ci			} else {
1508c2ecf20Sopenharmony_ci				file->user_info.fdType =
1518c2ecf20Sopenharmony_ci					cpu_to_be32(sbi->type);
1528c2ecf20Sopenharmony_ci				file->user_info.fdCreator =
1538c2ecf20Sopenharmony_ci					cpu_to_be32(sbi->creator);
1548c2ecf20Sopenharmony_ci			}
1558c2ecf20Sopenharmony_ci			if (HFSPLUS_FLG_IMMUTABLE &
1568c2ecf20Sopenharmony_ci					(file->permissions.rootflags |
1578c2ecf20Sopenharmony_ci					file->permissions.userflags))
1588c2ecf20Sopenharmony_ci				file->flags |=
1598c2ecf20Sopenharmony_ci					cpu_to_be16(HFSPLUS_FILE_LOCKED);
1608c2ecf20Sopenharmony_ci		} else {
1618c2ecf20Sopenharmony_ci			file->user_info.fdType =
1628c2ecf20Sopenharmony_ci				cpu_to_be32(HFSP_HARDLINK_TYPE);
1638c2ecf20Sopenharmony_ci			file->user_info.fdCreator =
1648c2ecf20Sopenharmony_ci				cpu_to_be32(HFSP_HFSPLUS_CREATOR);
1658c2ecf20Sopenharmony_ci			file->user_info.fdFlags =
1668c2ecf20Sopenharmony_ci				cpu_to_be16(0x100);
1678c2ecf20Sopenharmony_ci			file->create_date =
1688c2ecf20Sopenharmony_ci				HFSPLUS_I(sbi->hidden_dir)->create_date;
1698c2ecf20Sopenharmony_ci			file->permissions.dev =
1708c2ecf20Sopenharmony_ci				cpu_to_be32(HFSPLUS_I(inode)->linkid);
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci		return sizeof(*file);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int hfsplus_fill_cat_thread(struct super_block *sb,
1778c2ecf20Sopenharmony_ci				   hfsplus_cat_entry *entry, int type,
1788c2ecf20Sopenharmony_ci				   u32 parentid, const struct qstr *str)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	int err;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	entry->type = cpu_to_be16(type);
1838c2ecf20Sopenharmony_ci	entry->thread.reserved = 0;
1848c2ecf20Sopenharmony_ci	entry->thread.parentID = cpu_to_be32(parentid);
1858c2ecf20Sopenharmony_ci	err = hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN,
1868c2ecf20Sopenharmony_ci				str->name, str->len);
1878c2ecf20Sopenharmony_ci	if (unlikely(err < 0))
1888c2ecf20Sopenharmony_ci		return err;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/* Try to get a catalog entry for given catalog id */
1948c2ecf20Sopenharmony_ciint hfsplus_find_cat(struct super_block *sb, u32 cnid,
1958c2ecf20Sopenharmony_ci		     struct hfs_find_data *fd)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	hfsplus_cat_entry tmp;
1988c2ecf20Sopenharmony_ci	int err;
1998c2ecf20Sopenharmony_ci	u16 type;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid);
2028c2ecf20Sopenharmony_ci	err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
2038c2ecf20Sopenharmony_ci	if (err)
2048c2ecf20Sopenharmony_ci		return err;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	type = be16_to_cpu(tmp.type);
2078c2ecf20Sopenharmony_ci	if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) {
2088c2ecf20Sopenharmony_ci		pr_err("found bad thread record in catalog\n");
2098c2ecf20Sopenharmony_ci		return -EIO;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (be16_to_cpu(tmp.thread.nodeName.length) > 255) {
2138c2ecf20Sopenharmony_ci		pr_err("catalog name length corrupted\n");
2148c2ecf20Sopenharmony_ci		return -EIO;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	hfsplus_cat_build_key_uni(fd->search_key,
2188c2ecf20Sopenharmony_ci		be32_to_cpu(tmp.thread.parentID),
2198c2ecf20Sopenharmony_ci		&tmp.thread.nodeName);
2208c2ecf20Sopenharmony_ci	return hfs_brec_find(fd, hfs_find_rec_by_key);
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void hfsplus_subfolders_inc(struct inode *dir)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
2288c2ecf20Sopenharmony_ci		/*
2298c2ecf20Sopenharmony_ci		 * Increment subfolder count. Note, the value is only meaningful
2308c2ecf20Sopenharmony_ci		 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
2318c2ecf20Sopenharmony_ci		 */
2328c2ecf20Sopenharmony_ci		HFSPLUS_I(dir)->subfolders++;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic void hfsplus_subfolders_dec(struct inode *dir)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
2418c2ecf20Sopenharmony_ci		/*
2428c2ecf20Sopenharmony_ci		 * Decrement subfolder count. Note, the value is only meaningful
2438c2ecf20Sopenharmony_ci		 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
2448c2ecf20Sopenharmony_ci		 *
2458c2ecf20Sopenharmony_ci		 * Check for zero. Some subfolders may have been created
2468c2ecf20Sopenharmony_ci		 * by an implementation ignorant of this counter.
2478c2ecf20Sopenharmony_ci		 */
2488c2ecf20Sopenharmony_ci		if (HFSPLUS_I(dir)->subfolders)
2498c2ecf20Sopenharmony_ci			HFSPLUS_I(dir)->subfolders--;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ciint hfsplus_create_cat(u32 cnid, struct inode *dir,
2548c2ecf20Sopenharmony_ci		const struct qstr *str, struct inode *inode)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct super_block *sb = dir->i_sb;
2578c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
2588c2ecf20Sopenharmony_ci	hfsplus_cat_entry entry;
2598c2ecf20Sopenharmony_ci	int entry_size;
2608c2ecf20Sopenharmony_ci	int err;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	hfs_dbg(CAT_MOD, "create_cat: %s,%u(%d)\n",
2638c2ecf20Sopenharmony_ci		str->name, cnid, inode->i_nlink);
2648c2ecf20Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
2658c2ecf20Sopenharmony_ci	if (err)
2668c2ecf20Sopenharmony_ci		return err;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/*
2698c2ecf20Sopenharmony_ci	 * Fail early and avoid ENOSPC during the btree operations. We may
2708c2ecf20Sopenharmony_ci	 * have to split the root node at most once.
2718c2ecf20Sopenharmony_ci	 */
2728c2ecf20Sopenharmony_ci	err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth);
2738c2ecf20Sopenharmony_ci	if (err)
2748c2ecf20Sopenharmony_ci		goto err2;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
2778c2ecf20Sopenharmony_ci	entry_size = hfsplus_fill_cat_thread(sb, &entry,
2788c2ecf20Sopenharmony_ci		S_ISDIR(inode->i_mode) ?
2798c2ecf20Sopenharmony_ci			HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
2808c2ecf20Sopenharmony_ci		dir->i_ino, str);
2818c2ecf20Sopenharmony_ci	if (unlikely(entry_size < 0)) {
2828c2ecf20Sopenharmony_ci		err = entry_size;
2838c2ecf20Sopenharmony_ci		goto err2;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
2878c2ecf20Sopenharmony_ci	if (err != -ENOENT) {
2888c2ecf20Sopenharmony_ci		if (!err)
2898c2ecf20Sopenharmony_ci			err = -EEXIST;
2908c2ecf20Sopenharmony_ci		goto err2;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci	err = hfs_brec_insert(&fd, &entry, entry_size);
2938c2ecf20Sopenharmony_ci	if (err)
2948c2ecf20Sopenharmony_ci		goto err2;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
2978c2ecf20Sopenharmony_ci	if (unlikely(err))
2988c2ecf20Sopenharmony_ci		goto err1;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
3018c2ecf20Sopenharmony_ci	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
3028c2ecf20Sopenharmony_ci	if (err != -ENOENT) {
3038c2ecf20Sopenharmony_ci		/* panic? */
3048c2ecf20Sopenharmony_ci		if (!err)
3058c2ecf20Sopenharmony_ci			err = -EEXIST;
3068c2ecf20Sopenharmony_ci		goto err1;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci	err = hfs_brec_insert(&fd, &entry, entry_size);
3098c2ecf20Sopenharmony_ci	if (err)
3108c2ecf20Sopenharmony_ci		goto err1;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	dir->i_size++;
3138c2ecf20Sopenharmony_ci	if (S_ISDIR(inode->i_mode))
3148c2ecf20Sopenharmony_ci		hfsplus_subfolders_inc(dir);
3158c2ecf20Sopenharmony_ci	dir->i_mtime = dir->i_ctime = current_time(dir);
3168c2ecf20Sopenharmony_ci	hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
3198c2ecf20Sopenharmony_ci	return 0;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cierr1:
3228c2ecf20Sopenharmony_ci	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
3238c2ecf20Sopenharmony_ci	if (!hfs_brec_find(&fd, hfs_find_rec_by_key))
3248c2ecf20Sopenharmony_ci		hfs_brec_remove(&fd);
3258c2ecf20Sopenharmony_cierr2:
3268c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
3278c2ecf20Sopenharmony_ci	return err;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ciint hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct super_block *sb = dir->i_sb;
3338c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
3348c2ecf20Sopenharmony_ci	struct hfsplus_fork_raw fork;
3358c2ecf20Sopenharmony_ci	struct list_head *pos;
3368c2ecf20Sopenharmony_ci	int err, off;
3378c2ecf20Sopenharmony_ci	u16 type;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	hfs_dbg(CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
3408c2ecf20Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
3418c2ecf20Sopenharmony_ci	if (err)
3428c2ecf20Sopenharmony_ci		return err;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/*
3458c2ecf20Sopenharmony_ci	 * Fail early and avoid ENOSPC during the btree operations. We may
3468c2ecf20Sopenharmony_ci	 * have to split the root node at most once.
3478c2ecf20Sopenharmony_ci	 */
3488c2ecf20Sopenharmony_ci	err = hfs_bmap_reserve(fd.tree, 2 * (int)fd.tree->depth - 2);
3498c2ecf20Sopenharmony_ci	if (err)
3508c2ecf20Sopenharmony_ci		goto out;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if (!str) {
3538c2ecf20Sopenharmony_ci		int len;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
3568c2ecf20Sopenharmony_ci		err = hfs_brec_find(&fd, hfs_find_rec_by_key);
3578c2ecf20Sopenharmony_ci		if (err)
3588c2ecf20Sopenharmony_ci			goto out;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		off = fd.entryoffset +
3618c2ecf20Sopenharmony_ci			offsetof(struct hfsplus_cat_thread, nodeName);
3628c2ecf20Sopenharmony_ci		fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
3638c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode,
3648c2ecf20Sopenharmony_ci			&fd.search_key->cat.name.length, off, 2);
3658c2ecf20Sopenharmony_ci		len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
3668c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode,
3678c2ecf20Sopenharmony_ci			&fd.search_key->cat.name.unicode,
3688c2ecf20Sopenharmony_ci			off + 2, len);
3698c2ecf20Sopenharmony_ci		fd.search_key->key_len = cpu_to_be16(6 + len);
3708c2ecf20Sopenharmony_ci	} else {
3718c2ecf20Sopenharmony_ci		err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
3728c2ecf20Sopenharmony_ci		if (unlikely(err))
3738c2ecf20Sopenharmony_ci			goto out;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
3778c2ecf20Sopenharmony_ci	if (err)
3788c2ecf20Sopenharmony_ci		goto out;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
3818c2ecf20Sopenharmony_ci	if (type == HFSPLUS_FILE) {
3828c2ecf20Sopenharmony_ci#if 0
3838c2ecf20Sopenharmony_ci		off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork);
3848c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
3858c2ecf20Sopenharmony_ci		hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA);
3868c2ecf20Sopenharmony_ci#endif
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		off = fd.entryoffset +
3898c2ecf20Sopenharmony_ci			offsetof(struct hfsplus_cat_file, rsrc_fork);
3908c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
3918c2ecf20Sopenharmony_ci		hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* we only need to take spinlock for exclusion with ->release() */
3958c2ecf20Sopenharmony_ci	spin_lock(&HFSPLUS_I(dir)->open_dir_lock);
3968c2ecf20Sopenharmony_ci	list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) {
3978c2ecf20Sopenharmony_ci		struct hfsplus_readdir_data *rd =
3988c2ecf20Sopenharmony_ci			list_entry(pos, struct hfsplus_readdir_data, list);
3998c2ecf20Sopenharmony_ci		if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
4008c2ecf20Sopenharmony_ci			rd->file->f_pos--;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci	spin_unlock(&HFSPLUS_I(dir)->open_dir_lock);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	err = hfs_brec_remove(&fd);
4058c2ecf20Sopenharmony_ci	if (err)
4068c2ecf20Sopenharmony_ci		goto out;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
4098c2ecf20Sopenharmony_ci	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
4108c2ecf20Sopenharmony_ci	if (err)
4118c2ecf20Sopenharmony_ci		goto out;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	err = hfs_brec_remove(&fd);
4148c2ecf20Sopenharmony_ci	if (err)
4158c2ecf20Sopenharmony_ci		goto out;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	dir->i_size--;
4188c2ecf20Sopenharmony_ci	if (type == HFSPLUS_FOLDER)
4198c2ecf20Sopenharmony_ci		hfsplus_subfolders_dec(dir);
4208c2ecf20Sopenharmony_ci	dir->i_mtime = dir->i_ctime = current_time(dir);
4218c2ecf20Sopenharmony_ci	hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (type == HFSPLUS_FILE || type == HFSPLUS_FOLDER) {
4248c2ecf20Sopenharmony_ci		if (HFSPLUS_SB(sb)->attr_tree)
4258c2ecf20Sopenharmony_ci			hfsplus_delete_all_attrs(dir, cnid);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ciout:
4298c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return err;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ciint hfsplus_rename_cat(u32 cnid,
4358c2ecf20Sopenharmony_ci		       struct inode *src_dir, const struct qstr *src_name,
4368c2ecf20Sopenharmony_ci		       struct inode *dst_dir, const struct qstr *dst_name)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct super_block *sb = src_dir->i_sb;
4398c2ecf20Sopenharmony_ci	struct hfs_find_data src_fd, dst_fd;
4408c2ecf20Sopenharmony_ci	hfsplus_cat_entry entry;
4418c2ecf20Sopenharmony_ci	int entry_size, type;
4428c2ecf20Sopenharmony_ci	int err;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	hfs_dbg(CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n",
4458c2ecf20Sopenharmony_ci		cnid, src_dir->i_ino, src_name->name,
4468c2ecf20Sopenharmony_ci		dst_dir->i_ino, dst_name->name);
4478c2ecf20Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
4488c2ecf20Sopenharmony_ci	if (err)
4498c2ecf20Sopenharmony_ci		return err;
4508c2ecf20Sopenharmony_ci	dst_fd = src_fd;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	/*
4538c2ecf20Sopenharmony_ci	 * Fail early and avoid ENOSPC during the btree operations. We may
4548c2ecf20Sopenharmony_ci	 * have to split the root node at most twice.
4558c2ecf20Sopenharmony_ci	 */
4568c2ecf20Sopenharmony_ci	err = hfs_bmap_reserve(src_fd.tree, 4 * (int)src_fd.tree->depth - 1);
4578c2ecf20Sopenharmony_ci	if (err)
4588c2ecf20Sopenharmony_ci		goto out;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* find the old dir entry and read the data */
4618c2ecf20Sopenharmony_ci	err = hfsplus_cat_build_key(sb, src_fd.search_key,
4628c2ecf20Sopenharmony_ci			src_dir->i_ino, src_name);
4638c2ecf20Sopenharmony_ci	if (unlikely(err))
4648c2ecf20Sopenharmony_ci		goto out;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
4678c2ecf20Sopenharmony_ci	if (err)
4688c2ecf20Sopenharmony_ci		goto out;
4698c2ecf20Sopenharmony_ci	if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
4708c2ecf20Sopenharmony_ci		err = -EIO;
4718c2ecf20Sopenharmony_ci		goto out;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
4758c2ecf20Sopenharmony_ci				src_fd.entrylength);
4768c2ecf20Sopenharmony_ci	type = be16_to_cpu(entry.type);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/* create new dir entry with the data from the old entry */
4798c2ecf20Sopenharmony_ci	err = hfsplus_cat_build_key(sb, dst_fd.search_key,
4808c2ecf20Sopenharmony_ci			dst_dir->i_ino, dst_name);
4818c2ecf20Sopenharmony_ci	if (unlikely(err))
4828c2ecf20Sopenharmony_ci		goto out;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
4858c2ecf20Sopenharmony_ci	if (err != -ENOENT) {
4868c2ecf20Sopenharmony_ci		if (!err)
4878c2ecf20Sopenharmony_ci			err = -EEXIST;
4888c2ecf20Sopenharmony_ci		goto out;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
4928c2ecf20Sopenharmony_ci	if (err)
4938c2ecf20Sopenharmony_ci		goto out;
4948c2ecf20Sopenharmony_ci	dst_dir->i_size++;
4958c2ecf20Sopenharmony_ci	if (type == HFSPLUS_FOLDER)
4968c2ecf20Sopenharmony_ci		hfsplus_subfolders_inc(dst_dir);
4978c2ecf20Sopenharmony_ci	dst_dir->i_mtime = dst_dir->i_ctime = current_time(dst_dir);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/* finally remove the old entry */
5008c2ecf20Sopenharmony_ci	err = hfsplus_cat_build_key(sb, src_fd.search_key,
5018c2ecf20Sopenharmony_ci			src_dir->i_ino, src_name);
5028c2ecf20Sopenharmony_ci	if (unlikely(err))
5038c2ecf20Sopenharmony_ci		goto out;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
5068c2ecf20Sopenharmony_ci	if (err)
5078c2ecf20Sopenharmony_ci		goto out;
5088c2ecf20Sopenharmony_ci	err = hfs_brec_remove(&src_fd);
5098c2ecf20Sopenharmony_ci	if (err)
5108c2ecf20Sopenharmony_ci		goto out;
5118c2ecf20Sopenharmony_ci	src_dir->i_size--;
5128c2ecf20Sopenharmony_ci	if (type == HFSPLUS_FOLDER)
5138c2ecf20Sopenharmony_ci		hfsplus_subfolders_dec(src_dir);
5148c2ecf20Sopenharmony_ci	src_dir->i_mtime = src_dir->i_ctime = current_time(src_dir);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	/* remove old thread entry */
5178c2ecf20Sopenharmony_ci	hfsplus_cat_build_key_with_cnid(sb, src_fd.search_key, cnid);
5188c2ecf20Sopenharmony_ci	err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
5198c2ecf20Sopenharmony_ci	if (err)
5208c2ecf20Sopenharmony_ci		goto out;
5218c2ecf20Sopenharmony_ci	type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
5228c2ecf20Sopenharmony_ci	err = hfs_brec_remove(&src_fd);
5238c2ecf20Sopenharmony_ci	if (err)
5248c2ecf20Sopenharmony_ci		goto out;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* create new thread entry */
5278c2ecf20Sopenharmony_ci	hfsplus_cat_build_key_with_cnid(sb, dst_fd.search_key, cnid);
5288c2ecf20Sopenharmony_ci	entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
5298c2ecf20Sopenharmony_ci		dst_dir->i_ino, dst_name);
5308c2ecf20Sopenharmony_ci	if (unlikely(entry_size < 0)) {
5318c2ecf20Sopenharmony_ci		err = entry_size;
5328c2ecf20Sopenharmony_ci		goto out;
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
5368c2ecf20Sopenharmony_ci	if (err != -ENOENT) {
5378c2ecf20Sopenharmony_ci		if (!err)
5388c2ecf20Sopenharmony_ci			err = -EEXIST;
5398c2ecf20Sopenharmony_ci		goto out;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci	err = hfs_brec_insert(&dst_fd, &entry, entry_size);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY);
5448c2ecf20Sopenharmony_ci	hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY);
5458c2ecf20Sopenharmony_ciout:
5468c2ecf20Sopenharmony_ci	hfs_bnode_put(dst_fd.bnode);
5478c2ecf20Sopenharmony_ci	hfs_find_exit(&src_fd);
5488c2ecf20Sopenharmony_ci	return err;
5498c2ecf20Sopenharmony_ci}
550