xref: /kernel/linux/linux-6.6/fs/hpfs/namei.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/hpfs/namei.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  adding & removing files & directories
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/sched.h>
1062306a36Sopenharmony_ci#include "hpfs_fn.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic void hpfs_update_directory_times(struct inode *dir)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	time64_t t = local_to_gmt(dir->i_sb, local_get_seconds(dir->i_sb));
1562306a36Sopenharmony_ci	if (t == dir->i_mtime.tv_sec &&
1662306a36Sopenharmony_ci	    t == inode_get_ctime(dir).tv_sec)
1762306a36Sopenharmony_ci		return;
1862306a36Sopenharmony_ci	dir->i_mtime = inode_set_ctime(dir, t, 0);
1962306a36Sopenharmony_ci	hpfs_write_inode_nolock(dir);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int hpfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
2362306a36Sopenharmony_ci		      struct dentry *dentry, umode_t mode)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	const unsigned char *name = dentry->d_name.name;
2662306a36Sopenharmony_ci	unsigned len = dentry->d_name.len;
2762306a36Sopenharmony_ci	struct quad_buffer_head qbh0;
2862306a36Sopenharmony_ci	struct buffer_head *bh;
2962306a36Sopenharmony_ci	struct hpfs_dirent *de;
3062306a36Sopenharmony_ci	struct fnode *fnode;
3162306a36Sopenharmony_ci	struct dnode *dnode;
3262306a36Sopenharmony_ci	struct inode *result;
3362306a36Sopenharmony_ci	fnode_secno fno;
3462306a36Sopenharmony_ci	dnode_secno dno;
3562306a36Sopenharmony_ci	int r;
3662306a36Sopenharmony_ci	struct hpfs_dirent dee;
3762306a36Sopenharmony_ci	int err;
3862306a36Sopenharmony_ci	if ((err = hpfs_chk_name(name, &len))) return err==-ENOENT ? -EINVAL : err;
3962306a36Sopenharmony_ci	hpfs_lock(dir->i_sb);
4062306a36Sopenharmony_ci	err = -ENOSPC;
4162306a36Sopenharmony_ci	fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
4262306a36Sopenharmony_ci	if (!fnode)
4362306a36Sopenharmony_ci		goto bail;
4462306a36Sopenharmony_ci	dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0);
4562306a36Sopenharmony_ci	if (!dnode)
4662306a36Sopenharmony_ci		goto bail1;
4762306a36Sopenharmony_ci	memset(&dee, 0, sizeof dee);
4862306a36Sopenharmony_ci	dee.directory = 1;
4962306a36Sopenharmony_ci	if (!(mode & 0222)) dee.read_only = 1;
5062306a36Sopenharmony_ci	/*dee.archive = 0;*/
5162306a36Sopenharmony_ci	dee.hidden = name[0] == '.';
5262306a36Sopenharmony_ci	dee.fnode = cpu_to_le32(fno);
5362306a36Sopenharmony_ci	dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(local_get_seconds(dir->i_sb));
5462306a36Sopenharmony_ci	result = new_inode(dir->i_sb);
5562306a36Sopenharmony_ci	if (!result)
5662306a36Sopenharmony_ci		goto bail2;
5762306a36Sopenharmony_ci	hpfs_init_inode(result);
5862306a36Sopenharmony_ci	result->i_ino = fno;
5962306a36Sopenharmony_ci	hpfs_i(result)->i_parent_dir = dir->i_ino;
6062306a36Sopenharmony_ci	hpfs_i(result)->i_dno = dno;
6162306a36Sopenharmony_ci	result->i_mtime = result->i_atime =
6262306a36Sopenharmony_ci		inode_set_ctime(result, local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date)), 0);
6362306a36Sopenharmony_ci	hpfs_i(result)->i_ea_size = 0;
6462306a36Sopenharmony_ci	result->i_mode |= S_IFDIR;
6562306a36Sopenharmony_ci	result->i_op = &hpfs_dir_iops;
6662306a36Sopenharmony_ci	result->i_fop = &hpfs_dir_ops;
6762306a36Sopenharmony_ci	result->i_blocks = 4;
6862306a36Sopenharmony_ci	result->i_size = 2048;
6962306a36Sopenharmony_ci	set_nlink(result, 2);
7062306a36Sopenharmony_ci	if (dee.read_only)
7162306a36Sopenharmony_ci		result->i_mode &= ~0222;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	r = hpfs_add_dirent(dir, name, len, &dee);
7462306a36Sopenharmony_ci	if (r == 1)
7562306a36Sopenharmony_ci		goto bail3;
7662306a36Sopenharmony_ci	if (r == -1) {
7762306a36Sopenharmony_ci		err = -EEXIST;
7862306a36Sopenharmony_ci		goto bail3;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci	fnode->len = len;
8162306a36Sopenharmony_ci	memcpy(fnode->name, name, len > 15 ? 15 : len);
8262306a36Sopenharmony_ci	fnode->up = cpu_to_le32(dir->i_ino);
8362306a36Sopenharmony_ci	fnode->flags |= FNODE_dir;
8462306a36Sopenharmony_ci	fnode->btree.n_free_nodes = 7;
8562306a36Sopenharmony_ci	fnode->btree.n_used_nodes = 1;
8662306a36Sopenharmony_ci	fnode->btree.first_free = cpu_to_le16(0x14);
8762306a36Sopenharmony_ci	fnode->u.external[0].disk_secno = cpu_to_le32(dno);
8862306a36Sopenharmony_ci	fnode->u.external[0].file_secno = cpu_to_le32(-1);
8962306a36Sopenharmony_ci	dnode->root_dnode = 1;
9062306a36Sopenharmony_ci	dnode->up = cpu_to_le32(fno);
9162306a36Sopenharmony_ci	de = hpfs_add_de(dir->i_sb, dnode, "\001\001", 2, 0);
9262306a36Sopenharmony_ci	de->creation_date = de->write_date = de->read_date = cpu_to_le32(local_get_seconds(dir->i_sb));
9362306a36Sopenharmony_ci	if (!(mode & 0222)) de->read_only = 1;
9462306a36Sopenharmony_ci	de->first = de->directory = 1;
9562306a36Sopenharmony_ci	/*de->hidden = de->system = 0;*/
9662306a36Sopenharmony_ci	de->fnode = cpu_to_le32(fno);
9762306a36Sopenharmony_ci	mark_buffer_dirty(bh);
9862306a36Sopenharmony_ci	brelse(bh);
9962306a36Sopenharmony_ci	hpfs_mark_4buffers_dirty(&qbh0);
10062306a36Sopenharmony_ci	hpfs_brelse4(&qbh0);
10162306a36Sopenharmony_ci	inc_nlink(dir);
10262306a36Sopenharmony_ci	insert_inode_hash(result);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (!uid_eq(result->i_uid, current_fsuid()) ||
10562306a36Sopenharmony_ci	    !gid_eq(result->i_gid, current_fsgid()) ||
10662306a36Sopenharmony_ci	    result->i_mode != (mode | S_IFDIR)) {
10762306a36Sopenharmony_ci		result->i_uid = current_fsuid();
10862306a36Sopenharmony_ci		result->i_gid = current_fsgid();
10962306a36Sopenharmony_ci		result->i_mode = mode | S_IFDIR;
11062306a36Sopenharmony_ci		hpfs_write_inode_nolock(result);
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci	hpfs_update_directory_times(dir);
11362306a36Sopenharmony_ci	d_instantiate(dentry, result);
11462306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_cibail3:
11762306a36Sopenharmony_ci	iput(result);
11862306a36Sopenharmony_cibail2:
11962306a36Sopenharmony_ci	hpfs_brelse4(&qbh0);
12062306a36Sopenharmony_ci	hpfs_free_dnode(dir->i_sb, dno);
12162306a36Sopenharmony_cibail1:
12262306a36Sopenharmony_ci	brelse(bh);
12362306a36Sopenharmony_ci	hpfs_free_sectors(dir->i_sb, fno, 1);
12462306a36Sopenharmony_cibail:
12562306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
12662306a36Sopenharmony_ci	return err;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int hpfs_create(struct mnt_idmap *idmap, struct inode *dir,
13062306a36Sopenharmony_ci		       struct dentry *dentry, umode_t mode, bool excl)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	const unsigned char *name = dentry->d_name.name;
13362306a36Sopenharmony_ci	unsigned len = dentry->d_name.len;
13462306a36Sopenharmony_ci	struct inode *result = NULL;
13562306a36Sopenharmony_ci	struct buffer_head *bh;
13662306a36Sopenharmony_ci	struct fnode *fnode;
13762306a36Sopenharmony_ci	fnode_secno fno;
13862306a36Sopenharmony_ci	int r;
13962306a36Sopenharmony_ci	struct hpfs_dirent dee;
14062306a36Sopenharmony_ci	int err;
14162306a36Sopenharmony_ci	if ((err = hpfs_chk_name(name, &len)))
14262306a36Sopenharmony_ci		return err==-ENOENT ? -EINVAL : err;
14362306a36Sopenharmony_ci	hpfs_lock(dir->i_sb);
14462306a36Sopenharmony_ci	err = -ENOSPC;
14562306a36Sopenharmony_ci	fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
14662306a36Sopenharmony_ci	if (!fnode)
14762306a36Sopenharmony_ci		goto bail;
14862306a36Sopenharmony_ci	memset(&dee, 0, sizeof dee);
14962306a36Sopenharmony_ci	if (!(mode & 0222)) dee.read_only = 1;
15062306a36Sopenharmony_ci	dee.archive = 1;
15162306a36Sopenharmony_ci	dee.hidden = name[0] == '.';
15262306a36Sopenharmony_ci	dee.fnode = cpu_to_le32(fno);
15362306a36Sopenharmony_ci	dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(local_get_seconds(dir->i_sb));
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	result = new_inode(dir->i_sb);
15662306a36Sopenharmony_ci	if (!result)
15762306a36Sopenharmony_ci		goto bail1;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	hpfs_init_inode(result);
16062306a36Sopenharmony_ci	result->i_ino = fno;
16162306a36Sopenharmony_ci	result->i_mode |= S_IFREG;
16262306a36Sopenharmony_ci	result->i_mode &= ~0111;
16362306a36Sopenharmony_ci	result->i_op = &hpfs_file_iops;
16462306a36Sopenharmony_ci	result->i_fop = &hpfs_file_ops;
16562306a36Sopenharmony_ci	set_nlink(result, 1);
16662306a36Sopenharmony_ci	hpfs_i(result)->i_parent_dir = dir->i_ino;
16762306a36Sopenharmony_ci	result->i_mtime = result->i_atime =
16862306a36Sopenharmony_ci		inode_set_ctime(result, local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date)), 0);
16962306a36Sopenharmony_ci	hpfs_i(result)->i_ea_size = 0;
17062306a36Sopenharmony_ci	if (dee.read_only)
17162306a36Sopenharmony_ci		result->i_mode &= ~0222;
17262306a36Sopenharmony_ci	result->i_blocks = 1;
17362306a36Sopenharmony_ci	result->i_size = 0;
17462306a36Sopenharmony_ci	result->i_data.a_ops = &hpfs_aops;
17562306a36Sopenharmony_ci	hpfs_i(result)->mmu_private = 0;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	r = hpfs_add_dirent(dir, name, len, &dee);
17862306a36Sopenharmony_ci	if (r == 1)
17962306a36Sopenharmony_ci		goto bail2;
18062306a36Sopenharmony_ci	if (r == -1) {
18162306a36Sopenharmony_ci		err = -EEXIST;
18262306a36Sopenharmony_ci		goto bail2;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci	fnode->len = len;
18562306a36Sopenharmony_ci	memcpy(fnode->name, name, len > 15 ? 15 : len);
18662306a36Sopenharmony_ci	fnode->up = cpu_to_le32(dir->i_ino);
18762306a36Sopenharmony_ci	mark_buffer_dirty(bh);
18862306a36Sopenharmony_ci	brelse(bh);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	insert_inode_hash(result);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (!uid_eq(result->i_uid, current_fsuid()) ||
19362306a36Sopenharmony_ci	    !gid_eq(result->i_gid, current_fsgid()) ||
19462306a36Sopenharmony_ci	    result->i_mode != (mode | S_IFREG)) {
19562306a36Sopenharmony_ci		result->i_uid = current_fsuid();
19662306a36Sopenharmony_ci		result->i_gid = current_fsgid();
19762306a36Sopenharmony_ci		result->i_mode = mode | S_IFREG;
19862306a36Sopenharmony_ci		hpfs_write_inode_nolock(result);
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	hpfs_update_directory_times(dir);
20162306a36Sopenharmony_ci	d_instantiate(dentry, result);
20262306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cibail2:
20662306a36Sopenharmony_ci	iput(result);
20762306a36Sopenharmony_cibail1:
20862306a36Sopenharmony_ci	brelse(bh);
20962306a36Sopenharmony_ci	hpfs_free_sectors(dir->i_sb, fno, 1);
21062306a36Sopenharmony_cibail:
21162306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
21262306a36Sopenharmony_ci	return err;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int hpfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
21662306a36Sopenharmony_ci		      struct dentry *dentry, umode_t mode, dev_t rdev)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	const unsigned char *name = dentry->d_name.name;
21962306a36Sopenharmony_ci	unsigned len = dentry->d_name.len;
22062306a36Sopenharmony_ci	struct buffer_head *bh;
22162306a36Sopenharmony_ci	struct fnode *fnode;
22262306a36Sopenharmony_ci	fnode_secno fno;
22362306a36Sopenharmony_ci	int r;
22462306a36Sopenharmony_ci	struct hpfs_dirent dee;
22562306a36Sopenharmony_ci	struct inode *result = NULL;
22662306a36Sopenharmony_ci	int err;
22762306a36Sopenharmony_ci	if ((err = hpfs_chk_name(name, &len))) return err==-ENOENT ? -EINVAL : err;
22862306a36Sopenharmony_ci	if (hpfs_sb(dir->i_sb)->sb_eas < 2) return -EPERM;
22962306a36Sopenharmony_ci	hpfs_lock(dir->i_sb);
23062306a36Sopenharmony_ci	err = -ENOSPC;
23162306a36Sopenharmony_ci	fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
23262306a36Sopenharmony_ci	if (!fnode)
23362306a36Sopenharmony_ci		goto bail;
23462306a36Sopenharmony_ci	memset(&dee, 0, sizeof dee);
23562306a36Sopenharmony_ci	if (!(mode & 0222)) dee.read_only = 1;
23662306a36Sopenharmony_ci	dee.archive = 1;
23762306a36Sopenharmony_ci	dee.hidden = name[0] == '.';
23862306a36Sopenharmony_ci	dee.fnode = cpu_to_le32(fno);
23962306a36Sopenharmony_ci	dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(local_get_seconds(dir->i_sb));
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	result = new_inode(dir->i_sb);
24262306a36Sopenharmony_ci	if (!result)
24362306a36Sopenharmony_ci		goto bail1;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	hpfs_init_inode(result);
24662306a36Sopenharmony_ci	result->i_ino = fno;
24762306a36Sopenharmony_ci	hpfs_i(result)->i_parent_dir = dir->i_ino;
24862306a36Sopenharmony_ci	result->i_mtime = result->i_atime =
24962306a36Sopenharmony_ci		inode_set_ctime(result, local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date)), 0);
25062306a36Sopenharmony_ci	hpfs_i(result)->i_ea_size = 0;
25162306a36Sopenharmony_ci	result->i_uid = current_fsuid();
25262306a36Sopenharmony_ci	result->i_gid = current_fsgid();
25362306a36Sopenharmony_ci	set_nlink(result, 1);
25462306a36Sopenharmony_ci	result->i_size = 0;
25562306a36Sopenharmony_ci	result->i_blocks = 1;
25662306a36Sopenharmony_ci	init_special_inode(result, mode, rdev);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	r = hpfs_add_dirent(dir, name, len, &dee);
25962306a36Sopenharmony_ci	if (r == 1)
26062306a36Sopenharmony_ci		goto bail2;
26162306a36Sopenharmony_ci	if (r == -1) {
26262306a36Sopenharmony_ci		err = -EEXIST;
26362306a36Sopenharmony_ci		goto bail2;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci	fnode->len = len;
26662306a36Sopenharmony_ci	memcpy(fnode->name, name, len > 15 ? 15 : len);
26762306a36Sopenharmony_ci	fnode->up = cpu_to_le32(dir->i_ino);
26862306a36Sopenharmony_ci	mark_buffer_dirty(bh);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	insert_inode_hash(result);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	hpfs_write_inode_nolock(result);
27362306a36Sopenharmony_ci	hpfs_update_directory_times(dir);
27462306a36Sopenharmony_ci	d_instantiate(dentry, result);
27562306a36Sopenharmony_ci	brelse(bh);
27662306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_cibail2:
27962306a36Sopenharmony_ci	iput(result);
28062306a36Sopenharmony_cibail1:
28162306a36Sopenharmony_ci	brelse(bh);
28262306a36Sopenharmony_ci	hpfs_free_sectors(dir->i_sb, fno, 1);
28362306a36Sopenharmony_cibail:
28462306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
28562306a36Sopenharmony_ci	return err;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int hpfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
28962306a36Sopenharmony_ci			struct dentry *dentry, const char *symlink)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	const unsigned char *name = dentry->d_name.name;
29262306a36Sopenharmony_ci	unsigned len = dentry->d_name.len;
29362306a36Sopenharmony_ci	struct buffer_head *bh;
29462306a36Sopenharmony_ci	struct fnode *fnode;
29562306a36Sopenharmony_ci	fnode_secno fno;
29662306a36Sopenharmony_ci	int r;
29762306a36Sopenharmony_ci	struct hpfs_dirent dee;
29862306a36Sopenharmony_ci	struct inode *result;
29962306a36Sopenharmony_ci	int err;
30062306a36Sopenharmony_ci	if ((err = hpfs_chk_name(name, &len))) return err==-ENOENT ? -EINVAL : err;
30162306a36Sopenharmony_ci	hpfs_lock(dir->i_sb);
30262306a36Sopenharmony_ci	if (hpfs_sb(dir->i_sb)->sb_eas < 2) {
30362306a36Sopenharmony_ci		hpfs_unlock(dir->i_sb);
30462306a36Sopenharmony_ci		return -EPERM;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	err = -ENOSPC;
30762306a36Sopenharmony_ci	fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
30862306a36Sopenharmony_ci	if (!fnode)
30962306a36Sopenharmony_ci		goto bail;
31062306a36Sopenharmony_ci	memset(&dee, 0, sizeof dee);
31162306a36Sopenharmony_ci	dee.archive = 1;
31262306a36Sopenharmony_ci	dee.hidden = name[0] == '.';
31362306a36Sopenharmony_ci	dee.fnode = cpu_to_le32(fno);
31462306a36Sopenharmony_ci	dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(local_get_seconds(dir->i_sb));
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	result = new_inode(dir->i_sb);
31762306a36Sopenharmony_ci	if (!result)
31862306a36Sopenharmony_ci		goto bail1;
31962306a36Sopenharmony_ci	result->i_ino = fno;
32062306a36Sopenharmony_ci	hpfs_init_inode(result);
32162306a36Sopenharmony_ci	hpfs_i(result)->i_parent_dir = dir->i_ino;
32262306a36Sopenharmony_ci	result->i_mtime = result->i_atime =
32362306a36Sopenharmony_ci		inode_set_ctime(result, local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date)), 0);
32462306a36Sopenharmony_ci	hpfs_i(result)->i_ea_size = 0;
32562306a36Sopenharmony_ci	result->i_mode = S_IFLNK | 0777;
32662306a36Sopenharmony_ci	result->i_uid = current_fsuid();
32762306a36Sopenharmony_ci	result->i_gid = current_fsgid();
32862306a36Sopenharmony_ci	result->i_blocks = 1;
32962306a36Sopenharmony_ci	set_nlink(result, 1);
33062306a36Sopenharmony_ci	result->i_size = strlen(symlink);
33162306a36Sopenharmony_ci	inode_nohighmem(result);
33262306a36Sopenharmony_ci	result->i_op = &page_symlink_inode_operations;
33362306a36Sopenharmony_ci	result->i_data.a_ops = &hpfs_symlink_aops;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	r = hpfs_add_dirent(dir, name, len, &dee);
33662306a36Sopenharmony_ci	if (r == 1)
33762306a36Sopenharmony_ci		goto bail2;
33862306a36Sopenharmony_ci	if (r == -1) {
33962306a36Sopenharmony_ci		err = -EEXIST;
34062306a36Sopenharmony_ci		goto bail2;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci	fnode->len = len;
34362306a36Sopenharmony_ci	memcpy(fnode->name, name, len > 15 ? 15 : len);
34462306a36Sopenharmony_ci	fnode->up = cpu_to_le32(dir->i_ino);
34562306a36Sopenharmony_ci	hpfs_set_ea(result, fnode, "SYMLINK", symlink, strlen(symlink));
34662306a36Sopenharmony_ci	mark_buffer_dirty(bh);
34762306a36Sopenharmony_ci	brelse(bh);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	insert_inode_hash(result);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	hpfs_write_inode_nolock(result);
35262306a36Sopenharmony_ci	hpfs_update_directory_times(dir);
35362306a36Sopenharmony_ci	d_instantiate(dentry, result);
35462306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_cibail2:
35762306a36Sopenharmony_ci	iput(result);
35862306a36Sopenharmony_cibail1:
35962306a36Sopenharmony_ci	brelse(bh);
36062306a36Sopenharmony_ci	hpfs_free_sectors(dir->i_sb, fno, 1);
36162306a36Sopenharmony_cibail:
36262306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
36362306a36Sopenharmony_ci	return err;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic int hpfs_unlink(struct inode *dir, struct dentry *dentry)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	const unsigned char *name = dentry->d_name.name;
36962306a36Sopenharmony_ci	unsigned len = dentry->d_name.len;
37062306a36Sopenharmony_ci	struct quad_buffer_head qbh;
37162306a36Sopenharmony_ci	struct hpfs_dirent *de;
37262306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
37362306a36Sopenharmony_ci	dnode_secno dno;
37462306a36Sopenharmony_ci	int r;
37562306a36Sopenharmony_ci	int err;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	hpfs_lock(dir->i_sb);
37862306a36Sopenharmony_ci	hpfs_adjust_length(name, &len);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	err = -ENOENT;
38162306a36Sopenharmony_ci	de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh);
38262306a36Sopenharmony_ci	if (!de)
38362306a36Sopenharmony_ci		goto out;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	err = -EPERM;
38662306a36Sopenharmony_ci	if (de->first)
38762306a36Sopenharmony_ci		goto out1;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	err = -EISDIR;
39062306a36Sopenharmony_ci	if (de->directory)
39162306a36Sopenharmony_ci		goto out1;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	r = hpfs_remove_dirent(dir, dno, de, &qbh, 1);
39462306a36Sopenharmony_ci	switch (r) {
39562306a36Sopenharmony_ci	case 1:
39662306a36Sopenharmony_ci		hpfs_error(dir->i_sb, "there was error when removing dirent");
39762306a36Sopenharmony_ci		err = -EFSERROR;
39862306a36Sopenharmony_ci		break;
39962306a36Sopenharmony_ci	case 2:		/* no space for deleting */
40062306a36Sopenharmony_ci		err = -ENOSPC;
40162306a36Sopenharmony_ci		break;
40262306a36Sopenharmony_ci	default:
40362306a36Sopenharmony_ci		drop_nlink(inode);
40462306a36Sopenharmony_ci		err = 0;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci	goto out;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ciout1:
40962306a36Sopenharmony_ci	hpfs_brelse4(&qbh);
41062306a36Sopenharmony_ciout:
41162306a36Sopenharmony_ci	if (!err)
41262306a36Sopenharmony_ci		hpfs_update_directory_times(dir);
41362306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
41462306a36Sopenharmony_ci	return err;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	const unsigned char *name = dentry->d_name.name;
42062306a36Sopenharmony_ci	unsigned len = dentry->d_name.len;
42162306a36Sopenharmony_ci	struct quad_buffer_head qbh;
42262306a36Sopenharmony_ci	struct hpfs_dirent *de;
42362306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
42462306a36Sopenharmony_ci	dnode_secno dno;
42562306a36Sopenharmony_ci	int n_items = 0;
42662306a36Sopenharmony_ci	int err;
42762306a36Sopenharmony_ci	int r;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	hpfs_adjust_length(name, &len);
43062306a36Sopenharmony_ci	hpfs_lock(dir->i_sb);
43162306a36Sopenharmony_ci	err = -ENOENT;
43262306a36Sopenharmony_ci	de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh);
43362306a36Sopenharmony_ci	if (!de)
43462306a36Sopenharmony_ci		goto out;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	err = -EPERM;
43762306a36Sopenharmony_ci	if (de->first)
43862306a36Sopenharmony_ci		goto out1;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	err = -ENOTDIR;
44162306a36Sopenharmony_ci	if (!de->directory)
44262306a36Sopenharmony_ci		goto out1;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	hpfs_count_dnodes(dir->i_sb, hpfs_i(inode)->i_dno, NULL, NULL, &n_items);
44562306a36Sopenharmony_ci	err = -ENOTEMPTY;
44662306a36Sopenharmony_ci	if (n_items)
44762306a36Sopenharmony_ci		goto out1;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	r = hpfs_remove_dirent(dir, dno, de, &qbh, 1);
45062306a36Sopenharmony_ci	switch (r) {
45162306a36Sopenharmony_ci	case 1:
45262306a36Sopenharmony_ci		hpfs_error(dir->i_sb, "there was error when removing dirent");
45362306a36Sopenharmony_ci		err = -EFSERROR;
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case 2:
45662306a36Sopenharmony_ci		err = -ENOSPC;
45762306a36Sopenharmony_ci		break;
45862306a36Sopenharmony_ci	default:
45962306a36Sopenharmony_ci		drop_nlink(dir);
46062306a36Sopenharmony_ci		clear_nlink(inode);
46162306a36Sopenharmony_ci		err = 0;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci	goto out;
46462306a36Sopenharmony_ciout1:
46562306a36Sopenharmony_ci	hpfs_brelse4(&qbh);
46662306a36Sopenharmony_ciout:
46762306a36Sopenharmony_ci	if (!err)
46862306a36Sopenharmony_ci		hpfs_update_directory_times(dir);
46962306a36Sopenharmony_ci	hpfs_unlock(dir->i_sb);
47062306a36Sopenharmony_ci	return err;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic int hpfs_symlink_read_folio(struct file *file, struct folio *folio)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct page *page = &folio->page;
47662306a36Sopenharmony_ci	char *link = page_address(page);
47762306a36Sopenharmony_ci	struct inode *i = page->mapping->host;
47862306a36Sopenharmony_ci	struct fnode *fnode;
47962306a36Sopenharmony_ci	struct buffer_head *bh;
48062306a36Sopenharmony_ci	int err;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	err = -EIO;
48362306a36Sopenharmony_ci	hpfs_lock(i->i_sb);
48462306a36Sopenharmony_ci	if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh)))
48562306a36Sopenharmony_ci		goto fail;
48662306a36Sopenharmony_ci	err = hpfs_read_ea(i->i_sb, fnode, "SYMLINK", link, PAGE_SIZE);
48762306a36Sopenharmony_ci	brelse(bh);
48862306a36Sopenharmony_ci	if (err)
48962306a36Sopenharmony_ci		goto fail;
49062306a36Sopenharmony_ci	hpfs_unlock(i->i_sb);
49162306a36Sopenharmony_ci	SetPageUptodate(page);
49262306a36Sopenharmony_ci	unlock_page(page);
49362306a36Sopenharmony_ci	return 0;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cifail:
49662306a36Sopenharmony_ci	hpfs_unlock(i->i_sb);
49762306a36Sopenharmony_ci	SetPageError(page);
49862306a36Sopenharmony_ci	unlock_page(page);
49962306a36Sopenharmony_ci	return err;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ciconst struct address_space_operations hpfs_symlink_aops = {
50362306a36Sopenharmony_ci	.read_folio	= hpfs_symlink_read_folio
50462306a36Sopenharmony_ci};
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic int hpfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
50762306a36Sopenharmony_ci		       struct dentry *old_dentry, struct inode *new_dir,
50862306a36Sopenharmony_ci		       struct dentry *new_dentry, unsigned int flags)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	const unsigned char *old_name = old_dentry->d_name.name;
51162306a36Sopenharmony_ci	unsigned old_len = old_dentry->d_name.len;
51262306a36Sopenharmony_ci	const unsigned char *new_name = new_dentry->d_name.name;
51362306a36Sopenharmony_ci	unsigned new_len = new_dentry->d_name.len;
51462306a36Sopenharmony_ci	struct inode *i = d_inode(old_dentry);
51562306a36Sopenharmony_ci	struct inode *new_inode = d_inode(new_dentry);
51662306a36Sopenharmony_ci	struct quad_buffer_head qbh, qbh1;
51762306a36Sopenharmony_ci	struct hpfs_dirent *dep, *nde;
51862306a36Sopenharmony_ci	struct hpfs_dirent de;
51962306a36Sopenharmony_ci	dnode_secno dno;
52062306a36Sopenharmony_ci	int r;
52162306a36Sopenharmony_ci	struct buffer_head *bh;
52262306a36Sopenharmony_ci	struct fnode *fnode;
52362306a36Sopenharmony_ci	int err;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (flags & ~RENAME_NOREPLACE)
52662306a36Sopenharmony_ci		return -EINVAL;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if ((err = hpfs_chk_name(new_name, &new_len))) return err;
52962306a36Sopenharmony_ci	err = 0;
53062306a36Sopenharmony_ci	hpfs_adjust_length(old_name, &old_len);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	hpfs_lock(i->i_sb);
53362306a36Sopenharmony_ci	/* order doesn't matter, due to VFS exclusion */
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Erm? Moving over the empty non-busy directory is perfectly legal */
53662306a36Sopenharmony_ci	if (new_inode && S_ISDIR(new_inode->i_mode)) {
53762306a36Sopenharmony_ci		err = -EINVAL;
53862306a36Sopenharmony_ci		goto end1;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, old_name, old_len, &dno, &qbh))) {
54262306a36Sopenharmony_ci		hpfs_error(i->i_sb, "lookup succeeded but map dirent failed");
54362306a36Sopenharmony_ci		err = -ENOENT;
54462306a36Sopenharmony_ci		goto end1;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci	copy_de(&de, dep);
54762306a36Sopenharmony_ci	de.hidden = new_name[0] == '.';
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (new_inode) {
55062306a36Sopenharmony_ci		int r;
55162306a36Sopenharmony_ci		if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
55262306a36Sopenharmony_ci			if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, new_name, new_len, NULL, &qbh1))) {
55362306a36Sopenharmony_ci				clear_nlink(new_inode);
55462306a36Sopenharmony_ci				copy_de(nde, &de);
55562306a36Sopenharmony_ci				memcpy(nde->name, new_name, new_len);
55662306a36Sopenharmony_ci				hpfs_mark_4buffers_dirty(&qbh1);
55762306a36Sopenharmony_ci				hpfs_brelse4(&qbh1);
55862306a36Sopenharmony_ci				goto end;
55962306a36Sopenharmony_ci			}
56062306a36Sopenharmony_ci			hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent");
56162306a36Sopenharmony_ci			err = -EFSERROR;
56262306a36Sopenharmony_ci			goto end1;
56362306a36Sopenharmony_ci		}
56462306a36Sopenharmony_ci		err = -ENOSPC;
56562306a36Sopenharmony_ci		goto end1;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (new_dir == old_dir) hpfs_brelse4(&qbh);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de))) {
57162306a36Sopenharmony_ci		if (r == -1) hpfs_error(new_dir->i_sb, "hpfs_rename: dirent already exists!");
57262306a36Sopenharmony_ci		err = r == 1 ? -ENOSPC : -EFSERROR;
57362306a36Sopenharmony_ci		if (new_dir != old_dir) hpfs_brelse4(&qbh);
57462306a36Sopenharmony_ci		goto end1;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (new_dir == old_dir)
57862306a36Sopenharmony_ci		if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, old_name, old_len, &dno, &qbh))) {
57962306a36Sopenharmony_ci			hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
58062306a36Sopenharmony_ci			err = -ENOENT;
58162306a36Sopenharmony_ci			goto end1;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 0))) {
58562306a36Sopenharmony_ci		hpfs_error(i->i_sb, "hpfs_rename: could not remove dirent");
58662306a36Sopenharmony_ci		err = r == 2 ? -ENOSPC : -EFSERROR;
58762306a36Sopenharmony_ci		goto end1;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ciend:
59162306a36Sopenharmony_ci	hpfs_i(i)->i_parent_dir = new_dir->i_ino;
59262306a36Sopenharmony_ci	if (S_ISDIR(i->i_mode)) {
59362306a36Sopenharmony_ci		inc_nlink(new_dir);
59462306a36Sopenharmony_ci		drop_nlink(old_dir);
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci	if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
59762306a36Sopenharmony_ci		fnode->up = cpu_to_le32(new_dir->i_ino);
59862306a36Sopenharmony_ci		fnode->len = new_len;
59962306a36Sopenharmony_ci		memcpy(fnode->name, new_name, new_len>15?15:new_len);
60062306a36Sopenharmony_ci		if (new_len < 15) memset(&fnode->name[new_len], 0, 15 - new_len);
60162306a36Sopenharmony_ci		mark_buffer_dirty(bh);
60262306a36Sopenharmony_ci		brelse(bh);
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ciend1:
60562306a36Sopenharmony_ci	if (!err) {
60662306a36Sopenharmony_ci		hpfs_update_directory_times(old_dir);
60762306a36Sopenharmony_ci		hpfs_update_directory_times(new_dir);
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci	hpfs_unlock(i->i_sb);
61062306a36Sopenharmony_ci	return err;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ciconst struct inode_operations hpfs_dir_iops =
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	.create		= hpfs_create,
61662306a36Sopenharmony_ci	.lookup		= hpfs_lookup,
61762306a36Sopenharmony_ci	.unlink		= hpfs_unlink,
61862306a36Sopenharmony_ci	.symlink	= hpfs_symlink,
61962306a36Sopenharmony_ci	.mkdir		= hpfs_mkdir,
62062306a36Sopenharmony_ci	.rmdir		= hpfs_rmdir,
62162306a36Sopenharmony_ci	.mknod		= hpfs_mknod,
62262306a36Sopenharmony_ci	.rename		= hpfs_rename,
62362306a36Sopenharmony_ci	.setattr	= hpfs_setattr,
62462306a36Sopenharmony_ci};
625