xref: /openbmc/linux/fs/affs/namei.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/affs/namei.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  (c) 1996  Hans-Joachim Widmaier - Rewritten
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  (C) 1991  Linus Torvalds - minix filesystem
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include "affs.h"
13ed4433d7SFabian Frederick #include <linux/exportfs.h>
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds typedef int (*toupper_t)(int);
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /* Simple toupper() for DOS\1 */
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds static int
affs_toupper(int ch)201da177e4SLinus Torvalds affs_toupper(int ch)
211da177e4SLinus Torvalds {
221da177e4SLinus Torvalds 	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
231da177e4SLinus Torvalds }
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds /* International toupper() for DOS\3 ("international") */
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds static int
affs_intl_toupper(int ch)281da177e4SLinus Torvalds affs_intl_toupper(int ch)
291da177e4SLinus Torvalds {
301da177e4SLinus Torvalds 	return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
311da177e4SLinus Torvalds 		&& ch <= 0xFE && ch != 0xF7) ?
321da177e4SLinus Torvalds 		ch - ('a' - 'A') : ch;
331da177e4SLinus Torvalds }
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds static inline toupper_t
affs_get_toupper(struct super_block * sb)361da177e4SLinus Torvalds affs_get_toupper(struct super_block *sb)
371da177e4SLinus Torvalds {
3879bda4d5SFabian Frederick 	return affs_test_opt(AFFS_SB(sb)->s_flags, SF_INTL) ?
39a0016ff2SFabian Frederick 	       affs_intl_toupper : affs_toupper;
401da177e4SLinus Torvalds }
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds /*
431da177e4SLinus Torvalds  * Note: the dentry argument is the parent dentry.
441da177e4SLinus Torvalds  */
451da177e4SLinus Torvalds static inline int
__affs_hash_dentry(const struct dentry * dentry,struct qstr * qstr,toupper_t fn,bool notruncate)46*4d4f1468SAndy Shevchenko __affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr, toupper_t fn, bool notruncate)
471da177e4SLinus Torvalds {
481da177e4SLinus Torvalds 	const u8 *name = qstr->name;
491da177e4SLinus Torvalds 	unsigned long hash;
50eeb36f8eSFabian Frederick 	int retval;
51eeb36f8eSFabian Frederick 	u32 len;
521da177e4SLinus Torvalds 
53eeb36f8eSFabian Frederick 	retval = affs_check_name(qstr->name, qstr->len, notruncate);
54eeb36f8eSFabian Frederick 	if (retval)
55eeb36f8eSFabian Frederick 		return retval;
561da177e4SLinus Torvalds 
578387ff25SLinus Torvalds 	hash = init_name_hash(dentry);
58f157853eSFabian Frederick 	len = min(qstr->len, AFFSNAMEMAX);
59eeb36f8eSFabian Frederick 	for (; len > 0; name++, len--)
60*4d4f1468SAndy Shevchenko 		hash = partial_name_hash(fn(*name), hash);
611da177e4SLinus Torvalds 	qstr->hash = end_name_hash(hash);
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 	return 0;
641da177e4SLinus Torvalds }
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds static int
affs_hash_dentry(const struct dentry * dentry,struct qstr * qstr)67da53be12SLinus Torvalds affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
681da177e4SLinus Torvalds {
698387ff25SLinus Torvalds 	return __affs_hash_dentry(dentry, qstr, affs_toupper,
708ca57722SFabian Frederick 				  affs_nofilenametruncate(dentry));
718ca57722SFabian Frederick 
721da177e4SLinus Torvalds }
738ca57722SFabian Frederick 
741da177e4SLinus Torvalds static int
affs_intl_hash_dentry(const struct dentry * dentry,struct qstr * qstr)75da53be12SLinus Torvalds affs_intl_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
761da177e4SLinus Torvalds {
778387ff25SLinus Torvalds 	return __affs_hash_dentry(dentry, qstr, affs_intl_toupper,
788ca57722SFabian Frederick 				  affs_nofilenametruncate(dentry));
798ca57722SFabian Frederick 
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds 
__affs_compare_dentry(unsigned int len,const char * str,const struct qstr * name,toupper_t fn,bool notruncate)82621e155aSNick Piggin static inline int __affs_compare_dentry(unsigned int len,
83*4d4f1468SAndy Shevchenko 		const char *str, const struct qstr *name, toupper_t fn,
848ca57722SFabian Frederick 		bool notruncate)
851da177e4SLinus Torvalds {
86621e155aSNick Piggin 	const u8 *aname = str;
87621e155aSNick Piggin 	const u8 *bname = name->name;
881da177e4SLinus Torvalds 
89621e155aSNick Piggin 	/*
90621e155aSNick Piggin 	 * 'str' is the name of an already existing dentry, so the name
91621e155aSNick Piggin 	 * must be valid. 'name' must be validated first.
921da177e4SLinus Torvalds 	 */
931da177e4SLinus Torvalds 
948ca57722SFabian Frederick 	if (affs_check_name(name->name, name->len, notruncate))
951da177e4SLinus Torvalds 		return 1;
961da177e4SLinus Torvalds 
97621e155aSNick Piggin 	/*
98621e155aSNick Piggin 	 * If the names are longer than the allowed 30 chars,
991da177e4SLinus Torvalds 	 * the excess is ignored, so their length may differ.
1001da177e4SLinus Torvalds 	 */
101f157853eSFabian Frederick 	if (len >= AFFSNAMEMAX) {
102f157853eSFabian Frederick 		if (name->len < AFFSNAMEMAX)
1031da177e4SLinus Torvalds 			return 1;
104f157853eSFabian Frederick 		len = AFFSNAMEMAX;
105621e155aSNick Piggin 	} else if (len != name->len)
1061da177e4SLinus Torvalds 		return 1;
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds 	for (; len > 0; len--)
109*4d4f1468SAndy Shevchenko 		if (fn(*aname++) != fn(*bname++))
1101da177e4SLinus Torvalds 			return 1;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 	return 0;
1131da177e4SLinus Torvalds }
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds static int
affs_compare_dentry(const struct dentry * dentry,unsigned int len,const char * str,const struct qstr * name)1166fa67e70SAl Viro affs_compare_dentry(const struct dentry *dentry,
117621e155aSNick Piggin 		unsigned int len, const char *str, const struct qstr *name)
1181da177e4SLinus Torvalds {
1198ca57722SFabian Frederick 
1208ca57722SFabian Frederick 	return __affs_compare_dentry(len, str, name, affs_toupper,
121e0b3f595SAl Viro 				     affs_nofilenametruncate(dentry));
1221da177e4SLinus Torvalds }
1238ca57722SFabian Frederick 
1241da177e4SLinus Torvalds static int
affs_intl_compare_dentry(const struct dentry * dentry,unsigned int len,const char * str,const struct qstr * name)1256fa67e70SAl Viro affs_intl_compare_dentry(const struct dentry *dentry,
126621e155aSNick Piggin 		unsigned int len, const char *str, const struct qstr *name)
1271da177e4SLinus Torvalds {
1288ca57722SFabian Frederick 	return __affs_compare_dentry(len, str, name, affs_intl_toupper,
129e0b3f595SAl Viro 				     affs_nofilenametruncate(dentry));
1308ca57722SFabian Frederick 
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds /*
1341da177e4SLinus Torvalds  * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
1351da177e4SLinus Torvalds  */
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds static inline int
affs_match(struct dentry * dentry,const u8 * name2,toupper_t fn)138*4d4f1468SAndy Shevchenko affs_match(struct dentry *dentry, const u8 *name2, toupper_t fn)
1391da177e4SLinus Torvalds {
1401da177e4SLinus Torvalds 	const u8 *name = dentry->d_name.name;
1411da177e4SLinus Torvalds 	int len = dentry->d_name.len;
1421da177e4SLinus Torvalds 
143f157853eSFabian Frederick 	if (len >= AFFSNAMEMAX) {
144f157853eSFabian Frederick 		if (*name2 < AFFSNAMEMAX)
1451da177e4SLinus Torvalds 			return 0;
146f157853eSFabian Frederick 		len = AFFSNAMEMAX;
1471da177e4SLinus Torvalds 	} else if (len != *name2)
1481da177e4SLinus Torvalds 		return 0;
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	for (name2++; len > 0; len--)
151*4d4f1468SAndy Shevchenko 		if (fn(*name++) != fn(*name2++))
1521da177e4SLinus Torvalds 			return 0;
1531da177e4SLinus Torvalds 	return 1;
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds int
affs_hash_name(struct super_block * sb,const u8 * name,unsigned int len)1571da177e4SLinus Torvalds affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
1581da177e4SLinus Torvalds {
159*4d4f1468SAndy Shevchenko 	toupper_t fn = affs_get_toupper(sb);
160eeb36f8eSFabian Frederick 	u32 hash;
1611da177e4SLinus Torvalds 
162f157853eSFabian Frederick 	hash = len = min(len, AFFSNAMEMAX);
1631da177e4SLinus Torvalds 	for (; len > 0; len--)
164*4d4f1468SAndy Shevchenko 		hash = (hash * 13 + fn(*name++)) & 0x7ff;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	return hash % AFFS_SB(sb)->s_hashsize;
1671da177e4SLinus Torvalds }
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds static struct buffer_head *
affs_find_entry(struct inode * dir,struct dentry * dentry)1701da177e4SLinus Torvalds affs_find_entry(struct inode *dir, struct dentry *dentry)
1711da177e4SLinus Torvalds {
1721da177e4SLinus Torvalds 	struct super_block *sb = dir->i_sb;
1731da177e4SLinus Torvalds 	struct buffer_head *bh;
174*4d4f1468SAndy Shevchenko 	toupper_t fn = affs_get_toupper(sb);
1751da177e4SLinus Torvalds 	u32 key;
1761da177e4SLinus Torvalds 
177a455589fSAl Viro 	pr_debug("%s(\"%pd\")\n", __func__, dentry);
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	bh = affs_bread(sb, dir->i_ino);
1801da177e4SLinus Torvalds 	if (!bh)
1811da177e4SLinus Torvalds 		return ERR_PTR(-EIO);
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 	key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	for (;;) {
1861da177e4SLinus Torvalds 		affs_brelse(bh);
1871da177e4SLinus Torvalds 		if (key == 0)
1881da177e4SLinus Torvalds 			return NULL;
1891da177e4SLinus Torvalds 		bh = affs_bread(sb, key);
1901da177e4SLinus Torvalds 		if (!bh)
1911da177e4SLinus Torvalds 			return ERR_PTR(-EIO);
192*4d4f1468SAndy Shevchenko 		if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, fn))
1931da177e4SLinus Torvalds 			return bh;
1941da177e4SLinus Torvalds 		key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
1951da177e4SLinus Torvalds 	}
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds struct dentry *
affs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)19900cd8dd3SAl Viro affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
2001da177e4SLinus Torvalds {
2011da177e4SLinus Torvalds 	struct super_block *sb = dir->i_sb;
2021da177e4SLinus Torvalds 	struct buffer_head *bh;
2031da177e4SLinus Torvalds 	struct inode *inode = NULL;
20487fbd639SAl Viro 	struct dentry *res;
2051da177e4SLinus Torvalds 
206a455589fSAl Viro 	pr_debug("%s(\"%pd\")\n", __func__, dentry);
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 	affs_lock_dir(dir);
2091da177e4SLinus Torvalds 	bh = affs_find_entry(dir, dentry);
21030da870cSAl Viro 	if (IS_ERR(bh)) {
2111da177e4SLinus Torvalds 		affs_unlock_dir(dir);
212e231c2eeSDavid Howells 		return ERR_CAST(bh);
21330da870cSAl Viro 	}
2141da177e4SLinus Torvalds 	if (bh) {
2151da177e4SLinus Torvalds 		u32 ino = bh->b_blocknr;
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 		/* store the real header ino in d_fsdata for faster lookups */
2181da177e4SLinus Torvalds 		dentry->d_fsdata = (void *)(long)ino;
2191da177e4SLinus Torvalds 		switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
2201da177e4SLinus Torvalds 		//link to dirs disabled
2211da177e4SLinus Torvalds 		//case ST_LINKDIR:
2221da177e4SLinus Torvalds 		case ST_LINKFILE:
2231da177e4SLinus Torvalds 			ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
2241da177e4SLinus Torvalds 		}
2251da177e4SLinus Torvalds 		affs_brelse(bh);
226210f8559SDavid Howells 		inode = affs_iget(sb, ino);
2271da177e4SLinus Torvalds 	}
22887fbd639SAl Viro 	res = d_splice_alias(inode, dentry);
22987fbd639SAl Viro 	if (!IS_ERR_OR_NULL(res))
23087fbd639SAl Viro 		res->d_fsdata = dentry->d_fsdata;
23130da870cSAl Viro 	affs_unlock_dir(dir);
23287fbd639SAl Viro 	return res;
2331da177e4SLinus Torvalds }
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds int
affs_unlink(struct inode * dir,struct dentry * dentry)2361da177e4SLinus Torvalds affs_unlink(struct inode *dir, struct dentry *dentry)
2371da177e4SLinus Torvalds {
23808fe100dSGeert Uytterhoeven 	pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino,
2392b0143b5SDavid Howells 		 d_inode(dentry)->i_ino, dentry);
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	return affs_remove_header(dentry);
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds int
affs_create(struct mnt_idmap * idmap,struct inode * dir,struct dentry * dentry,umode_t mode,bool excl)2456c960e68SChristian Brauner affs_create(struct mnt_idmap *idmap, struct inode *dir,
246549c7297SChristian Brauner 	    struct dentry *dentry, umode_t mode, bool excl)
2471da177e4SLinus Torvalds {
2481da177e4SLinus Torvalds 	struct super_block *sb = dir->i_sb;
2491da177e4SLinus Torvalds 	struct inode	*inode;
2501da177e4SLinus Torvalds 	int		 error;
2511da177e4SLinus Torvalds 
252a455589fSAl Viro 	pr_debug("%s(%lu,\"%pd\",0%ho)\n",
253a455589fSAl Viro 		 __func__, dir->i_ino, dentry, mode);
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 	inode = affs_new_inode(dir);
2561da177e4SLinus Torvalds 	if (!inode)
2571da177e4SLinus Torvalds 		return -ENOSPC;
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	inode->i_mode = mode;
260c1618208SFabian Frederick 	affs_mode_to_prot(inode);
2611da177e4SLinus Torvalds 	mark_inode_dirty(inode);
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	inode->i_op = &affs_file_inode_operations;
2641da177e4SLinus Torvalds 	inode->i_fop = &affs_file_operations;
26579bda4d5SFabian Frederick 	inode->i_mapping->a_ops = affs_test_opt(AFFS_SB(sb)->s_flags, SF_OFS) ?
266a0016ff2SFabian Frederick 				  &affs_aops_ofs : &affs_aops;
2671da177e4SLinus Torvalds 	error = affs_add_entry(dir, inode, dentry, ST_FILE);
2681da177e4SLinus Torvalds 	if (error) {
2696d6b77f1SMiklos Szeredi 		clear_nlink(inode);
2701da177e4SLinus Torvalds 		iput(inode);
2711da177e4SLinus Torvalds 		return error;
2721da177e4SLinus Torvalds 	}
2731da177e4SLinus Torvalds 	return 0;
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds int
affs_mkdir(struct mnt_idmap * idmap,struct inode * dir,struct dentry * dentry,umode_t mode)277c54bd91eSChristian Brauner affs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
278549c7297SChristian Brauner 	   struct dentry *dentry, umode_t mode)
2791da177e4SLinus Torvalds {
2801da177e4SLinus Torvalds 	struct inode		*inode;
2811da177e4SLinus Torvalds 	int			 error;
2821da177e4SLinus Torvalds 
283a455589fSAl Viro 	pr_debug("%s(%lu,\"%pd\",0%ho)\n",
284a455589fSAl Viro 		 __func__, dir->i_ino, dentry, mode);
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds 	inode = affs_new_inode(dir);
2871da177e4SLinus Torvalds 	if (!inode)
2881da177e4SLinus Torvalds 		return -ENOSPC;
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 	inode->i_mode = S_IFDIR | mode;
291c1618208SFabian Frederick 	affs_mode_to_prot(inode);
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	inode->i_op = &affs_dir_inode_operations;
2941da177e4SLinus Torvalds 	inode->i_fop = &affs_dir_operations;
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
2971da177e4SLinus Torvalds 	if (error) {
2986d6b77f1SMiklos Szeredi 		clear_nlink(inode);
2991da177e4SLinus Torvalds 		mark_inode_dirty(inode);
3001da177e4SLinus Torvalds 		iput(inode);
3011da177e4SLinus Torvalds 		return error;
3021da177e4SLinus Torvalds 	}
3031da177e4SLinus Torvalds 	return 0;
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds int
affs_rmdir(struct inode * dir,struct dentry * dentry)3071da177e4SLinus Torvalds affs_rmdir(struct inode *dir, struct dentry *dentry)
3081da177e4SLinus Torvalds {
30908fe100dSGeert Uytterhoeven 	pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino,
3102b0143b5SDavid Howells 		 d_inode(dentry)->i_ino, dentry);
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	return affs_remove_header(dentry);
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds int
affs_symlink(struct mnt_idmap * idmap,struct inode * dir,struct dentry * dentry,const char * symname)3167a77db95SChristian Brauner affs_symlink(struct mnt_idmap *idmap, struct inode *dir,
317549c7297SChristian Brauner 	     struct dentry *dentry, const char *symname)
3181da177e4SLinus Torvalds {
3191da177e4SLinus Torvalds 	struct super_block	*sb = dir->i_sb;
3201da177e4SLinus Torvalds 	struct buffer_head	*bh;
3211da177e4SLinus Torvalds 	struct inode		*inode;
3221da177e4SLinus Torvalds 	char			*p;
3231da177e4SLinus Torvalds 	int			 i, maxlen, error;
3241da177e4SLinus Torvalds 	char			 c, lc;
3251da177e4SLinus Torvalds 
326a455589fSAl Viro 	pr_debug("%s(%lu,\"%pd\" -> \"%s\")\n",
327a455589fSAl Viro 		 __func__, dir->i_ino, dentry, symname);
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1;
3301da177e4SLinus Torvalds 	inode  = affs_new_inode(dir);
3311da177e4SLinus Torvalds 	if (!inode)
3321da177e4SLinus Torvalds 		return -ENOSPC;
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	inode->i_op = &affs_symlink_inode_operations;
33521fc61c7SAl Viro 	inode_nohighmem(inode);
3361da177e4SLinus Torvalds 	inode->i_data.a_ops = &affs_symlink_aops;
3371da177e4SLinus Torvalds 	inode->i_mode = S_IFLNK | 0777;
338c1618208SFabian Frederick 	affs_mode_to_prot(inode);
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	error = -EIO;
3411da177e4SLinus Torvalds 	bh = affs_bread(sb, inode->i_ino);
3421da177e4SLinus Torvalds 	if (!bh)
3431da177e4SLinus Torvalds 		goto err;
3441da177e4SLinus Torvalds 	i  = 0;
3451da177e4SLinus Torvalds 	p  = (char *)AFFS_HEAD(bh)->table;
3461da177e4SLinus Torvalds 	lc = '/';
3471da177e4SLinus Torvalds 	if (*symname == '/') {
34829333920SAl Viro 		struct affs_sb_info *sbi = AFFS_SB(sb);
3491da177e4SLinus Torvalds 		while (*symname == '/')
3501da177e4SLinus Torvalds 			symname++;
35129333920SAl Viro 		spin_lock(&sbi->symlink_lock);
35229333920SAl Viro 		while (sbi->s_volume[i])	/* Cannot overflow */
35329333920SAl Viro 			*p++ = sbi->s_volume[i++];
35429333920SAl Viro 		spin_unlock(&sbi->symlink_lock);
3551da177e4SLinus Torvalds 	}
3561da177e4SLinus Torvalds 	while (i < maxlen && (c = *symname++)) {
3571da177e4SLinus Torvalds 		if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
3581da177e4SLinus Torvalds 			*p++ = '/';
3591da177e4SLinus Torvalds 			i++;
3601da177e4SLinus Torvalds 			symname += 2;
3611da177e4SLinus Torvalds 			lc = '/';
3621da177e4SLinus Torvalds 		} else if (c == '.' && lc == '/' && *symname == '/') {
3631da177e4SLinus Torvalds 			symname++;
3641da177e4SLinus Torvalds 			lc = '/';
3651da177e4SLinus Torvalds 		} else {
3661da177e4SLinus Torvalds 			*p++ = c;
3671da177e4SLinus Torvalds 			lc   = c;
3681da177e4SLinus Torvalds 			i++;
3691da177e4SLinus Torvalds 		}
3701da177e4SLinus Torvalds 		if (lc == '/')
3711da177e4SLinus Torvalds 			while (*symname == '/')
3721da177e4SLinus Torvalds 				symname++;
3731da177e4SLinus Torvalds 	}
3741da177e4SLinus Torvalds 	*p = 0;
375f1bf9072SFabian Frederick 	inode->i_size = i + 1;
3761da177e4SLinus Torvalds 	mark_buffer_dirty_inode(bh, inode);
3771da177e4SLinus Torvalds 	affs_brelse(bh);
3781da177e4SLinus Torvalds 	mark_inode_dirty(inode);
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
3811da177e4SLinus Torvalds 	if (error)
3821da177e4SLinus Torvalds 		goto err;
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	return 0;
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds err:
3876d6b77f1SMiklos Szeredi 	clear_nlink(inode);
3881da177e4SLinus Torvalds 	mark_inode_dirty(inode);
3891da177e4SLinus Torvalds 	iput(inode);
3901da177e4SLinus Torvalds 	return error;
3911da177e4SLinus Torvalds }
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds int
affs_link(struct dentry * old_dentry,struct inode * dir,struct dentry * dentry)3941da177e4SLinus Torvalds affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
3951da177e4SLinus Torvalds {
3962b0143b5SDavid Howells 	struct inode *inode = d_inode(old_dentry);
3971da177e4SLinus Torvalds 
39808fe100dSGeert Uytterhoeven 	pr_debug("%s(%lu, %lu, \"%pd\")\n", __func__, inode->i_ino, dir->i_ino,
399a455589fSAl Viro 		 dentry);
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	return affs_add_entry(dir, inode, dentry, ST_LINKFILE);
4021da177e4SLinus Torvalds }
4031da177e4SLinus Torvalds 
404c6184028SFabian Frederick static int
affs_rename(struct inode * old_dir,struct dentry * old_dentry,struct inode * new_dir,struct dentry * new_dentry)4051da177e4SLinus Torvalds affs_rename(struct inode *old_dir, struct dentry *old_dentry,
406c6184028SFabian Frederick 	    struct inode *new_dir, struct dentry *new_dentry)
4071da177e4SLinus Torvalds {
4081da177e4SLinus Torvalds 	struct super_block *sb = old_dir->i_sb;
4091da177e4SLinus Torvalds 	struct buffer_head *bh = NULL;
4101da177e4SLinus Torvalds 	int retval;
4111da177e4SLinus Torvalds 
4128ca57722SFabian Frederick 	retval = affs_check_name(new_dentry->d_name.name,
4138ca57722SFabian Frederick 				 new_dentry->d_name.len,
4148ca57722SFabian Frederick 				 affs_nofilenametruncate(old_dentry));
4158ca57722SFabian Frederick 
4161da177e4SLinus Torvalds 	if (retval)
4171da177e4SLinus Torvalds 		return retval;
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	/* Unlink destination if it already exists */
4202b0143b5SDavid Howells 	if (d_really_is_positive(new_dentry)) {
4211da177e4SLinus Torvalds 		retval = affs_remove_header(new_dentry);
4221da177e4SLinus Torvalds 		if (retval)
4231da177e4SLinus Torvalds 			return retval;
4241da177e4SLinus Torvalds 	}
4251da177e4SLinus Torvalds 
4262b0143b5SDavid Howells 	bh = affs_bread(sb, d_inode(old_dentry)->i_ino);
4271da177e4SLinus Torvalds 	if (!bh)
4283ac81413SFlorin Malita 		return -EIO;
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	/* Remove header from its parent directory. */
4311da177e4SLinus Torvalds 	affs_lock_dir(old_dir);
4321da177e4SLinus Torvalds 	retval = affs_remove_hash(old_dir, bh);
4331da177e4SLinus Torvalds 	affs_unlock_dir(old_dir);
4341da177e4SLinus Torvalds 	if (retval)
4351da177e4SLinus Torvalds 		goto done;
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	/* And insert it into the new directory with the new name. */
4381da177e4SLinus Torvalds 	affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
4391da177e4SLinus Torvalds 	affs_fix_checksum(sb, bh);
4401da177e4SLinus Torvalds 	affs_lock_dir(new_dir);
4411da177e4SLinus Torvalds 	retval = affs_insert_hash(new_dir, bh);
4421da177e4SLinus Torvalds 	affs_unlock_dir(new_dir);
4431da177e4SLinus Torvalds 	/* TODO: move it back to old_dir, if error? */
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds done:
4461da177e4SLinus Torvalds 	mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
4471da177e4SLinus Torvalds 	affs_brelse(bh);
4481da177e4SLinus Torvalds 	return retval;
4491da177e4SLinus Torvalds }
450ed4433d7SFabian Frederick 
4516b465766SFabian Frederick static int
affs_xrename(struct inode * old_dir,struct dentry * old_dentry,struct inode * new_dir,struct dentry * new_dentry)4526b465766SFabian Frederick affs_xrename(struct inode *old_dir, struct dentry *old_dentry,
4536b465766SFabian Frederick 	     struct inode *new_dir, struct dentry *new_dentry)
4546b465766SFabian Frederick {
4556b465766SFabian Frederick 
4566b465766SFabian Frederick 	struct super_block *sb = old_dir->i_sb;
4576b465766SFabian Frederick 	struct buffer_head *bh_old = NULL;
4586b465766SFabian Frederick 	struct buffer_head *bh_new = NULL;
4596b465766SFabian Frederick 	int retval;
4606b465766SFabian Frederick 
4616b465766SFabian Frederick 	bh_old = affs_bread(sb, d_inode(old_dentry)->i_ino);
4626b465766SFabian Frederick 	if (!bh_old)
4636b465766SFabian Frederick 		return -EIO;
4646b465766SFabian Frederick 
4656b465766SFabian Frederick 	bh_new = affs_bread(sb, d_inode(new_dentry)->i_ino);
46670779b89SPan Bian 	if (!bh_new) {
46770779b89SPan Bian 		affs_brelse(bh_old);
4686b465766SFabian Frederick 		return -EIO;
46970779b89SPan Bian 	}
4706b465766SFabian Frederick 
4716b465766SFabian Frederick 	/* Remove old header from its parent directory. */
4726b465766SFabian Frederick 	affs_lock_dir(old_dir);
4736b465766SFabian Frederick 	retval = affs_remove_hash(old_dir, bh_old);
4746b465766SFabian Frederick 	affs_unlock_dir(old_dir);
4756b465766SFabian Frederick 	if (retval)
4766b465766SFabian Frederick 		goto done;
4776b465766SFabian Frederick 
4786b465766SFabian Frederick 	/* Remove new header from its parent directory. */
4796b465766SFabian Frederick 	affs_lock_dir(new_dir);
4806b465766SFabian Frederick 	retval = affs_remove_hash(new_dir, bh_new);
4816b465766SFabian Frederick 	affs_unlock_dir(new_dir);
4826b465766SFabian Frederick 	if (retval)
4836b465766SFabian Frederick 		goto done;
4846b465766SFabian Frederick 
4856b465766SFabian Frederick 	/* Insert old into the new directory with the new name. */
4866b465766SFabian Frederick 	affs_copy_name(AFFS_TAIL(sb, bh_old)->name, new_dentry);
4876b465766SFabian Frederick 	affs_fix_checksum(sb, bh_old);
4886b465766SFabian Frederick 	affs_lock_dir(new_dir);
4896b465766SFabian Frederick 	retval = affs_insert_hash(new_dir, bh_old);
4906b465766SFabian Frederick 	affs_unlock_dir(new_dir);
4916b465766SFabian Frederick 
4926b465766SFabian Frederick 	/* Insert new into the old directory with the old name. */
4936b465766SFabian Frederick 	affs_copy_name(AFFS_TAIL(sb, bh_new)->name, old_dentry);
4946b465766SFabian Frederick 	affs_fix_checksum(sb, bh_new);
4956b465766SFabian Frederick 	affs_lock_dir(old_dir);
4966b465766SFabian Frederick 	retval = affs_insert_hash(old_dir, bh_new);
4976b465766SFabian Frederick 	affs_unlock_dir(old_dir);
4986b465766SFabian Frederick done:
4996b465766SFabian Frederick 	mark_buffer_dirty_inode(bh_old, new_dir);
5006b465766SFabian Frederick 	mark_buffer_dirty_inode(bh_new, old_dir);
5016b465766SFabian Frederick 	affs_brelse(bh_old);
5026b465766SFabian Frederick 	affs_brelse(bh_new);
5036b465766SFabian Frederick 	return retval;
5046b465766SFabian Frederick }
5056b465766SFabian Frederick 
affs_rename2(struct mnt_idmap * idmap,struct inode * old_dir,struct dentry * old_dentry,struct inode * new_dir,struct dentry * new_dentry,unsigned int flags)506e18275aeSChristian Brauner int affs_rename2(struct mnt_idmap *idmap, struct inode *old_dir,
507549c7297SChristian Brauner 		 struct dentry *old_dentry, struct inode *new_dir,
508549c7297SChristian Brauner 		 struct dentry *new_dentry, unsigned int flags)
509c6184028SFabian Frederick {
510c6184028SFabian Frederick 
5116b465766SFabian Frederick 	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
512c6184028SFabian Frederick 		return -EINVAL;
513c6184028SFabian Frederick 
514c6184028SFabian Frederick 	pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__,
515c6184028SFabian Frederick 		 old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry);
516c6184028SFabian Frederick 
5176b465766SFabian Frederick 	if (flags & RENAME_EXCHANGE)
5186b465766SFabian Frederick 		return affs_xrename(old_dir, old_dentry, new_dir, new_dentry);
5196b465766SFabian Frederick 
520c6184028SFabian Frederick 	return affs_rename(old_dir, old_dentry, new_dir, new_dentry);
521c6184028SFabian Frederick }
522c6184028SFabian Frederick 
affs_get_parent(struct dentry * child)523b3b42c0dSFabian Frederick static struct dentry *affs_get_parent(struct dentry *child)
524b3b42c0dSFabian Frederick {
525b3b42c0dSFabian Frederick 	struct inode *parent;
526b3b42c0dSFabian Frederick 	struct buffer_head *bh;
527b3b42c0dSFabian Frederick 
528b3b42c0dSFabian Frederick 	bh = affs_bread(child->d_sb, d_inode(child)->i_ino);
529b3b42c0dSFabian Frederick 	if (!bh)
530b3b42c0dSFabian Frederick 		return ERR_PTR(-EIO);
531b3b42c0dSFabian Frederick 
532b3b42c0dSFabian Frederick 	parent = affs_iget(child->d_sb,
533b3b42c0dSFabian Frederick 			   be32_to_cpu(AFFS_TAIL(child->d_sb, bh)->parent));
534b3b42c0dSFabian Frederick 	brelse(bh);
535b3b42c0dSFabian Frederick 	if (IS_ERR(parent))
536b3b42c0dSFabian Frederick 		return ERR_CAST(parent);
537b3b42c0dSFabian Frederick 
538b3b42c0dSFabian Frederick 	return d_obtain_alias(parent);
539b3b42c0dSFabian Frederick }
540b3b42c0dSFabian Frederick 
affs_nfs_get_inode(struct super_block * sb,u64 ino,u32 generation)541ed4433d7SFabian Frederick static struct inode *affs_nfs_get_inode(struct super_block *sb, u64 ino,
542ed4433d7SFabian Frederick 					u32 generation)
543ed4433d7SFabian Frederick {
544ed4433d7SFabian Frederick 	struct inode *inode;
545ed4433d7SFabian Frederick 
546ed4433d7SFabian Frederick 	if (!affs_validblock(sb, ino))
547ed4433d7SFabian Frederick 		return ERR_PTR(-ESTALE);
548ed4433d7SFabian Frederick 
549ed4433d7SFabian Frederick 	inode = affs_iget(sb, ino);
550ed4433d7SFabian Frederick 	if (IS_ERR(inode))
551ed4433d7SFabian Frederick 		return ERR_CAST(inode);
552ed4433d7SFabian Frederick 
553ed4433d7SFabian Frederick 	return inode;
554ed4433d7SFabian Frederick }
555ed4433d7SFabian Frederick 
affs_fh_to_dentry(struct super_block * sb,struct fid * fid,int fh_len,int fh_type)556ed4433d7SFabian Frederick static struct dentry *affs_fh_to_dentry(struct super_block *sb, struct fid *fid,
557ed4433d7SFabian Frederick 					int fh_len, int fh_type)
558ed4433d7SFabian Frederick {
559ed4433d7SFabian Frederick 	return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
560ed4433d7SFabian Frederick 				    affs_nfs_get_inode);
561ed4433d7SFabian Frederick }
562ed4433d7SFabian Frederick 
affs_fh_to_parent(struct super_block * sb,struct fid * fid,int fh_len,int fh_type)563ed4433d7SFabian Frederick static struct dentry *affs_fh_to_parent(struct super_block *sb, struct fid *fid,
564ed4433d7SFabian Frederick 					int fh_len, int fh_type)
565ed4433d7SFabian Frederick {
566ed4433d7SFabian Frederick 	return generic_fh_to_parent(sb, fid, fh_len, fh_type,
567ed4433d7SFabian Frederick 				    affs_nfs_get_inode);
568ed4433d7SFabian Frederick }
569ed4433d7SFabian Frederick 
570ed4433d7SFabian Frederick const struct export_operations affs_export_ops = {
571ed4433d7SFabian Frederick 	.fh_to_dentry = affs_fh_to_dentry,
572ed4433d7SFabian Frederick 	.fh_to_parent = affs_fh_to_parent,
573b3b42c0dSFabian Frederick 	.get_parent = affs_get_parent,
574ed4433d7SFabian Frederick };
575f567accbSFabian Frederick 
576f567accbSFabian Frederick const struct dentry_operations affs_dentry_operations = {
577f567accbSFabian Frederick 	.d_hash		= affs_hash_dentry,
578f567accbSFabian Frederick 	.d_compare	= affs_compare_dentry,
579f567accbSFabian Frederick };
580f567accbSFabian Frederick 
581f567accbSFabian Frederick const struct dentry_operations affs_intl_dentry_operations = {
582f567accbSFabian Frederick 	.d_hash		= affs_intl_hash_dentry,
583f567accbSFabian Frederick 	.d_compare	= affs_intl_compare_dentry,
584f567accbSFabian Frederick };
585