xref: /openbmc/linux/fs/erofs/namei.c (revision 96d3c5a7d20ec546e44695983fe0508c6f904248)
147e4937aSGao Xiang // SPDX-License-Identifier: GPL-2.0-only
247e4937aSGao Xiang /*
347e4937aSGao Xiang  * Copyright (C) 2017-2018 HUAWEI, Inc.
4592e7cd0SAlexander A. Klimov  *             https://www.huawei.com/
5500edd09SGao Xiang  * Copyright (C) 2022, Alibaba Cloud
647e4937aSGao Xiang  */
747e4937aSGao Xiang #include "xattr.h"
847e4937aSGao Xiang #include <trace/events/erofs.h>
947e4937aSGao Xiang 
1047e4937aSGao Xiang struct erofs_qstr {
1147e4937aSGao Xiang 	const unsigned char *name;
1247e4937aSGao Xiang 	const unsigned char *end;
1347e4937aSGao Xiang };
1447e4937aSGao Xiang 
1547e4937aSGao Xiang /* based on the end of qn is accurate and it must have the trailing '\0' */
erofs_dirnamecmp(const struct erofs_qstr * qn,const struct erofs_qstr * qd,unsigned int * matched)1699634bf3SGao Xiang static inline int erofs_dirnamecmp(const struct erofs_qstr *qn,
1747e4937aSGao Xiang 				   const struct erofs_qstr *qd,
1847e4937aSGao Xiang 				   unsigned int *matched)
1947e4937aSGao Xiang {
2047e4937aSGao Xiang 	unsigned int i = *matched;
2147e4937aSGao Xiang 
2247e4937aSGao Xiang 	/*
2347e4937aSGao Xiang 	 * on-disk error, let's only BUG_ON in the debugging mode.
2447e4937aSGao Xiang 	 * otherwise, it will return 1 to just skip the invalid name
2547e4937aSGao Xiang 	 * and go on (in consideration of the lookup performance).
2647e4937aSGao Xiang 	 */
2747e4937aSGao Xiang 	DBG_BUGON(qd->name > qd->end);
2847e4937aSGao Xiang 
2947e4937aSGao Xiang 	/* qd could not have trailing '\0' */
3047e4937aSGao Xiang 	/* However it is absolutely safe if < qd->end */
3147e4937aSGao Xiang 	while (qd->name + i < qd->end && qd->name[i] != '\0') {
3247e4937aSGao Xiang 		if (qn->name[i] != qd->name[i]) {
3347e4937aSGao Xiang 			*matched = i;
3447e4937aSGao Xiang 			return qn->name[i] > qd->name[i] ? 1 : -1;
3547e4937aSGao Xiang 		}
3647e4937aSGao Xiang 		++i;
3747e4937aSGao Xiang 	}
3847e4937aSGao Xiang 	*matched = i;
3947e4937aSGao Xiang 	/* See comments in __d_alloc on the terminating NUL character */
4047e4937aSGao Xiang 	return qn->name[i] == '\0' ? 0 : 1;
4147e4937aSGao Xiang }
4247e4937aSGao Xiang 
4347e4937aSGao Xiang #define nameoff_from_disk(off, sz)	(le16_to_cpu(off) & ((sz) - 1))
4447e4937aSGao Xiang 
find_target_dirent(struct erofs_qstr * name,u8 * data,unsigned int dirblksize,const int ndirents)4547e4937aSGao Xiang static struct erofs_dirent *find_target_dirent(struct erofs_qstr *name,
4647e4937aSGao Xiang 					       u8 *data,
4747e4937aSGao Xiang 					       unsigned int dirblksize,
4847e4937aSGao Xiang 					       const int ndirents)
4947e4937aSGao Xiang {
5047e4937aSGao Xiang 	int head, back;
5147e4937aSGao Xiang 	unsigned int startprfx, endprfx;
5247e4937aSGao Xiang 	struct erofs_dirent *const de = (struct erofs_dirent *)data;
5347e4937aSGao Xiang 
5447e4937aSGao Xiang 	/* since the 1st dirent has been evaluated previously */
5547e4937aSGao Xiang 	head = 1;
5647e4937aSGao Xiang 	back = ndirents - 1;
5747e4937aSGao Xiang 	startprfx = endprfx = 0;
5847e4937aSGao Xiang 
5947e4937aSGao Xiang 	while (head <= back) {
6047e4937aSGao Xiang 		const int mid = head + (back - head) / 2;
6147e4937aSGao Xiang 		const int nameoff = nameoff_from_disk(de[mid].nameoff,
6247e4937aSGao Xiang 						      dirblksize);
6347e4937aSGao Xiang 		unsigned int matched = min(startprfx, endprfx);
6447e4937aSGao Xiang 		struct erofs_qstr dname = {
6547e4937aSGao Xiang 			.name = data + nameoff,
668d8a09b0SGao Xiang 			.end = mid >= ndirents - 1 ?
6747e4937aSGao Xiang 				data + dirblksize :
6847e4937aSGao Xiang 				data + nameoff_from_disk(de[mid + 1].nameoff,
6947e4937aSGao Xiang 							 dirblksize)
7047e4937aSGao Xiang 		};
7147e4937aSGao Xiang 
7247e4937aSGao Xiang 		/* string comparison without already matched prefix */
7399634bf3SGao Xiang 		int ret = erofs_dirnamecmp(name, &dname, &matched);
7447e4937aSGao Xiang 
758d8a09b0SGao Xiang 		if (!ret) {
7647e4937aSGao Xiang 			return de + mid;
7747e4937aSGao Xiang 		} else if (ret > 0) {
7847e4937aSGao Xiang 			head = mid + 1;
7947e4937aSGao Xiang 			startprfx = matched;
8047e4937aSGao Xiang 		} else {
8147e4937aSGao Xiang 			back = mid - 1;
8247e4937aSGao Xiang 			endprfx = matched;
8347e4937aSGao Xiang 		}
8447e4937aSGao Xiang 	}
8547e4937aSGao Xiang 
8647e4937aSGao Xiang 	return ERR_PTR(-ENOENT);
8747e4937aSGao Xiang }
8847e4937aSGao Xiang 
erofs_find_target_block(struct erofs_buf * target,struct inode * dir,struct erofs_qstr * name,int * _ndirents)894efdec36SGao Xiang static void *erofs_find_target_block(struct erofs_buf *target,
904efdec36SGao Xiang 		struct inode *dir, struct erofs_qstr *name, int *_ndirents)
9147e4937aSGao Xiang {
923acea5fcSJingbo Xu 	unsigned int bsz = i_blocksize(dir);
933acea5fcSJingbo Xu 	int head = 0, back = erofs_iblks(dir) - 1;
944efdec36SGao Xiang 	unsigned int startprfx = 0, endprfx = 0;
95500edd09SGao Xiang 	void *candidate = ERR_PTR(-ENOENT);
9647e4937aSGao Xiang 
9747e4937aSGao Xiang 	while (head <= back) {
9847e4937aSGao Xiang 		const int mid = head + (back - head) / 2;
99500edd09SGao Xiang 		struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
100500edd09SGao Xiang 		struct erofs_dirent *de;
10147e4937aSGao Xiang 
102eb2c5e41SGao Xiang 		buf.inode = dir;
103eb2c5e41SGao Xiang 		de = erofs_bread(&buf, mid, EROFS_KMAP);
104500edd09SGao Xiang 		if (!IS_ERR(de)) {
1053acea5fcSJingbo Xu 			const int nameoff = nameoff_from_disk(de->nameoff, bsz);
10647e4937aSGao Xiang 			const int ndirents = nameoff / sizeof(*de);
10747e4937aSGao Xiang 			int diff;
10847e4937aSGao Xiang 			unsigned int matched;
10947e4937aSGao Xiang 			struct erofs_qstr dname;
11047e4937aSGao Xiang 
1118d8a09b0SGao Xiang 			if (!ndirents) {
112500edd09SGao Xiang 				erofs_put_metabuf(&buf);
1134f761fa2SGao Xiang 				erofs_err(dir->i_sb,
1144f761fa2SGao Xiang 					  "corrupted dir block %d @ nid %llu",
115a5876e24SGao Xiang 					  mid, EROFS_I(dir)->nid);
11647e4937aSGao Xiang 				DBG_BUGON(1);
117500edd09SGao Xiang 				de = ERR_PTR(-EFSCORRUPTED);
11847e4937aSGao Xiang 				goto out;
11947e4937aSGao Xiang 			}
12047e4937aSGao Xiang 
12147e4937aSGao Xiang 			matched = min(startprfx, endprfx);
12247e4937aSGao Xiang 
12347e4937aSGao Xiang 			dname.name = (u8 *)de + nameoff;
12447e4937aSGao Xiang 			if (ndirents == 1)
1253acea5fcSJingbo Xu 				dname.end = (u8 *)de + bsz;
12647e4937aSGao Xiang 			else
12747e4937aSGao Xiang 				dname.end = (u8 *)de +
1283acea5fcSJingbo Xu 					nameoff_from_disk(de[1].nameoff, bsz);
12947e4937aSGao Xiang 
13047e4937aSGao Xiang 			/* string comparison without already matched prefix */
13199634bf3SGao Xiang 			diff = erofs_dirnamecmp(name, &dname, &matched);
13247e4937aSGao Xiang 
133*ba84bbbcSSandeep Dhavale 			if (diff < 0) {
134*ba84bbbcSSandeep Dhavale 				erofs_put_metabuf(&buf);
135*ba84bbbcSSandeep Dhavale 				back = mid - 1;
136*ba84bbbcSSandeep Dhavale 				endprfx = matched;
137*ba84bbbcSSandeep Dhavale 				continue;
138*ba84bbbcSSandeep Dhavale 			}
13947e4937aSGao Xiang 
14047e4937aSGao Xiang 			if (!IS_ERR(candidate))
141500edd09SGao Xiang 				erofs_put_metabuf(target);
142500edd09SGao Xiang 			*target = buf;
143*ba84bbbcSSandeep Dhavale 			if (!diff) {
144*ba84bbbcSSandeep Dhavale 				*_ndirents = 0;
145*ba84bbbcSSandeep Dhavale 				return de;
146*ba84bbbcSSandeep Dhavale 			}
147*ba84bbbcSSandeep Dhavale 			head = mid + 1;
148*ba84bbbcSSandeep Dhavale 			startprfx = matched;
149500edd09SGao Xiang 			candidate = de;
15047e4937aSGao Xiang 			*_ndirents = ndirents;
15147e4937aSGao Xiang 			continue;
15247e4937aSGao Xiang 		}
15347e4937aSGao Xiang out:		/* free if the candidate is valid */
15447e4937aSGao Xiang 		if (!IS_ERR(candidate))
155500edd09SGao Xiang 			erofs_put_metabuf(target);
156500edd09SGao Xiang 		return de;
15747e4937aSGao Xiang 	}
15847e4937aSGao Xiang 	return candidate;
15947e4937aSGao Xiang }
16047e4937aSGao Xiang 
erofs_namei(struct inode * dir,const struct qstr * name,erofs_nid_t * nid,unsigned int * d_type)1613e917cc3SHongnan Li int erofs_namei(struct inode *dir, const struct qstr *name, erofs_nid_t *nid,
1623e917cc3SHongnan Li 		unsigned int *d_type)
16347e4937aSGao Xiang {
16447e4937aSGao Xiang 	int ndirents;
165500edd09SGao Xiang 	struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
16647e4937aSGao Xiang 	struct erofs_dirent *de;
16747e4937aSGao Xiang 	struct erofs_qstr qn;
16847e4937aSGao Xiang 
1698d8a09b0SGao Xiang 	if (!dir->i_size)
17047e4937aSGao Xiang 		return -ENOENT;
17147e4937aSGao Xiang 
17247e4937aSGao Xiang 	qn.name = name->name;
17347e4937aSGao Xiang 	qn.end = name->name + name->len;
174eb2c5e41SGao Xiang 	buf.inode = dir;
17547e4937aSGao Xiang 
17647e4937aSGao Xiang 	ndirents = 0;
1774efdec36SGao Xiang 	de = erofs_find_target_block(&buf, dir, &qn, &ndirents);
178500edd09SGao Xiang 	if (IS_ERR(de))
179500edd09SGao Xiang 		return PTR_ERR(de);
18047e4937aSGao Xiang 
18147e4937aSGao Xiang 	if (ndirents)
1823acea5fcSJingbo Xu 		de = find_target_dirent(&qn, (u8 *)de, i_blocksize(dir),
1833acea5fcSJingbo Xu 					ndirents);
18447e4937aSGao Xiang 
18547e4937aSGao Xiang 	if (!IS_ERR(de)) {
18647e4937aSGao Xiang 		*nid = le64_to_cpu(de->nid);
18747e4937aSGao Xiang 		*d_type = de->file_type;
18847e4937aSGao Xiang 	}
189500edd09SGao Xiang 	erofs_put_metabuf(&buf);
19047e4937aSGao Xiang 	return PTR_ERR_OR_ZERO(de);
19147e4937aSGao Xiang }
19247e4937aSGao Xiang 
erofs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)19353a7f996SGao Xiang static struct dentry *erofs_lookup(struct inode *dir, struct dentry *dentry,
19447e4937aSGao Xiang 				   unsigned int flags)
19547e4937aSGao Xiang {
19647e4937aSGao Xiang 	int err;
19747e4937aSGao Xiang 	erofs_nid_t nid;
19847e4937aSGao Xiang 	unsigned int d_type;
19947e4937aSGao Xiang 	struct inode *inode;
20047e4937aSGao Xiang 
20147e4937aSGao Xiang 	trace_erofs_lookup(dir, dentry, flags);
20247e4937aSGao Xiang 
2038d8a09b0SGao Xiang 	if (dentry->d_name.len > EROFS_NAME_LEN)
20447e4937aSGao Xiang 		return ERR_PTR(-ENAMETOOLONG);
20547e4937aSGao Xiang 
20647e4937aSGao Xiang 	err = erofs_namei(dir, &dentry->d_name, &nid, &d_type);
20747e4937aSGao Xiang 
20810656f9cSGao Xiang 	if (err == -ENOENT)
20947e4937aSGao Xiang 		/* negative dentry */
21047e4937aSGao Xiang 		inode = NULL;
21110656f9cSGao Xiang 	else if (err)
21247e4937aSGao Xiang 		inode = ERR_PTR(err);
21310656f9cSGao Xiang 	else
214312fe643SGao Xiang 		inode = erofs_iget(dir->i_sb, nid);
21547e4937aSGao Xiang 	return d_splice_alias(inode, dentry);
21647e4937aSGao Xiang }
21747e4937aSGao Xiang 
21847e4937aSGao Xiang const struct inode_operations erofs_dir_iops = {
21947e4937aSGao Xiang 	.lookup = erofs_lookup,
22047e4937aSGao Xiang 	.getattr = erofs_getattr,
22147e4937aSGao Xiang 	.listxattr = erofs_listxattr,
222cac2f8b8SChristian Brauner 	.get_inode_acl = erofs_get_acl,
223eadcd6b5SGao Xiang 	.fiemap = erofs_fiemap,
22447e4937aSGao Xiang };
225