18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/hfsplus/dir.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 directories
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/random.h>
168c2ecf20Sopenharmony_ci#include <linux/nls.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "hfsplus_fs.h"
198c2ecf20Sopenharmony_ci#include "hfsplus_raw.h"
208c2ecf20Sopenharmony_ci#include "xattr.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic inline void hfsplus_instantiate(struct dentry *dentry,
238c2ecf20Sopenharmony_ci				       struct inode *inode, u32 cnid)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	dentry->d_fsdata = (void *)(unsigned long)cnid;
268c2ecf20Sopenharmony_ci	d_instantiate(dentry, inode);
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* Find the entry inside dir named dentry->d_name */
308c2ecf20Sopenharmony_cistatic struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
318c2ecf20Sopenharmony_ci				     unsigned int flags)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct inode *inode = NULL;
348c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
358c2ecf20Sopenharmony_ci	struct super_block *sb;
368c2ecf20Sopenharmony_ci	hfsplus_cat_entry entry;
378c2ecf20Sopenharmony_ci	int err;
388c2ecf20Sopenharmony_ci	u32 cnid, linkid = 0;
398c2ecf20Sopenharmony_ci	u16 type;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	sb = dir->i_sb;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	dentry->d_fsdata = NULL;
448c2ecf20Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
458c2ecf20Sopenharmony_ci	if (err)
468c2ecf20Sopenharmony_ci		return ERR_PTR(err);
478c2ecf20Sopenharmony_ci	err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino,
488c2ecf20Sopenharmony_ci			&dentry->d_name);
498c2ecf20Sopenharmony_ci	if (unlikely(err < 0))
508c2ecf20Sopenharmony_ci		goto fail;
518c2ecf20Sopenharmony_ciagain:
528c2ecf20Sopenharmony_ci	err = hfs_brec_read(&fd, &entry, sizeof(entry));
538c2ecf20Sopenharmony_ci	if (err) {
548c2ecf20Sopenharmony_ci		if (err == -ENOENT) {
558c2ecf20Sopenharmony_ci			hfs_find_exit(&fd);
568c2ecf20Sopenharmony_ci			/* No such entry */
578c2ecf20Sopenharmony_ci			inode = NULL;
588c2ecf20Sopenharmony_ci			goto out;
598c2ecf20Sopenharmony_ci		}
608c2ecf20Sopenharmony_ci		goto fail;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci	type = be16_to_cpu(entry.type);
638c2ecf20Sopenharmony_ci	if (type == HFSPLUS_FOLDER) {
648c2ecf20Sopenharmony_ci		if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
658c2ecf20Sopenharmony_ci			err = -EIO;
668c2ecf20Sopenharmony_ci			goto fail;
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci		cnid = be32_to_cpu(entry.folder.id);
698c2ecf20Sopenharmony_ci		dentry->d_fsdata = (void *)(unsigned long)cnid;
708c2ecf20Sopenharmony_ci	} else if (type == HFSPLUS_FILE) {
718c2ecf20Sopenharmony_ci		if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
728c2ecf20Sopenharmony_ci			err = -EIO;
738c2ecf20Sopenharmony_ci			goto fail;
748c2ecf20Sopenharmony_ci		}
758c2ecf20Sopenharmony_ci		cnid = be32_to_cpu(entry.file.id);
768c2ecf20Sopenharmony_ci		if (entry.file.user_info.fdType ==
778c2ecf20Sopenharmony_ci				cpu_to_be32(HFSP_HARDLINK_TYPE) &&
788c2ecf20Sopenharmony_ci				entry.file.user_info.fdCreator ==
798c2ecf20Sopenharmony_ci				cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
808c2ecf20Sopenharmony_ci				HFSPLUS_SB(sb)->hidden_dir &&
818c2ecf20Sopenharmony_ci				(entry.file.create_date ==
828c2ecf20Sopenharmony_ci					HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
838c2ecf20Sopenharmony_ci						create_date ||
848c2ecf20Sopenharmony_ci				entry.file.create_date ==
858c2ecf20Sopenharmony_ci					HFSPLUS_I(d_inode(sb->s_root))->
868c2ecf20Sopenharmony_ci						create_date)) {
878c2ecf20Sopenharmony_ci			struct qstr str;
888c2ecf20Sopenharmony_ci			char name[32];
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci			if (dentry->d_fsdata) {
918c2ecf20Sopenharmony_ci				/*
928c2ecf20Sopenharmony_ci				 * We found a link pointing to another link,
938c2ecf20Sopenharmony_ci				 * so ignore it and treat it as regular file.
948c2ecf20Sopenharmony_ci				 */
958c2ecf20Sopenharmony_ci				cnid = (unsigned long)dentry->d_fsdata;
968c2ecf20Sopenharmony_ci				linkid = 0;
978c2ecf20Sopenharmony_ci			} else {
988c2ecf20Sopenharmony_ci				dentry->d_fsdata = (void *)(unsigned long)cnid;
998c2ecf20Sopenharmony_ci				linkid =
1008c2ecf20Sopenharmony_ci					be32_to_cpu(entry.file.permissions.dev);
1018c2ecf20Sopenharmony_ci				str.len = sprintf(name, "iNode%d", linkid);
1028c2ecf20Sopenharmony_ci				str.name = name;
1038c2ecf20Sopenharmony_ci				err = hfsplus_cat_build_key(sb, fd.search_key,
1048c2ecf20Sopenharmony_ci					HFSPLUS_SB(sb)->hidden_dir->i_ino,
1058c2ecf20Sopenharmony_ci					&str);
1068c2ecf20Sopenharmony_ci				if (unlikely(err < 0))
1078c2ecf20Sopenharmony_ci					goto fail;
1088c2ecf20Sopenharmony_ci				goto again;
1098c2ecf20Sopenharmony_ci			}
1108c2ecf20Sopenharmony_ci		} else if (!dentry->d_fsdata)
1118c2ecf20Sopenharmony_ci			dentry->d_fsdata = (void *)(unsigned long)cnid;
1128c2ecf20Sopenharmony_ci	} else {
1138c2ecf20Sopenharmony_ci		pr_err("invalid catalog entry type in lookup\n");
1148c2ecf20Sopenharmony_ci		err = -EIO;
1158c2ecf20Sopenharmony_ci		goto fail;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
1188c2ecf20Sopenharmony_ci	inode = hfsplus_iget(dir->i_sb, cnid);
1198c2ecf20Sopenharmony_ci	if (IS_ERR(inode))
1208c2ecf20Sopenharmony_ci		return ERR_CAST(inode);
1218c2ecf20Sopenharmony_ci	if (S_ISREG(inode->i_mode))
1228c2ecf20Sopenharmony_ci		HFSPLUS_I(inode)->linkid = linkid;
1238c2ecf20Sopenharmony_ciout:
1248c2ecf20Sopenharmony_ci	return d_splice_alias(inode, dentry);
1258c2ecf20Sopenharmony_cifail:
1268c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
1278c2ecf20Sopenharmony_ci	return ERR_PTR(err);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int hfsplus_readdir(struct file *file, struct dir_context *ctx)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
1338c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
1348c2ecf20Sopenharmony_ci	int len, err;
1358c2ecf20Sopenharmony_ci	char *strbuf;
1368c2ecf20Sopenharmony_ci	hfsplus_cat_entry entry;
1378c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
1388c2ecf20Sopenharmony_ci	struct hfsplus_readdir_data *rd;
1398c2ecf20Sopenharmony_ci	u16 type;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (file->f_pos >= inode->i_size)
1428c2ecf20Sopenharmony_ci		return 0;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
1458c2ecf20Sopenharmony_ci	if (err)
1468c2ecf20Sopenharmony_ci		return err;
1478c2ecf20Sopenharmony_ci	strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL);
1488c2ecf20Sopenharmony_ci	if (!strbuf) {
1498c2ecf20Sopenharmony_ci		err = -ENOMEM;
1508c2ecf20Sopenharmony_ci		goto out;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci	hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino);
1538c2ecf20Sopenharmony_ci	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
1548c2ecf20Sopenharmony_ci	if (err)
1558c2ecf20Sopenharmony_ci		goto out;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (ctx->pos == 0) {
1588c2ecf20Sopenharmony_ci		/* This is completely artificial... */
1598c2ecf20Sopenharmony_ci		if (!dir_emit_dot(file, ctx))
1608c2ecf20Sopenharmony_ci			goto out;
1618c2ecf20Sopenharmony_ci		ctx->pos = 1;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci	if (ctx->pos == 1) {
1648c2ecf20Sopenharmony_ci		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
1658c2ecf20Sopenharmony_ci			err = -EIO;
1668c2ecf20Sopenharmony_ci			goto out;
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
1708c2ecf20Sopenharmony_ci			fd.entrylength);
1718c2ecf20Sopenharmony_ci		if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
1728c2ecf20Sopenharmony_ci			pr_err("bad catalog folder thread\n");
1738c2ecf20Sopenharmony_ci			err = -EIO;
1748c2ecf20Sopenharmony_ci			goto out;
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci		if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
1778c2ecf20Sopenharmony_ci			pr_err("truncated catalog thread\n");
1788c2ecf20Sopenharmony_ci			err = -EIO;
1798c2ecf20Sopenharmony_ci			goto out;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci		if (!dir_emit(ctx, "..", 2,
1828c2ecf20Sopenharmony_ci			    be32_to_cpu(entry.thread.parentID), DT_DIR))
1838c2ecf20Sopenharmony_ci			goto out;
1848c2ecf20Sopenharmony_ci		ctx->pos = 2;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	if (ctx->pos >= inode->i_size)
1878c2ecf20Sopenharmony_ci		goto out;
1888c2ecf20Sopenharmony_ci	err = hfs_brec_goto(&fd, ctx->pos - 1);
1898c2ecf20Sopenharmony_ci	if (err)
1908c2ecf20Sopenharmony_ci		goto out;
1918c2ecf20Sopenharmony_ci	for (;;) {
1928c2ecf20Sopenharmony_ci		if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
1938c2ecf20Sopenharmony_ci			pr_err("walked past end of dir\n");
1948c2ecf20Sopenharmony_ci			err = -EIO;
1958c2ecf20Sopenharmony_ci			goto out;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
1998c2ecf20Sopenharmony_ci			err = -EIO;
2008c2ecf20Sopenharmony_ci			goto out;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
2048c2ecf20Sopenharmony_ci			fd.entrylength);
2058c2ecf20Sopenharmony_ci		type = be16_to_cpu(entry.type);
2068c2ecf20Sopenharmony_ci		len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN;
2078c2ecf20Sopenharmony_ci		err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
2088c2ecf20Sopenharmony_ci		if (err)
2098c2ecf20Sopenharmony_ci			goto out;
2108c2ecf20Sopenharmony_ci		if (type == HFSPLUS_FOLDER) {
2118c2ecf20Sopenharmony_ci			if (fd.entrylength <
2128c2ecf20Sopenharmony_ci					sizeof(struct hfsplus_cat_folder)) {
2138c2ecf20Sopenharmony_ci				pr_err("small dir entry\n");
2148c2ecf20Sopenharmony_ci				err = -EIO;
2158c2ecf20Sopenharmony_ci				goto out;
2168c2ecf20Sopenharmony_ci			}
2178c2ecf20Sopenharmony_ci			if (HFSPLUS_SB(sb)->hidden_dir &&
2188c2ecf20Sopenharmony_ci			    HFSPLUS_SB(sb)->hidden_dir->i_ino ==
2198c2ecf20Sopenharmony_ci					be32_to_cpu(entry.folder.id))
2208c2ecf20Sopenharmony_ci				goto next;
2218c2ecf20Sopenharmony_ci			if (!dir_emit(ctx, strbuf, len,
2228c2ecf20Sopenharmony_ci				    be32_to_cpu(entry.folder.id), DT_DIR))
2238c2ecf20Sopenharmony_ci				break;
2248c2ecf20Sopenharmony_ci		} else if (type == HFSPLUS_FILE) {
2258c2ecf20Sopenharmony_ci			u16 mode;
2268c2ecf20Sopenharmony_ci			unsigned type = DT_UNKNOWN;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci			if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
2298c2ecf20Sopenharmony_ci				pr_err("small file entry\n");
2308c2ecf20Sopenharmony_ci				err = -EIO;
2318c2ecf20Sopenharmony_ci				goto out;
2328c2ecf20Sopenharmony_ci			}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci			mode = be16_to_cpu(entry.file.permissions.mode);
2358c2ecf20Sopenharmony_ci			if (S_ISREG(mode))
2368c2ecf20Sopenharmony_ci				type = DT_REG;
2378c2ecf20Sopenharmony_ci			else if (S_ISLNK(mode))
2388c2ecf20Sopenharmony_ci				type = DT_LNK;
2398c2ecf20Sopenharmony_ci			else if (S_ISFIFO(mode))
2408c2ecf20Sopenharmony_ci				type = DT_FIFO;
2418c2ecf20Sopenharmony_ci			else if (S_ISCHR(mode))
2428c2ecf20Sopenharmony_ci				type = DT_CHR;
2438c2ecf20Sopenharmony_ci			else if (S_ISBLK(mode))
2448c2ecf20Sopenharmony_ci				type = DT_BLK;
2458c2ecf20Sopenharmony_ci			else if (S_ISSOCK(mode))
2468c2ecf20Sopenharmony_ci				type = DT_SOCK;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci			if (!dir_emit(ctx, strbuf, len,
2498c2ecf20Sopenharmony_ci				      be32_to_cpu(entry.file.id), type))
2508c2ecf20Sopenharmony_ci				break;
2518c2ecf20Sopenharmony_ci		} else {
2528c2ecf20Sopenharmony_ci			pr_err("bad catalog entry type\n");
2538c2ecf20Sopenharmony_ci			err = -EIO;
2548c2ecf20Sopenharmony_ci			goto out;
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_cinext:
2578c2ecf20Sopenharmony_ci		ctx->pos++;
2588c2ecf20Sopenharmony_ci		if (ctx->pos >= inode->i_size)
2598c2ecf20Sopenharmony_ci			goto out;
2608c2ecf20Sopenharmony_ci		err = hfs_brec_goto(&fd, 1);
2618c2ecf20Sopenharmony_ci		if (err)
2628c2ecf20Sopenharmony_ci			goto out;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci	rd = file->private_data;
2658c2ecf20Sopenharmony_ci	if (!rd) {
2668c2ecf20Sopenharmony_ci		rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
2678c2ecf20Sopenharmony_ci		if (!rd) {
2688c2ecf20Sopenharmony_ci			err = -ENOMEM;
2698c2ecf20Sopenharmony_ci			goto out;
2708c2ecf20Sopenharmony_ci		}
2718c2ecf20Sopenharmony_ci		file->private_data = rd;
2728c2ecf20Sopenharmony_ci		rd->file = file;
2738c2ecf20Sopenharmony_ci		spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
2748c2ecf20Sopenharmony_ci		list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
2758c2ecf20Sopenharmony_ci		spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci	/*
2788c2ecf20Sopenharmony_ci	 * Can be done after the list insertion; exclusion with
2798c2ecf20Sopenharmony_ci	 * hfsplus_delete_cat() is provided by directory lock.
2808c2ecf20Sopenharmony_ci	 */
2818c2ecf20Sopenharmony_ci	memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
2828c2ecf20Sopenharmony_ciout:
2838c2ecf20Sopenharmony_ci	kfree(strbuf);
2848c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
2858c2ecf20Sopenharmony_ci	return err;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic int hfsplus_dir_release(struct inode *inode, struct file *file)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct hfsplus_readdir_data *rd = file->private_data;
2918c2ecf20Sopenharmony_ci	if (rd) {
2928c2ecf20Sopenharmony_ci		spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
2938c2ecf20Sopenharmony_ci		list_del(&rd->list);
2948c2ecf20Sopenharmony_ci		spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
2958c2ecf20Sopenharmony_ci		kfree(rd);
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
3018c2ecf20Sopenharmony_ci			struct dentry *dst_dentry)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
3048c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(src_dentry);
3058c2ecf20Sopenharmony_ci	struct inode *src_dir = d_inode(src_dentry->d_parent);
3068c2ecf20Sopenharmony_ci	struct qstr str;
3078c2ecf20Sopenharmony_ci	char name[32];
3088c2ecf20Sopenharmony_ci	u32 cnid, id;
3098c2ecf20Sopenharmony_ci	int res;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (HFSPLUS_IS_RSRC(inode))
3128c2ecf20Sopenharmony_ci		return -EPERM;
3138c2ecf20Sopenharmony_ci	if (!S_ISREG(inode->i_mode))
3148c2ecf20Sopenharmony_ci		return -EPERM;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	mutex_lock(&sbi->vh_mutex);
3178c2ecf20Sopenharmony_ci	if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
3188c2ecf20Sopenharmony_ci		for (;;) {
3198c2ecf20Sopenharmony_ci			get_random_bytes(&id, sizeof(cnid));
3208c2ecf20Sopenharmony_ci			id &= 0x3fffffff;
3218c2ecf20Sopenharmony_ci			str.name = name;
3228c2ecf20Sopenharmony_ci			str.len = sprintf(name, "iNode%d", id);
3238c2ecf20Sopenharmony_ci			res = hfsplus_rename_cat(inode->i_ino,
3248c2ecf20Sopenharmony_ci						 src_dir, &src_dentry->d_name,
3258c2ecf20Sopenharmony_ci						 sbi->hidden_dir, &str);
3268c2ecf20Sopenharmony_ci			if (!res)
3278c2ecf20Sopenharmony_ci				break;
3288c2ecf20Sopenharmony_ci			if (res != -EEXIST)
3298c2ecf20Sopenharmony_ci				goto out;
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci		HFSPLUS_I(inode)->linkid = id;
3328c2ecf20Sopenharmony_ci		cnid = sbi->next_cnid++;
3338c2ecf20Sopenharmony_ci		src_dentry->d_fsdata = (void *)(unsigned long)cnid;
3348c2ecf20Sopenharmony_ci		res = hfsplus_create_cat(cnid, src_dir,
3358c2ecf20Sopenharmony_ci			&src_dentry->d_name, inode);
3368c2ecf20Sopenharmony_ci		if (res)
3378c2ecf20Sopenharmony_ci			/* panic? */
3388c2ecf20Sopenharmony_ci			goto out;
3398c2ecf20Sopenharmony_ci		sbi->file_count++;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci	cnid = sbi->next_cnid++;
3428c2ecf20Sopenharmony_ci	res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
3438c2ecf20Sopenharmony_ci	if (res)
3448c2ecf20Sopenharmony_ci		goto out;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	inc_nlink(inode);
3478c2ecf20Sopenharmony_ci	hfsplus_instantiate(dst_dentry, inode, cnid);
3488c2ecf20Sopenharmony_ci	ihold(inode);
3498c2ecf20Sopenharmony_ci	inode->i_ctime = current_time(inode);
3508c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
3518c2ecf20Sopenharmony_ci	sbi->file_count++;
3528c2ecf20Sopenharmony_ci	hfsplus_mark_mdb_dirty(dst_dir->i_sb);
3538c2ecf20Sopenharmony_ciout:
3548c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->vh_mutex);
3558c2ecf20Sopenharmony_ci	return res;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
3618c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry);
3628c2ecf20Sopenharmony_ci	struct qstr str;
3638c2ecf20Sopenharmony_ci	char name[32];
3648c2ecf20Sopenharmony_ci	u32 cnid;
3658c2ecf20Sopenharmony_ci	int res;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (HFSPLUS_IS_RSRC(inode))
3688c2ecf20Sopenharmony_ci		return -EPERM;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	mutex_lock(&sbi->vh_mutex);
3718c2ecf20Sopenharmony_ci	cnid = (u32)(unsigned long)dentry->d_fsdata;
3728c2ecf20Sopenharmony_ci	if (inode->i_ino == cnid &&
3738c2ecf20Sopenharmony_ci	    atomic_read(&HFSPLUS_I(inode)->opencnt)) {
3748c2ecf20Sopenharmony_ci		str.name = name;
3758c2ecf20Sopenharmony_ci		str.len = sprintf(name, "temp%lu", inode->i_ino);
3768c2ecf20Sopenharmony_ci		res = hfsplus_rename_cat(inode->i_ino,
3778c2ecf20Sopenharmony_ci					 dir, &dentry->d_name,
3788c2ecf20Sopenharmony_ci					 sbi->hidden_dir, &str);
3798c2ecf20Sopenharmony_ci		if (!res) {
3808c2ecf20Sopenharmony_ci			inode->i_flags |= S_DEAD;
3818c2ecf20Sopenharmony_ci			drop_nlink(inode);
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci		goto out;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci	res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
3868c2ecf20Sopenharmony_ci	if (res)
3878c2ecf20Sopenharmony_ci		goto out;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (inode->i_nlink > 0)
3908c2ecf20Sopenharmony_ci		drop_nlink(inode);
3918c2ecf20Sopenharmony_ci	if (inode->i_ino == cnid)
3928c2ecf20Sopenharmony_ci		clear_nlink(inode);
3938c2ecf20Sopenharmony_ci	if (!inode->i_nlink) {
3948c2ecf20Sopenharmony_ci		if (inode->i_ino != cnid) {
3958c2ecf20Sopenharmony_ci			sbi->file_count--;
3968c2ecf20Sopenharmony_ci			if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
3978c2ecf20Sopenharmony_ci				res = hfsplus_delete_cat(inode->i_ino,
3988c2ecf20Sopenharmony_ci							 sbi->hidden_dir,
3998c2ecf20Sopenharmony_ci							 NULL);
4008c2ecf20Sopenharmony_ci				if (!res)
4018c2ecf20Sopenharmony_ci					hfsplus_delete_inode(inode);
4028c2ecf20Sopenharmony_ci			} else
4038c2ecf20Sopenharmony_ci				inode->i_flags |= S_DEAD;
4048c2ecf20Sopenharmony_ci		} else
4058c2ecf20Sopenharmony_ci			hfsplus_delete_inode(inode);
4068c2ecf20Sopenharmony_ci	} else
4078c2ecf20Sopenharmony_ci		sbi->file_count--;
4088c2ecf20Sopenharmony_ci	inode->i_ctime = current_time(inode);
4098c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
4108c2ecf20Sopenharmony_ciout:
4118c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->vh_mutex);
4128c2ecf20Sopenharmony_ci	return res;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
4188c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry);
4198c2ecf20Sopenharmony_ci	int res;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (inode->i_size != 2)
4228c2ecf20Sopenharmony_ci		return -ENOTEMPTY;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	mutex_lock(&sbi->vh_mutex);
4258c2ecf20Sopenharmony_ci	res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
4268c2ecf20Sopenharmony_ci	if (res)
4278c2ecf20Sopenharmony_ci		goto out;
4288c2ecf20Sopenharmony_ci	clear_nlink(inode);
4298c2ecf20Sopenharmony_ci	inode->i_ctime = current_time(inode);
4308c2ecf20Sopenharmony_ci	hfsplus_delete_inode(inode);
4318c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
4328c2ecf20Sopenharmony_ciout:
4338c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->vh_mutex);
4348c2ecf20Sopenharmony_ci	return res;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
4388c2ecf20Sopenharmony_ci			   const char *symname)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
4418c2ecf20Sopenharmony_ci	struct inode *inode;
4428c2ecf20Sopenharmony_ci	int res = -ENOMEM;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	mutex_lock(&sbi->vh_mutex);
4458c2ecf20Sopenharmony_ci	inode = hfsplus_new_inode(dir->i_sb, dir, S_IFLNK | S_IRWXUGO);
4468c2ecf20Sopenharmony_ci	if (!inode)
4478c2ecf20Sopenharmony_ci		goto out;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	res = page_symlink(inode, symname, strlen(symname) + 1);
4508c2ecf20Sopenharmony_ci	if (res)
4518c2ecf20Sopenharmony_ci		goto out_err;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
4548c2ecf20Sopenharmony_ci	if (res)
4558c2ecf20Sopenharmony_ci		goto out_err;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	res = hfsplus_init_security(inode, dir, &dentry->d_name);
4588c2ecf20Sopenharmony_ci	if (res == -EOPNOTSUPP)
4598c2ecf20Sopenharmony_ci		res = 0; /* Operation is not supported. */
4608c2ecf20Sopenharmony_ci	else if (res) {
4618c2ecf20Sopenharmony_ci		/* Try to delete anyway without error analysis. */
4628c2ecf20Sopenharmony_ci		hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
4638c2ecf20Sopenharmony_ci		goto out_err;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	hfsplus_instantiate(dentry, inode, inode->i_ino);
4678c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
4688c2ecf20Sopenharmony_ci	goto out;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ciout_err:
4718c2ecf20Sopenharmony_ci	clear_nlink(inode);
4728c2ecf20Sopenharmony_ci	hfsplus_delete_inode(inode);
4738c2ecf20Sopenharmony_ci	iput(inode);
4748c2ecf20Sopenharmony_ciout:
4758c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->vh_mutex);
4768c2ecf20Sopenharmony_ci	return res;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
4808c2ecf20Sopenharmony_ci			 umode_t mode, dev_t rdev)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
4838c2ecf20Sopenharmony_ci	struct inode *inode;
4848c2ecf20Sopenharmony_ci	int res = -ENOMEM;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	mutex_lock(&sbi->vh_mutex);
4878c2ecf20Sopenharmony_ci	inode = hfsplus_new_inode(dir->i_sb, dir, mode);
4888c2ecf20Sopenharmony_ci	if (!inode)
4898c2ecf20Sopenharmony_ci		goto out;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
4928c2ecf20Sopenharmony_ci		init_special_inode(inode, mode, rdev);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
4958c2ecf20Sopenharmony_ci	if (res)
4968c2ecf20Sopenharmony_ci		goto failed_mknod;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	res = hfsplus_init_security(inode, dir, &dentry->d_name);
4998c2ecf20Sopenharmony_ci	if (res == -EOPNOTSUPP)
5008c2ecf20Sopenharmony_ci		res = 0; /* Operation is not supported. */
5018c2ecf20Sopenharmony_ci	else if (res) {
5028c2ecf20Sopenharmony_ci		/* Try to delete anyway without error analysis. */
5038c2ecf20Sopenharmony_ci		hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
5048c2ecf20Sopenharmony_ci		goto failed_mknod;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	hfsplus_instantiate(dentry, inode, inode->i_ino);
5088c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
5098c2ecf20Sopenharmony_ci	goto out;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cifailed_mknod:
5128c2ecf20Sopenharmony_ci	clear_nlink(inode);
5138c2ecf20Sopenharmony_ci	hfsplus_delete_inode(inode);
5148c2ecf20Sopenharmony_ci	iput(inode);
5158c2ecf20Sopenharmony_ciout:
5168c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->vh_mutex);
5178c2ecf20Sopenharmony_ci	return res;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
5218c2ecf20Sopenharmony_ci			  bool excl)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	return hfsplus_mknod(dir, dentry, mode, 0);
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
5328c2ecf20Sopenharmony_ci			  struct inode *new_dir, struct dentry *new_dentry,
5338c2ecf20Sopenharmony_ci			  unsigned int flags)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	int res;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	if (flags & ~RENAME_NOREPLACE)
5388c2ecf20Sopenharmony_ci		return -EINVAL;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* Unlink destination if it already exists */
5418c2ecf20Sopenharmony_ci	if (d_really_is_positive(new_dentry)) {
5428c2ecf20Sopenharmony_ci		if (d_is_dir(new_dentry))
5438c2ecf20Sopenharmony_ci			res = hfsplus_rmdir(new_dir, new_dentry);
5448c2ecf20Sopenharmony_ci		else
5458c2ecf20Sopenharmony_ci			res = hfsplus_unlink(new_dir, new_dentry);
5468c2ecf20Sopenharmony_ci		if (res)
5478c2ecf20Sopenharmony_ci			return res;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
5518c2ecf20Sopenharmony_ci				 old_dir, &old_dentry->d_name,
5528c2ecf20Sopenharmony_ci				 new_dir, &new_dentry->d_name);
5538c2ecf20Sopenharmony_ci	if (!res)
5548c2ecf20Sopenharmony_ci		new_dentry->d_fsdata = old_dentry->d_fsdata;
5558c2ecf20Sopenharmony_ci	return res;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ciconst struct inode_operations hfsplus_dir_inode_operations = {
5598c2ecf20Sopenharmony_ci	.lookup			= hfsplus_lookup,
5608c2ecf20Sopenharmony_ci	.create			= hfsplus_create,
5618c2ecf20Sopenharmony_ci	.link			= hfsplus_link,
5628c2ecf20Sopenharmony_ci	.unlink			= hfsplus_unlink,
5638c2ecf20Sopenharmony_ci	.mkdir			= hfsplus_mkdir,
5648c2ecf20Sopenharmony_ci	.rmdir			= hfsplus_rmdir,
5658c2ecf20Sopenharmony_ci	.symlink		= hfsplus_symlink,
5668c2ecf20Sopenharmony_ci	.mknod			= hfsplus_mknod,
5678c2ecf20Sopenharmony_ci	.rename			= hfsplus_rename,
5688c2ecf20Sopenharmony_ci	.getattr		= hfsplus_getattr,
5698c2ecf20Sopenharmony_ci	.listxattr		= hfsplus_listxattr,
5708c2ecf20Sopenharmony_ci};
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ciconst struct file_operations hfsplus_dir_operations = {
5738c2ecf20Sopenharmony_ci	.fsync		= hfsplus_file_fsync,
5748c2ecf20Sopenharmony_ci	.read		= generic_read_dir,
5758c2ecf20Sopenharmony_ci	.iterate_shared	= hfsplus_readdir,
5768c2ecf20Sopenharmony_ci	.unlocked_ioctl = hfsplus_ioctl,
5778c2ecf20Sopenharmony_ci	.llseek		= generic_file_llseek,
5788c2ecf20Sopenharmony_ci	.release	= hfsplus_dir_release,
5798c2ecf20Sopenharmony_ci};
580