xref: /linux/fs/adfs/dir.c (revision 1dd9f5babfd95fea5a77b27bab48c04c29db1f5f)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/adfs/dir.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1999-2000 Russell King
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Common directory handling for ADFS
81da177e4SLinus Torvalds  */
9*1dd9f5baSRussell King #include <linux/slab.h>
101da177e4SLinus Torvalds #include "adfs.h"
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds /*
131da177e4SLinus Torvalds  * For future.  This should probably be per-directory.
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds static DEFINE_RWLOCK(adfs_dir_lock);
161da177e4SLinus Torvalds 
17*1dd9f5baSRussell King void adfs_dir_relse(struct adfs_dir *dir)
18*1dd9f5baSRussell King {
19*1dd9f5baSRussell King 	unsigned int i;
20*1dd9f5baSRussell King 
21*1dd9f5baSRussell King 	for (i = 0; i < dir->nr_buffers; i++)
22*1dd9f5baSRussell King 		brelse(dir->bhs[i]);
23*1dd9f5baSRussell King 	dir->nr_buffers = 0;
24*1dd9f5baSRussell King 
25*1dd9f5baSRussell King 	if (dir->bhs != dir->bh)
26*1dd9f5baSRussell King 		kfree(dir->bhs);
27*1dd9f5baSRussell King 	dir->bhs = NULL;
28*1dd9f5baSRussell King 	dir->sb = NULL;
29*1dd9f5baSRussell King }
30*1dd9f5baSRussell King 
3195fbadbbSRussell King static int adfs_dir_read(struct super_block *sb, u32 indaddr,
3295fbadbbSRussell King 			 unsigned int size, struct adfs_dir *dir)
3395fbadbbSRussell King {
3495fbadbbSRussell King 	dir->sb = sb;
3595fbadbbSRussell King 	dir->bhs = dir->bh;
3695fbadbbSRussell King 	dir->nr_buffers = 0;
3795fbadbbSRussell King 
3895fbadbbSRussell King 	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
3995fbadbbSRussell King }
4095fbadbbSRussell King 
41411c49bcSRussell King void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
42411c49bcSRussell King {
43fc722a04SRussell King 	unsigned int dots, i;
44adb514a4SRussell King 
45adb514a4SRussell King 	/*
46adb514a4SRussell King 	 * RISC OS allows the use of '/' in directory entry names, so we need
47adb514a4SRussell King 	 * to fix these up.  '/' is typically used for FAT compatibility to
48adb514a4SRussell King 	 * represent '.', so do the same conversion here.  In any case, '.'
49adb514a4SRussell King 	 * will never be in a RISC OS name since it is used as the pathname
50fc722a04SRussell King 	 * separator.  Handle the case where we may generate a '.' or '..'
51fc722a04SRussell King 	 * name, replacing the first character with '^' (the RISC OS "parent
52fc722a04SRussell King 	 * directory" character.)
53adb514a4SRussell King 	 */
54fc722a04SRussell King 	for (i = dots = 0; i < obj->name_len; i++)
55fc722a04SRussell King 		if (obj->name[i] == '/') {
56adb514a4SRussell King 			obj->name[i] = '.';
57fc722a04SRussell King 			dots++;
58fc722a04SRussell King 		}
59fc722a04SRussell King 
60fc722a04SRussell King 	if (obj->name_len <= 2 && dots == obj->name_len)
61fc722a04SRussell King 		obj->name[0] = '^';
62adb514a4SRussell King 
63411c49bcSRussell King 	/*
64b4ed8f75SRussell King 	 * If the object is a file, and the user requested the ,xyz hex
65b4ed8f75SRussell King 	 * filetype suffix to the name, check the filetype and append.
66411c49bcSRussell King 	 */
67b4ed8f75SRussell King 	if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
68b4ed8f75SRussell King 		u16 filetype = adfs_filetype(obj->loadaddr);
69411c49bcSRussell King 
70b4ed8f75SRussell King 		if (filetype != ADFS_FILETYPE_NONE) {
715f8de487SRussell King 			obj->name[obj->name_len++] = ',';
725f8de487SRussell King 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
735f8de487SRussell King 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
745f8de487SRussell King 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
755f8de487SRussell King 		}
76411c49bcSRussell King 	}
77411c49bcSRussell King }
78411c49bcSRussell King 
791da177e4SLinus Torvalds static int
802638ffbaSAl Viro adfs_readdir(struct file *file, struct dir_context *ctx)
811da177e4SLinus Torvalds {
822638ffbaSAl Viro 	struct inode *inode = file_inode(file);
831da177e4SLinus Torvalds 	struct super_block *sb = inode->i_sb;
840125f504SJulia Lawall 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
851da177e4SLinus Torvalds 	struct object_info obj;
861da177e4SLinus Torvalds 	struct adfs_dir dir;
871da177e4SLinus Torvalds 	int ret = 0;
881da177e4SLinus Torvalds 
892638ffbaSAl Viro 	if (ctx->pos >> 32)
902638ffbaSAl Viro 		return 0;
911da177e4SLinus Torvalds 
9295fbadbbSRussell King 	ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, &dir);
931da177e4SLinus Torvalds 	if (ret)
942638ffbaSAl Viro 		return ret;
951da177e4SLinus Torvalds 
962638ffbaSAl Viro 	if (ctx->pos == 0) {
972638ffbaSAl Viro 		if (!dir_emit_dot(file, ctx))
981da177e4SLinus Torvalds 			goto free_out;
992638ffbaSAl Viro 		ctx->pos = 1;
1002638ffbaSAl Viro 	}
1012638ffbaSAl Viro 	if (ctx->pos == 1) {
1022638ffbaSAl Viro 		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
1031da177e4SLinus Torvalds 			goto free_out;
1042638ffbaSAl Viro 		ctx->pos = 2;
1051da177e4SLinus Torvalds 	}
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	read_lock(&adfs_dir_lock);
1081da177e4SLinus Torvalds 
1092638ffbaSAl Viro 	ret = ops->setpos(&dir, ctx->pos - 2);
1101da177e4SLinus Torvalds 	if (ret)
1111da177e4SLinus Torvalds 		goto unlock_out;
1121da177e4SLinus Torvalds 	while (ops->getnext(&dir, &obj) == 0) {
1132638ffbaSAl Viro 		if (!dir_emit(ctx, obj.name, obj.name_len,
1145ed70bb4SRussell King 			      obj.indaddr, DT_UNKNOWN))
1152638ffbaSAl Viro 			break;
1162638ffbaSAl Viro 		ctx->pos++;
1171da177e4SLinus Torvalds 	}
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds unlock_out:
1201da177e4SLinus Torvalds 	read_unlock(&adfs_dir_lock);
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds free_out:
123*1dd9f5baSRussell King 	adfs_dir_relse(&dir);
1241da177e4SLinus Torvalds 	return ret;
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds int
128ffdc9064SAl Viro adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
1291da177e4SLinus Torvalds {
1301da177e4SLinus Torvalds 	int ret = -EINVAL;
1311da177e4SLinus Torvalds #ifdef CONFIG_ADFS_FS_RW
1320125f504SJulia Lawall 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
1331da177e4SLinus Torvalds 	struct adfs_dir dir;
1341da177e4SLinus Torvalds 
1355ed70bb4SRussell King 	printk(KERN_INFO "adfs_dir_update: object %06x in dir %06x\n",
1365ed70bb4SRussell King 		 obj->indaddr, obj->parent_id);
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	if (!ops->update) {
1391da177e4SLinus Torvalds 		ret = -EINVAL;
1401da177e4SLinus Torvalds 		goto out;
1411da177e4SLinus Torvalds 	}
1421da177e4SLinus Torvalds 
14395fbadbbSRussell King 	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
1441da177e4SLinus Torvalds 	if (ret)
1451da177e4SLinus Torvalds 		goto out;
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds 	write_lock(&adfs_dir_lock);
1481da177e4SLinus Torvalds 	ret = ops->update(&dir, obj);
1491da177e4SLinus Torvalds 	write_unlock(&adfs_dir_lock);
1501da177e4SLinus Torvalds 
151ffdc9064SAl Viro 	if (wait) {
152ffdc9064SAl Viro 		int err = ops->sync(&dir);
153ffdc9064SAl Viro 		if (!ret)
154ffdc9064SAl Viro 			ret = err;
155ffdc9064SAl Viro 	}
156ffdc9064SAl Viro 
157*1dd9f5baSRussell King 	adfs_dir_relse(&dir);
1581da177e4SLinus Torvalds out:
1591da177e4SLinus Torvalds #endif
1601da177e4SLinus Torvalds 	return ret;
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
163525715d0SRussell King static unsigned char adfs_tolower(unsigned char c)
164525715d0SRussell King {
165525715d0SRussell King 	if (c >= 'A' && c <= 'Z')
166525715d0SRussell King 		c += 'a' - 'A';
167525715d0SRussell King 	return c;
168525715d0SRussell King }
169525715d0SRussell King 
1701e504cf8SRussell King static int __adfs_compare(const unsigned char *qstr, u32 qlen,
1711e504cf8SRussell King 			  const char *str, u32 len)
1721da177e4SLinus Torvalds {
1731e504cf8SRussell King 	u32 i;
1741da177e4SLinus Torvalds 
1751e504cf8SRussell King 	if (qlen != len)
1761e504cf8SRussell King 		return 1;
1771da177e4SLinus Torvalds 
178525715d0SRussell King 	for (i = 0; i < qlen; i++)
179525715d0SRussell King 		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
1801da177e4SLinus Torvalds 			return 1;
181525715d0SRussell King 
1821e504cf8SRussell King 	return 0;
1831e504cf8SRussell King }
1841da177e4SLinus Torvalds 
1851e504cf8SRussell King static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
1861e504cf8SRussell King 				  struct object_info *obj)
1871da177e4SLinus Torvalds {
1881da177e4SLinus Torvalds 	struct super_block *sb = inode->i_sb;
1890125f504SJulia Lawall 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
1901e504cf8SRussell King 	const unsigned char *name;
1911da177e4SLinus Torvalds 	struct adfs_dir dir;
1921e504cf8SRussell King 	u32 name_len;
1931da177e4SLinus Torvalds 	int ret;
1941da177e4SLinus Torvalds 
19595fbadbbSRussell King 	ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, &dir);
1961da177e4SLinus Torvalds 	if (ret)
1971da177e4SLinus Torvalds 		goto out;
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	if (ADFS_I(inode)->parent_id != dir.parent_id) {
2005ed70bb4SRussell King 		adfs_error(sb,
2015ed70bb4SRussell King 			   "parent directory changed under me! (%06x but got %06x)\n",
2021da177e4SLinus Torvalds 			   ADFS_I(inode)->parent_id, dir.parent_id);
2031da177e4SLinus Torvalds 		ret = -EIO;
2041da177e4SLinus Torvalds 		goto free_out;
2051da177e4SLinus Torvalds 	}
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	obj->parent_id = inode->i_ino;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	read_lock(&adfs_dir_lock);
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	ret = ops->setpos(&dir, 0);
2121da177e4SLinus Torvalds 	if (ret)
2131da177e4SLinus Torvalds 		goto unlock_out;
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 	ret = -ENOENT;
2161e504cf8SRussell King 	name = qstr->name;
2171e504cf8SRussell King 	name_len = qstr->len;
2181da177e4SLinus Torvalds 	while (ops->getnext(&dir, obj) == 0) {
2191e504cf8SRussell King 		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
2201da177e4SLinus Torvalds 			ret = 0;
2211da177e4SLinus Torvalds 			break;
2221da177e4SLinus Torvalds 		}
2231da177e4SLinus Torvalds 	}
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds unlock_out:
2261da177e4SLinus Torvalds 	read_unlock(&adfs_dir_lock);
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds free_out:
229*1dd9f5baSRussell King 	adfs_dir_relse(&dir);
2301da177e4SLinus Torvalds out:
2311da177e4SLinus Torvalds 	return ret;
2321da177e4SLinus Torvalds }
2331da177e4SLinus Torvalds 
2344b6f5d20SArjan van de Ven const struct file_operations adfs_dir_operations = {
2351da177e4SLinus Torvalds 	.read		= generic_read_dir,
23659af1584SAl Viro 	.llseek		= generic_file_llseek,
2372638ffbaSAl Viro 	.iterate	= adfs_readdir,
2381b061d92SChristoph Hellwig 	.fsync		= generic_file_fsync,
2391da177e4SLinus Torvalds };
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds static int
242da53be12SLinus Torvalds adfs_hash(const struct dentry *parent, struct qstr *qstr)
2431da177e4SLinus Torvalds {
2441da177e4SLinus Torvalds 	const unsigned char *name;
2451da177e4SLinus Torvalds 	unsigned long hash;
2462eb0684fSRussell King 	u32 len;
2471da177e4SLinus Torvalds 
2482eb0684fSRussell King 	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
2492eb0684fSRussell King 		return -ENAMETOOLONG;
2501da177e4SLinus Torvalds 
2512eb0684fSRussell King 	len = qstr->len;
2521da177e4SLinus Torvalds 	name = qstr->name;
2538387ff25SLinus Torvalds 	hash = init_name_hash(parent);
2542eb0684fSRussell King 	while (len--)
255525715d0SRussell King 		hash = partial_name_hash(adfs_tolower(*name++), hash);
2561da177e4SLinus Torvalds 	qstr->hash = end_name_hash(hash);
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	return 0;
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds /*
2621da177e4SLinus Torvalds  * Compare two names, taking note of the name length
2631da177e4SLinus Torvalds  * requirements of the underlying filesystem.
2641da177e4SLinus Torvalds  */
2651e504cf8SRussell King static int adfs_compare(const struct dentry *dentry, unsigned int len,
2661e504cf8SRussell King 			const char *str, const struct qstr *qstr)
2671da177e4SLinus Torvalds {
2681e504cf8SRussell King 	return __adfs_compare(qstr->name, qstr->len, str, len);
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
271e16404edSAl Viro const struct dentry_operations adfs_dentry_operations = {
2721da177e4SLinus Torvalds 	.d_hash		= adfs_hash,
2731da177e4SLinus Torvalds 	.d_compare	= adfs_compare,
2741da177e4SLinus Torvalds };
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds static struct dentry *
27700cd8dd3SAl Viro adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
2781da177e4SLinus Torvalds {
2791da177e4SLinus Torvalds 	struct inode *inode = NULL;
2801da177e4SLinus Torvalds 	struct object_info obj;
2811da177e4SLinus Torvalds 	int error;
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
2841da177e4SLinus Torvalds 	if (error == 0) {
2851da177e4SLinus Torvalds 		/*
2861da177e4SLinus Torvalds 		 * This only returns NULL if get_empty_inode
2871da177e4SLinus Torvalds 		 * fails.
2881da177e4SLinus Torvalds 		 */
2891da177e4SLinus Torvalds 		inode = adfs_iget(dir->i_sb, &obj);
2909a7dddcaSAl Viro 		if (!inode)
2919a7dddcaSAl Viro 			inode = ERR_PTR(-EACCES);
2929a7dddcaSAl Viro 	} else if (error != -ENOENT) {
2939a7dddcaSAl Viro 		inode = ERR_PTR(error);
2941da177e4SLinus Torvalds 	}
2959a7dddcaSAl Viro 	return d_splice_alias(inode, dentry);
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds /*
2991da177e4SLinus Torvalds  * directories can handle most operations...
3001da177e4SLinus Torvalds  */
301754661f1SArjan van de Ven const struct inode_operations adfs_dir_inode_operations = {
3021da177e4SLinus Torvalds 	.lookup		= adfs_lookup,
3031da177e4SLinus Torvalds 	.setattr	= adfs_notify_change,
3041da177e4SLinus Torvalds };
305