xref: /openbmc/linux/fs/hfsplus/catalog.c (revision 101fa821)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/hfsplus/catalog.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 2001
61da177e4SLinus Torvalds  * Brad Boyer (flar@allandria.com)
71da177e4SLinus Torvalds  * (C) 2003 Ardis Technologies <roman@ardistech.com>
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Handling of catalog records
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include "hfsplus_fs.h"
141da177e4SLinus Torvalds #include "hfsplus_raw.h"
151da177e4SLinus Torvalds 
hfsplus_cat_case_cmp_key(const hfsplus_btree_key * k1,const hfsplus_btree_key * k2)162179d372SDavid Elliott int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1,
172179d372SDavid Elliott 			     const hfsplus_btree_key *k2)
181da177e4SLinus Torvalds {
191da177e4SLinus Torvalds 	__be32 k1p, k2p;
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds 	k1p = k1->cat.parent;
221da177e4SLinus Torvalds 	k2p = k2->cat.parent;
231da177e4SLinus Torvalds 	if (k1p != k2p)
241da177e4SLinus Torvalds 		return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
251da177e4SLinus Torvalds 
262179d372SDavid Elliott 	return hfsplus_strcasecmp(&k1->cat.name, &k2->cat.name);
272179d372SDavid Elliott }
282179d372SDavid Elliott 
hfsplus_cat_bin_cmp_key(const hfsplus_btree_key * k1,const hfsplus_btree_key * k2)292179d372SDavid Elliott int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
302179d372SDavid Elliott 			    const hfsplus_btree_key *k2)
312179d372SDavid Elliott {
322179d372SDavid Elliott 	__be32 k1p, k2p;
332179d372SDavid Elliott 
342179d372SDavid Elliott 	k1p = k1->cat.parent;
352179d372SDavid Elliott 	k2p = k2->cat.parent;
362179d372SDavid Elliott 	if (k1p != k2p)
372179d372SDavid Elliott 		return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
382179d372SDavid Elliott 
392179d372SDavid Elliott 	return hfsplus_strcmp(&k1->cat.name, &k2->cat.name);
401da177e4SLinus Torvalds }
411da177e4SLinus Torvalds 
4289ac9b4dSSougata Santra /* Generates key for catalog file/folders record. */
hfsplus_cat_build_key(struct super_block * sb,hfsplus_btree_key * key,u32 parent,const struct qstr * str)4389ac9b4dSSougata Santra int hfsplus_cat_build_key(struct super_block *sb,
44b5cce521SAl Viro 		hfsplus_btree_key *key, u32 parent, const struct qstr *str)
451da177e4SLinus Torvalds {
4689ac9b4dSSougata Santra 	int len, err;
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds 	key->cat.parent = cpu_to_be32(parent);
4989ac9b4dSSougata Santra 	err = hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN,
50324ef39aSVyacheslav Dubeyko 			str->name, str->len);
5189ac9b4dSSougata Santra 	if (unlikely(err < 0))
5289ac9b4dSSougata Santra 		return err;
5389ac9b4dSSougata Santra 
541da177e4SLinus Torvalds 	len = be16_to_cpu(key->cat.name.length);
551da177e4SLinus Torvalds 	key->key_len = cpu_to_be16(6 + 2 * len);
5689ac9b4dSSougata Santra 	return 0;
5789ac9b4dSSougata Santra }
5889ac9b4dSSougata Santra 
5989ac9b4dSSougata Santra /* Generates key for catalog thread record. */
hfsplus_cat_build_key_with_cnid(struct super_block * sb,hfsplus_btree_key * key,u32 parent)6089ac9b4dSSougata Santra void hfsplus_cat_build_key_with_cnid(struct super_block *sb,
6189ac9b4dSSougata Santra 			hfsplus_btree_key *key, u32 parent)
6289ac9b4dSSougata Santra {
6389ac9b4dSSougata Santra 	key->cat.parent = cpu_to_be32(parent);
6489ac9b4dSSougata Santra 	key->cat.name.length = 0;
6589ac9b4dSSougata Santra 	key->key_len = cpu_to_be16(6);
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds 
hfsplus_cat_build_key_uni(hfsplus_btree_key * key,u32 parent,struct hfsplus_unistr * name)681da177e4SLinus Torvalds static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent,
691da177e4SLinus Torvalds 				      struct hfsplus_unistr *name)
701da177e4SLinus Torvalds {
711da177e4SLinus Torvalds 	int ustrlen;
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	ustrlen = be16_to_cpu(name->length);
741da177e4SLinus Torvalds 	key->cat.parent = cpu_to_be32(parent);
751da177e4SLinus Torvalds 	key->cat.name.length = cpu_to_be16(ustrlen);
761da177e4SLinus Torvalds 	ustrlen *= 2;
771da177e4SLinus Torvalds 	memcpy(key->cat.name.unicode, name->unicode, ustrlen);
781da177e4SLinus Torvalds 	key->key_len = cpu_to_be16(6 + ustrlen);
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds 
hfsplus_cat_set_perms(struct inode * inode,struct hfsplus_perm * perms)8190e61690SChristoph Hellwig void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms)
821da177e4SLinus Torvalds {
831da177e4SLinus Torvalds 	if (inode->i_flags & S_IMMUTABLE)
841da177e4SLinus Torvalds 		perms->rootflags |= HFSPLUS_FLG_IMMUTABLE;
851da177e4SLinus Torvalds 	else
861da177e4SLinus Torvalds 		perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
871da177e4SLinus Torvalds 	if (inode->i_flags & S_APPEND)
881da177e4SLinus Torvalds 		perms->rootflags |= HFSPLUS_FLG_APPEND;
891da177e4SLinus Torvalds 	else
901da177e4SLinus Torvalds 		perms->rootflags &= ~HFSPLUS_FLG_APPEND;
9190e61690SChristoph Hellwig 
9290e61690SChristoph Hellwig 	perms->userflags = HFSPLUS_I(inode)->userflags;
931da177e4SLinus Torvalds 	perms->mode = cpu_to_be16(inode->i_mode);
9416525e3fSEric W. Biederman 	perms->owner = cpu_to_be32(i_uid_read(inode));
9516525e3fSEric W. Biederman 	perms->group = cpu_to_be32(i_gid_read(inode));
9690e61690SChristoph Hellwig 
9790e61690SChristoph Hellwig 	if (S_ISREG(inode->i_mode))
9890e61690SChristoph Hellwig 		perms->dev = cpu_to_be32(inode->i_nlink);
9990e61690SChristoph Hellwig 	else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode))
10090e61690SChristoph Hellwig 		perms->dev = cpu_to_be32(inode->i_rdev);
10190e61690SChristoph Hellwig 	else
10290e61690SChristoph Hellwig 		perms->dev = 0;
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds 
hfsplus_cat_build_record(hfsplus_cat_entry * entry,u32 cnid,struct inode * inode)1052753cc28SAnton Salikhmetov static int hfsplus_cat_build_record(hfsplus_cat_entry *entry,
1062753cc28SAnton Salikhmetov 		u32 cnid, struct inode *inode)
1071da177e4SLinus Torvalds {
108dd73a01aSChristoph Hellwig 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
109dd73a01aSChristoph Hellwig 
1101da177e4SLinus Torvalds 	if (S_ISDIR(inode->i_mode)) {
1111da177e4SLinus Torvalds 		struct hfsplus_cat_folder *folder;
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 		folder = &entry->folder;
1141da177e4SLinus Torvalds 		memset(folder, 0, sizeof(*folder));
1151da177e4SLinus Torvalds 		folder->type = cpu_to_be16(HFSPLUS_FOLDER);
116d7d673a5SSergei Antonov 		if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags))
117d7d673a5SSergei Antonov 			folder->flags |= cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT);
1181da177e4SLinus Torvalds 		folder->id = cpu_to_be32(inode->i_ino);
1196af502deSChristoph Hellwig 		HFSPLUS_I(inode)->create_date =
1209a4cad95SRoman Zippel 			folder->create_date =
1219a4cad95SRoman Zippel 			folder->content_mod_date =
1229a4cad95SRoman Zippel 			folder->attribute_mod_date =
1239a4cad95SRoman Zippel 			folder->access_date = hfsp_now2mt();
12490e61690SChristoph Hellwig 		hfsplus_cat_set_perms(inode, &folder->permissions);
125dd73a01aSChristoph Hellwig 		if (inode == sbi->hidden_dir)
1261da177e4SLinus Torvalds 			/* invisible and namelocked */
1271da177e4SLinus Torvalds 			folder->user_info.frFlags = cpu_to_be16(0x5000);
1281da177e4SLinus Torvalds 		return sizeof(*folder);
1291da177e4SLinus Torvalds 	} else {
1301da177e4SLinus Torvalds 		struct hfsplus_cat_file *file;
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 		file = &entry->file;
1331da177e4SLinus Torvalds 		memset(file, 0, sizeof(*file));
1341da177e4SLinus Torvalds 		file->type = cpu_to_be16(HFSPLUS_FILE);
1351da177e4SLinus Torvalds 		file->flags = cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS);
1361da177e4SLinus Torvalds 		file->id = cpu_to_be32(cnid);
1376af502deSChristoph Hellwig 		HFSPLUS_I(inode)->create_date =
1389a4cad95SRoman Zippel 			file->create_date =
1399a4cad95SRoman Zippel 			file->content_mod_date =
1409a4cad95SRoman Zippel 			file->attribute_mod_date =
1419a4cad95SRoman Zippel 			file->access_date = hfsp_now2mt();
1421da177e4SLinus Torvalds 		if (cnid == inode->i_ino) {
14390e61690SChristoph Hellwig 			hfsplus_cat_set_perms(inode, &file->permissions);
1446b192832SRoman Zippel 			if (S_ISLNK(inode->i_mode)) {
1452753cc28SAnton Salikhmetov 				file->user_info.fdType =
1462753cc28SAnton Salikhmetov 					cpu_to_be32(HFSP_SYMLINK_TYPE);
1472753cc28SAnton Salikhmetov 				file->user_info.fdCreator =
1482753cc28SAnton Salikhmetov 					cpu_to_be32(HFSP_SYMLINK_CREATOR);
1496b192832SRoman Zippel 			} else {
1502753cc28SAnton Salikhmetov 				file->user_info.fdType =
1512753cc28SAnton Salikhmetov 					cpu_to_be32(sbi->type);
1522753cc28SAnton Salikhmetov 				file->user_info.fdCreator =
1532753cc28SAnton Salikhmetov 					cpu_to_be32(sbi->creator);
1546b192832SRoman Zippel 			}
1552753cc28SAnton Salikhmetov 			if (HFSPLUS_FLG_IMMUTABLE &
1562753cc28SAnton Salikhmetov 					(file->permissions.rootflags |
1572753cc28SAnton Salikhmetov 					file->permissions.userflags))
1582753cc28SAnton Salikhmetov 				file->flags |=
1592753cc28SAnton Salikhmetov 					cpu_to_be16(HFSPLUS_FILE_LOCKED);
1601da177e4SLinus Torvalds 		} else {
1612753cc28SAnton Salikhmetov 			file->user_info.fdType =
1622753cc28SAnton Salikhmetov 				cpu_to_be32(HFSP_HARDLINK_TYPE);
1632753cc28SAnton Salikhmetov 			file->user_info.fdCreator =
1642753cc28SAnton Salikhmetov 				cpu_to_be32(HFSP_HFSPLUS_CREATOR);
1652753cc28SAnton Salikhmetov 			file->user_info.fdFlags =
1662753cc28SAnton Salikhmetov 				cpu_to_be16(0x100);
1672753cc28SAnton Salikhmetov 			file->create_date =
1682753cc28SAnton Salikhmetov 				HFSPLUS_I(sbi->hidden_dir)->create_date;
1692753cc28SAnton Salikhmetov 			file->permissions.dev =
1702753cc28SAnton Salikhmetov 				cpu_to_be32(HFSPLUS_I(inode)->linkid);
1711da177e4SLinus Torvalds 		}
1721da177e4SLinus Torvalds 		return sizeof(*file);
1731da177e4SLinus Torvalds 	}
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds 
hfsplus_fill_cat_thread(struct super_block * sb,hfsplus_cat_entry * entry,int type,u32 parentid,const struct qstr * str)1761da177e4SLinus Torvalds static int hfsplus_fill_cat_thread(struct super_block *sb,
1771da177e4SLinus Torvalds 				   hfsplus_cat_entry *entry, int type,
178b5cce521SAl Viro 				   u32 parentid, const struct qstr *str)
1791da177e4SLinus Torvalds {
18089ac9b4dSSougata Santra 	int err;
18189ac9b4dSSougata Santra 
1821da177e4SLinus Torvalds 	entry->type = cpu_to_be16(type);
1831da177e4SLinus Torvalds 	entry->thread.reserved = 0;
1841da177e4SLinus Torvalds 	entry->thread.parentID = cpu_to_be32(parentid);
18589ac9b4dSSougata Santra 	err = hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN,
186324ef39aSVyacheslav Dubeyko 				str->name, str->len);
18789ac9b4dSSougata Santra 	if (unlikely(err < 0))
18889ac9b4dSSougata Santra 		return err;
18989ac9b4dSSougata Santra 
1901da177e4SLinus Torvalds 	return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
1911da177e4SLinus Torvalds }
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds /* Try to get a catalog entry for given catalog id */
hfsplus_find_cat(struct super_block * sb,u32 cnid,struct hfs_find_data * fd)1941da177e4SLinus Torvalds int hfsplus_find_cat(struct super_block *sb, u32 cnid,
1951da177e4SLinus Torvalds 		     struct hfs_find_data *fd)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds 	hfsplus_cat_entry tmp;
1981da177e4SLinus Torvalds 	int err;
1991da177e4SLinus Torvalds 	u16 type;
2001da177e4SLinus Torvalds 
20189ac9b4dSSougata Santra 	hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid);
2021da177e4SLinus Torvalds 	err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
2031da177e4SLinus Torvalds 	if (err)
2041da177e4SLinus Torvalds 		return err;
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	type = be16_to_cpu(tmp.type);
2071da177e4SLinus Torvalds 	if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) {
208d6142673SJoe Perches 		pr_err("found bad thread record in catalog\n");
2091da177e4SLinus Torvalds 		return -EIO;
2101da177e4SLinus Torvalds 	}
2111da177e4SLinus Torvalds 
212efc7ffcbSEric Sesterhenn 	if (be16_to_cpu(tmp.thread.nodeName.length) > 255) {
213d6142673SJoe Perches 		pr_err("catalog name length corrupted\n");
214efc7ffcbSEric Sesterhenn 		return -EIO;
215efc7ffcbSEric Sesterhenn 	}
216efc7ffcbSEric Sesterhenn 
2172753cc28SAnton Salikhmetov 	hfsplus_cat_build_key_uni(fd->search_key,
2182753cc28SAnton Salikhmetov 		be32_to_cpu(tmp.thread.parentID),
2191da177e4SLinus Torvalds 		&tmp.thread.nodeName);
220324ef39aSVyacheslav Dubeyko 	return hfs_brec_find(fd, hfs_find_rec_by_key);
2211da177e4SLinus Torvalds }
2221da177e4SLinus Torvalds 
hfsplus_subfolders_inc(struct inode * dir)223d7d673a5SSergei Antonov static void hfsplus_subfolders_inc(struct inode *dir)
224d7d673a5SSergei Antonov {
225d7d673a5SSergei Antonov 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
226d7d673a5SSergei Antonov 
227d7d673a5SSergei Antonov 	if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
228d7d673a5SSergei Antonov 		/*
229d7d673a5SSergei Antonov 		 * Increment subfolder count. Note, the value is only meaningful
230d7d673a5SSergei Antonov 		 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
231d7d673a5SSergei Antonov 		 */
232d7d673a5SSergei Antonov 		HFSPLUS_I(dir)->subfolders++;
233d7d673a5SSergei Antonov 	}
234d7d673a5SSergei Antonov }
235d7d673a5SSergei Antonov 
hfsplus_subfolders_dec(struct inode * dir)236d7d673a5SSergei Antonov static void hfsplus_subfolders_dec(struct inode *dir)
237d7d673a5SSergei Antonov {
238d7d673a5SSergei Antonov 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
239d7d673a5SSergei Antonov 
240d7d673a5SSergei Antonov 	if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
241d7d673a5SSergei Antonov 		/*
242d7d673a5SSergei Antonov 		 * Decrement subfolder count. Note, the value is only meaningful
243d7d673a5SSergei Antonov 		 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
244d7d673a5SSergei Antonov 		 *
245d7d673a5SSergei Antonov 		 * Check for zero. Some subfolders may have been created
246d7d673a5SSergei Antonov 		 * by an implementation ignorant of this counter.
247d7d673a5SSergei Antonov 		 */
248d7d673a5SSergei Antonov 		if (HFSPLUS_I(dir)->subfolders)
249d7d673a5SSergei Antonov 			HFSPLUS_I(dir)->subfolders--;
250d7d673a5SSergei Antonov 	}
251d7d673a5SSergei Antonov }
252d7d673a5SSergei Antonov 
hfsplus_create_cat(u32 cnid,struct inode * dir,const struct qstr * str,struct inode * inode)2532753cc28SAnton Salikhmetov int hfsplus_create_cat(u32 cnid, struct inode *dir,
254b5cce521SAl Viro 		const struct qstr *str, struct inode *inode)
2551da177e4SLinus Torvalds {
256dd73a01aSChristoph Hellwig 	struct super_block *sb = dir->i_sb;
2571da177e4SLinus Torvalds 	struct hfs_find_data fd;
2581da177e4SLinus Torvalds 	hfsplus_cat_entry entry;
2591da177e4SLinus Torvalds 	int entry_size;
2601da177e4SLinus Torvalds 	int err;
2611da177e4SLinus Torvalds 
262c2b3e1f7SJoe Perches 	hfs_dbg(CAT_MOD, "create_cat: %s,%u(%d)\n",
2632753cc28SAnton Salikhmetov 		str->name, cnid, inode->i_nlink);
2645bd9d99dSAlexey Khoroshilov 	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
2655bd9d99dSAlexey Khoroshilov 	if (err)
2665bd9d99dSAlexey Khoroshilov 		return err;
2671da177e4SLinus Torvalds 
268d92915c3SErnesto A. Fernández 	/*
269d92915c3SErnesto A. Fernández 	 * Fail early and avoid ENOSPC during the btree operations. We may
270d92915c3SErnesto A. Fernández 	 * have to split the root node at most once.
271d92915c3SErnesto A. Fernández 	 */
272d92915c3SErnesto A. Fernández 	err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth);
273d92915c3SErnesto A. Fernández 	if (err)
274d92915c3SErnesto A. Fernández 		goto err2;
275d92915c3SErnesto A. Fernández 
27689ac9b4dSSougata Santra 	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
2772753cc28SAnton Salikhmetov 	entry_size = hfsplus_fill_cat_thread(sb, &entry,
2782753cc28SAnton Salikhmetov 		S_ISDIR(inode->i_mode) ?
2791da177e4SLinus Torvalds 			HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
2801da177e4SLinus Torvalds 		dir->i_ino, str);
28189ac9b4dSSougata Santra 	if (unlikely(entry_size < 0)) {
28289ac9b4dSSougata Santra 		err = entry_size;
28389ac9b4dSSougata Santra 		goto err2;
28489ac9b4dSSougata Santra 	}
28589ac9b4dSSougata Santra 
286324ef39aSVyacheslav Dubeyko 	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
2871da177e4SLinus Torvalds 	if (err != -ENOENT) {
2881da177e4SLinus Torvalds 		if (!err)
2891da177e4SLinus Torvalds 			err = -EEXIST;
2901da177e4SLinus Torvalds 		goto err2;
2911da177e4SLinus Torvalds 	}
2921da177e4SLinus Torvalds 	err = hfs_brec_insert(&fd, &entry, entry_size);
2931da177e4SLinus Torvalds 	if (err)
2941da177e4SLinus Torvalds 		goto err2;
2951da177e4SLinus Torvalds 
29689ac9b4dSSougata Santra 	err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
29789ac9b4dSSougata Santra 	if (unlikely(err))
29889ac9b4dSSougata Santra 		goto err1;
29989ac9b4dSSougata Santra 
3001da177e4SLinus Torvalds 	entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
301324ef39aSVyacheslav Dubeyko 	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
3021da177e4SLinus Torvalds 	if (err != -ENOENT) {
3031da177e4SLinus Torvalds 		/* panic? */
3041da177e4SLinus Torvalds 		if (!err)
3051da177e4SLinus Torvalds 			err = -EEXIST;
3061da177e4SLinus Torvalds 		goto err1;
3071da177e4SLinus Torvalds 	}
3081da177e4SLinus Torvalds 	err = hfs_brec_insert(&fd, &entry, entry_size);
3091da177e4SLinus Torvalds 	if (err)
3101da177e4SLinus Torvalds 		goto err1;
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	dir->i_size++;
313d7d673a5SSergei Antonov 	if (S_ISDIR(inode->i_mode))
314d7d673a5SSergei Antonov 		hfsplus_subfolders_inc(dir);
315*101fa821SJeff Layton 	dir->i_mtime = inode_set_ctime_current(dir);
316e3494705SChristoph Hellwig 	hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
317e3494705SChristoph Hellwig 
3181da177e4SLinus Torvalds 	hfs_find_exit(&fd);
3191da177e4SLinus Torvalds 	return 0;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds err1:
32289ac9b4dSSougata Santra 	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
323324ef39aSVyacheslav Dubeyko 	if (!hfs_brec_find(&fd, hfs_find_rec_by_key))
3241da177e4SLinus Torvalds 		hfs_brec_remove(&fd);
3251da177e4SLinus Torvalds err2:
3261da177e4SLinus Torvalds 	hfs_find_exit(&fd);
3271da177e4SLinus Torvalds 	return err;
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds 
hfsplus_delete_cat(u32 cnid,struct inode * dir,const struct qstr * str)330b5cce521SAl Viro int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str)
3311da177e4SLinus Torvalds {
332dd73a01aSChristoph Hellwig 	struct super_block *sb = dir->i_sb;
3331da177e4SLinus Torvalds 	struct hfs_find_data fd;
3341da177e4SLinus Torvalds 	struct hfsplus_fork_raw fork;
3351da177e4SLinus Torvalds 	struct list_head *pos;
3361da177e4SLinus Torvalds 	int err, off;
3371da177e4SLinus Torvalds 	u16 type;
3381da177e4SLinus Torvalds 
339c2b3e1f7SJoe Perches 	hfs_dbg(CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
3405bd9d99dSAlexey Khoroshilov 	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
3415bd9d99dSAlexey Khoroshilov 	if (err)
3425bd9d99dSAlexey Khoroshilov 		return err;
3431da177e4SLinus Torvalds 
344d92915c3SErnesto A. Fernández 	/*
345d92915c3SErnesto A. Fernández 	 * Fail early and avoid ENOSPC during the btree operations. We may
346d92915c3SErnesto A. Fernández 	 * have to split the root node at most once.
347d92915c3SErnesto A. Fernández 	 */
348d92915c3SErnesto A. Fernández 	err = hfs_bmap_reserve(fd.tree, 2 * (int)fd.tree->depth - 2);
349d92915c3SErnesto A. Fernández 	if (err)
350d92915c3SErnesto A. Fernández 		goto out;
351d92915c3SErnesto A. Fernández 
3521da177e4SLinus Torvalds 	if (!str) {
3531da177e4SLinus Torvalds 		int len;
3541da177e4SLinus Torvalds 
35589ac9b4dSSougata Santra 		hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
356324ef39aSVyacheslav Dubeyko 		err = hfs_brec_find(&fd, hfs_find_rec_by_key);
3571da177e4SLinus Torvalds 		if (err)
3581da177e4SLinus Torvalds 			goto out;
3591da177e4SLinus Torvalds 
3602753cc28SAnton Salikhmetov 		off = fd.entryoffset +
3612753cc28SAnton Salikhmetov 			offsetof(struct hfsplus_cat_thread, nodeName);
3621da177e4SLinus Torvalds 		fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
3632753cc28SAnton Salikhmetov 		hfs_bnode_read(fd.bnode,
3642753cc28SAnton Salikhmetov 			&fd.search_key->cat.name.length, off, 2);
3651da177e4SLinus Torvalds 		len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
3662753cc28SAnton Salikhmetov 		hfs_bnode_read(fd.bnode,
3672753cc28SAnton Salikhmetov 			&fd.search_key->cat.name.unicode,
3682753cc28SAnton Salikhmetov 			off + 2, len);
3691da177e4SLinus Torvalds 		fd.search_key->key_len = cpu_to_be16(6 + len);
370f01fa5fbSDan Carpenter 	} else {
37189ac9b4dSSougata Santra 		err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
37289ac9b4dSSougata Santra 		if (unlikely(err))
37389ac9b4dSSougata Santra 			goto out;
374f01fa5fbSDan Carpenter 	}
3751da177e4SLinus Torvalds 
376324ef39aSVyacheslav Dubeyko 	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
3771da177e4SLinus Torvalds 	if (err)
3781da177e4SLinus Torvalds 		goto out;
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
3811da177e4SLinus Torvalds 	if (type == HFSPLUS_FILE) {
3821da177e4SLinus Torvalds #if 0
3831da177e4SLinus Torvalds 		off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork);
3841da177e4SLinus Torvalds 		hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
3851da177e4SLinus Torvalds 		hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA);
3861da177e4SLinus Torvalds #endif
3871da177e4SLinus Torvalds 
3882753cc28SAnton Salikhmetov 		off = fd.entryoffset +
3892753cc28SAnton Salikhmetov 			offsetof(struct hfsplus_cat_file, rsrc_fork);
3901da177e4SLinus Torvalds 		hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
3911da177e4SLinus Torvalds 		hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
3921da177e4SLinus Torvalds 	}
3931da177e4SLinus Torvalds 
394323ee8fcSAl Viro 	/* we only need to take spinlock for exclusion with ->release() */
395323ee8fcSAl Viro 	spin_lock(&HFSPLUS_I(dir)->open_dir_lock);
3966af502deSChristoph Hellwig 	list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) {
3971da177e4SLinus Torvalds 		struct hfsplus_readdir_data *rd =
3981da177e4SLinus Torvalds 			list_entry(pos, struct hfsplus_readdir_data, list);
3991da177e4SLinus Torvalds 		if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
4001da177e4SLinus Torvalds 			rd->file->f_pos--;
4011da177e4SLinus Torvalds 	}
402323ee8fcSAl Viro 	spin_unlock(&HFSPLUS_I(dir)->open_dir_lock);
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds 	err = hfs_brec_remove(&fd);
4051da177e4SLinus Torvalds 	if (err)
4061da177e4SLinus Torvalds 		goto out;
4071da177e4SLinus Torvalds 
40889ac9b4dSSougata Santra 	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
409324ef39aSVyacheslav Dubeyko 	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
4101da177e4SLinus Torvalds 	if (err)
4111da177e4SLinus Torvalds 		goto out;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	err = hfs_brec_remove(&fd);
4141da177e4SLinus Torvalds 	if (err)
4151da177e4SLinus Torvalds 		goto out;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	dir->i_size--;
418d7d673a5SSergei Antonov 	if (type == HFSPLUS_FOLDER)
419d7d673a5SSergei Antonov 		hfsplus_subfolders_dec(dir);
420*101fa821SJeff Layton 	dir->i_mtime = inode_set_ctime_current(dir);
421e3494705SChristoph Hellwig 	hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
422324ef39aSVyacheslav Dubeyko 
423324ef39aSVyacheslav Dubeyko 	if (type == HFSPLUS_FILE || type == HFSPLUS_FOLDER) {
424324ef39aSVyacheslav Dubeyko 		if (HFSPLUS_SB(sb)->attr_tree)
425324ef39aSVyacheslav Dubeyko 			hfsplus_delete_all_attrs(dir, cnid);
426324ef39aSVyacheslav Dubeyko 	}
427324ef39aSVyacheslav Dubeyko 
4281da177e4SLinus Torvalds out:
4291da177e4SLinus Torvalds 	hfs_find_exit(&fd);
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	return err;
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds 
hfsplus_rename_cat(u32 cnid,struct inode * src_dir,const struct qstr * src_name,struct inode * dst_dir,const struct qstr * dst_name)4341da177e4SLinus Torvalds int hfsplus_rename_cat(u32 cnid,
435b5cce521SAl Viro 		       struct inode *src_dir, const struct qstr *src_name,
436b5cce521SAl Viro 		       struct inode *dst_dir, const struct qstr *dst_name)
4371da177e4SLinus Torvalds {
438dd73a01aSChristoph Hellwig 	struct super_block *sb = src_dir->i_sb;
4391da177e4SLinus Torvalds 	struct hfs_find_data src_fd, dst_fd;
4401da177e4SLinus Torvalds 	hfsplus_cat_entry entry;
4411da177e4SLinus Torvalds 	int entry_size, type;
4425bd9d99dSAlexey Khoroshilov 	int err;
4431da177e4SLinus Torvalds 
444c2b3e1f7SJoe Perches 	hfs_dbg(CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n",
4452753cc28SAnton Salikhmetov 		cnid, src_dir->i_ino, src_name->name,
4461da177e4SLinus Torvalds 		dst_dir->i_ino, dst_name->name);
4475bd9d99dSAlexey Khoroshilov 	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
4485bd9d99dSAlexey Khoroshilov 	if (err)
4495bd9d99dSAlexey Khoroshilov 		return err;
4501da177e4SLinus Torvalds 	dst_fd = src_fd;
4511da177e4SLinus Torvalds 
452d92915c3SErnesto A. Fernández 	/*
453d92915c3SErnesto A. Fernández 	 * Fail early and avoid ENOSPC during the btree operations. We may
454d92915c3SErnesto A. Fernández 	 * have to split the root node at most twice.
455d92915c3SErnesto A. Fernández 	 */
456d92915c3SErnesto A. Fernández 	err = hfs_bmap_reserve(src_fd.tree, 4 * (int)src_fd.tree->depth - 1);
457d92915c3SErnesto A. Fernández 	if (err)
458d92915c3SErnesto A. Fernández 		goto out;
459d92915c3SErnesto A. Fernández 
4601da177e4SLinus Torvalds 	/* find the old dir entry and read the data */
46189ac9b4dSSougata Santra 	err = hfsplus_cat_build_key(sb, src_fd.search_key,
46289ac9b4dSSougata Santra 			src_dir->i_ino, src_name);
46389ac9b4dSSougata Santra 	if (unlikely(err))
46489ac9b4dSSougata Santra 		goto out;
46589ac9b4dSSougata Santra 
466324ef39aSVyacheslav Dubeyko 	err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
4671da177e4SLinus Torvalds 	if (err)
4681da177e4SLinus Torvalds 		goto out;
4696f24f892SGreg Kroah-Hartman 	if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
4706f24f892SGreg Kroah-Hartman 		err = -EIO;
4716f24f892SGreg Kroah-Hartman 		goto out;
4726f24f892SGreg Kroah-Hartman 	}
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
4751da177e4SLinus Torvalds 				src_fd.entrylength);
476d7d673a5SSergei Antonov 	type = be16_to_cpu(entry.type);
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	/* create new dir entry with the data from the old entry */
47989ac9b4dSSougata Santra 	err = hfsplus_cat_build_key(sb, dst_fd.search_key,
48089ac9b4dSSougata Santra 			dst_dir->i_ino, dst_name);
48189ac9b4dSSougata Santra 	if (unlikely(err))
48289ac9b4dSSougata Santra 		goto out;
48389ac9b4dSSougata Santra 
484324ef39aSVyacheslav Dubeyko 	err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
4851da177e4SLinus Torvalds 	if (err != -ENOENT) {
4861da177e4SLinus Torvalds 		if (!err)
4871da177e4SLinus Torvalds 			err = -EEXIST;
4881da177e4SLinus Torvalds 		goto out;
4891da177e4SLinus Torvalds 	}
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds 	err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
4921da177e4SLinus Torvalds 	if (err)
4931da177e4SLinus Torvalds 		goto out;
4941da177e4SLinus Torvalds 	dst_dir->i_size++;
495d7d673a5SSergei Antonov 	if (type == HFSPLUS_FOLDER)
496d7d673a5SSergei Antonov 		hfsplus_subfolders_inc(dst_dir);
497*101fa821SJeff Layton 	dst_dir->i_mtime = inode_set_ctime_current(dst_dir);
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds 	/* finally remove the old entry */
50089ac9b4dSSougata Santra 	err = hfsplus_cat_build_key(sb, src_fd.search_key,
50189ac9b4dSSougata Santra 			src_dir->i_ino, src_name);
50289ac9b4dSSougata Santra 	if (unlikely(err))
50389ac9b4dSSougata Santra 		goto out;
50489ac9b4dSSougata Santra 
505324ef39aSVyacheslav Dubeyko 	err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
5061da177e4SLinus Torvalds 	if (err)
5071da177e4SLinus Torvalds 		goto out;
5081da177e4SLinus Torvalds 	err = hfs_brec_remove(&src_fd);
5091da177e4SLinus Torvalds 	if (err)
5101da177e4SLinus Torvalds 		goto out;
5111da177e4SLinus Torvalds 	src_dir->i_size--;
512d7d673a5SSergei Antonov 	if (type == HFSPLUS_FOLDER)
513d7d673a5SSergei Antonov 		hfsplus_subfolders_dec(src_dir);
514*101fa821SJeff Layton 	src_dir->i_mtime = inode_set_ctime_current(src_dir);
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 	/* remove old thread entry */
51789ac9b4dSSougata Santra 	hfsplus_cat_build_key_with_cnid(sb, src_fd.search_key, cnid);
518324ef39aSVyacheslav Dubeyko 	err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
5191da177e4SLinus Torvalds 	if (err)
5201da177e4SLinus Torvalds 		goto out;
5211da177e4SLinus Torvalds 	type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
5221da177e4SLinus Torvalds 	err = hfs_brec_remove(&src_fd);
5231da177e4SLinus Torvalds 	if (err)
5241da177e4SLinus Torvalds 		goto out;
5251da177e4SLinus Torvalds 
5261da177e4SLinus Torvalds 	/* create new thread entry */
52789ac9b4dSSougata Santra 	hfsplus_cat_build_key_with_cnid(sb, dst_fd.search_key, cnid);
5282753cc28SAnton Salikhmetov 	entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
5292753cc28SAnton Salikhmetov 		dst_dir->i_ino, dst_name);
53089ac9b4dSSougata Santra 	if (unlikely(entry_size < 0)) {
53189ac9b4dSSougata Santra 		err = entry_size;
53289ac9b4dSSougata Santra 		goto out;
53389ac9b4dSSougata Santra 	}
53489ac9b4dSSougata Santra 
535324ef39aSVyacheslav Dubeyko 	err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
5361da177e4SLinus Torvalds 	if (err != -ENOENT) {
5371da177e4SLinus Torvalds 		if (!err)
5381da177e4SLinus Torvalds 			err = -EEXIST;
5391da177e4SLinus Torvalds 		goto out;
5401da177e4SLinus Torvalds 	}
5411da177e4SLinus Torvalds 	err = hfs_brec_insert(&dst_fd, &entry, entry_size);
542e3494705SChristoph Hellwig 
543e3494705SChristoph Hellwig 	hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY);
544e3494705SChristoph Hellwig 	hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY);
5451da177e4SLinus Torvalds out:
5461da177e4SLinus Torvalds 	hfs_bnode_put(dst_fd.bnode);
5471da177e4SLinus Torvalds 	hfs_find_exit(&src_fd);
5481da177e4SLinus Torvalds 	return err;
5491da177e4SLinus Torvalds }
550