xref: /openbmc/linux/fs/erofs/namei.c (revision 53a7f9961cddf505a9f3a7baa017e5af31838840)
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 
947e4937aSGao Xiang #include <trace/events/erofs.h>
1047e4937aSGao Xiang 
1147e4937aSGao Xiang struct erofs_qstr {
1247e4937aSGao Xiang 	const unsigned char *name;
1347e4937aSGao Xiang 	const unsigned char *end;
1447e4937aSGao Xiang };
1547e4937aSGao Xiang 
1647e4937aSGao Xiang /* based on the end of qn is accurate and it must have the trailing '\0' */
1799634bf3SGao Xiang static inline int erofs_dirnamecmp(const struct erofs_qstr *qn,
1847e4937aSGao Xiang 				   const struct erofs_qstr *qd,
1947e4937aSGao Xiang 				   unsigned int *matched)
2047e4937aSGao Xiang {
2147e4937aSGao Xiang 	unsigned int i = *matched;
2247e4937aSGao Xiang 
2347e4937aSGao Xiang 	/*
2447e4937aSGao Xiang 	 * on-disk error, let's only BUG_ON in the debugging mode.
2547e4937aSGao Xiang 	 * otherwise, it will return 1 to just skip the invalid name
2647e4937aSGao Xiang 	 * and go on (in consideration of the lookup performance).
2747e4937aSGao Xiang 	 */
2847e4937aSGao Xiang 	DBG_BUGON(qd->name > qd->end);
2947e4937aSGao Xiang 
3047e4937aSGao Xiang 	/* qd could not have trailing '\0' */
3147e4937aSGao Xiang 	/* However it is absolutely safe if < qd->end */
3247e4937aSGao Xiang 	while (qd->name + i < qd->end && qd->name[i] != '\0') {
3347e4937aSGao Xiang 		if (qn->name[i] != qd->name[i]) {
3447e4937aSGao Xiang 			*matched = i;
3547e4937aSGao Xiang 			return qn->name[i] > qd->name[i] ? 1 : -1;
3647e4937aSGao Xiang 		}
3747e4937aSGao Xiang 		++i;
3847e4937aSGao Xiang 	}
3947e4937aSGao Xiang 	*matched = i;
4047e4937aSGao Xiang 	/* See comments in __d_alloc on the terminating NUL character */
4147e4937aSGao Xiang 	return qn->name[i] == '\0' ? 0 : 1;
4247e4937aSGao Xiang }
4347e4937aSGao Xiang 
4447e4937aSGao Xiang #define nameoff_from_disk(off, sz)	(le16_to_cpu(off) & ((sz) - 1))
4547e4937aSGao Xiang 
4647e4937aSGao Xiang static struct erofs_dirent *find_target_dirent(struct erofs_qstr *name,
4747e4937aSGao Xiang 					       u8 *data,
4847e4937aSGao Xiang 					       unsigned int dirblksize,
4947e4937aSGao Xiang 					       const int ndirents)
5047e4937aSGao Xiang {
5147e4937aSGao Xiang 	int head, back;
5247e4937aSGao Xiang 	unsigned int startprfx, endprfx;
5347e4937aSGao Xiang 	struct erofs_dirent *const de = (struct erofs_dirent *)data;
5447e4937aSGao Xiang 
5547e4937aSGao Xiang 	/* since the 1st dirent has been evaluated previously */
5647e4937aSGao Xiang 	head = 1;
5747e4937aSGao Xiang 	back = ndirents - 1;
5847e4937aSGao Xiang 	startprfx = endprfx = 0;
5947e4937aSGao Xiang 
6047e4937aSGao Xiang 	while (head <= back) {
6147e4937aSGao Xiang 		const int mid = head + (back - head) / 2;
6247e4937aSGao Xiang 		const int nameoff = nameoff_from_disk(de[mid].nameoff,
6347e4937aSGao Xiang 						      dirblksize);
6447e4937aSGao Xiang 		unsigned int matched = min(startprfx, endprfx);
6547e4937aSGao Xiang 		struct erofs_qstr dname = {
6647e4937aSGao Xiang 			.name = data + nameoff,
678d8a09b0SGao Xiang 			.end = mid >= ndirents - 1 ?
6847e4937aSGao Xiang 				data + dirblksize :
6947e4937aSGao Xiang 				data + nameoff_from_disk(de[mid + 1].nameoff,
7047e4937aSGao Xiang 							 dirblksize)
7147e4937aSGao Xiang 		};
7247e4937aSGao Xiang 
7347e4937aSGao Xiang 		/* string comparison without already matched prefix */
7499634bf3SGao Xiang 		int ret = erofs_dirnamecmp(name, &dname, &matched);
7547e4937aSGao Xiang 
768d8a09b0SGao Xiang 		if (!ret) {
7747e4937aSGao Xiang 			return de + mid;
7847e4937aSGao Xiang 		} else if (ret > 0) {
7947e4937aSGao Xiang 			head = mid + 1;
8047e4937aSGao Xiang 			startprfx = matched;
8147e4937aSGao Xiang 		} else {
8247e4937aSGao Xiang 			back = mid - 1;
8347e4937aSGao Xiang 			endprfx = matched;
8447e4937aSGao Xiang 		}
8547e4937aSGao Xiang 	}
8647e4937aSGao Xiang 
8747e4937aSGao Xiang 	return ERR_PTR(-ENOENT);
8847e4937aSGao Xiang }
8947e4937aSGao Xiang 
90500edd09SGao Xiang static void *find_target_block_classic(struct erofs_buf *target,
91500edd09SGao Xiang 				       struct inode *dir,
9247e4937aSGao Xiang 				       struct erofs_qstr *name,
9347e4937aSGao Xiang 				       int *_ndirents)
9447e4937aSGao Xiang {
9547e4937aSGao Xiang 	unsigned int startprfx, endprfx;
9647e4937aSGao Xiang 	int head, back;
97500edd09SGao Xiang 	void *candidate = ERR_PTR(-ENOENT);
9847e4937aSGao Xiang 
9947e4937aSGao Xiang 	startprfx = endprfx = 0;
10047e4937aSGao Xiang 	head = 0;
10199634bf3SGao Xiang 	back = erofs_inode_datablocks(dir) - 1;
10247e4937aSGao Xiang 
10347e4937aSGao Xiang 	while (head <= back) {
10447e4937aSGao Xiang 		const int mid = head + (back - head) / 2;
105500edd09SGao Xiang 		struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
106500edd09SGao Xiang 		struct erofs_dirent *de;
10747e4937aSGao Xiang 
108500edd09SGao Xiang 		de = erofs_bread(&buf, dir, mid, EROFS_KMAP);
109500edd09SGao Xiang 		if (!IS_ERR(de)) {
11047e4937aSGao Xiang 			const int nameoff = nameoff_from_disk(de->nameoff,
11147e4937aSGao Xiang 							      EROFS_BLKSIZ);
11247e4937aSGao Xiang 			const int ndirents = nameoff / sizeof(*de);
11347e4937aSGao Xiang 			int diff;
11447e4937aSGao Xiang 			unsigned int matched;
11547e4937aSGao Xiang 			struct erofs_qstr dname;
11647e4937aSGao Xiang 
1178d8a09b0SGao Xiang 			if (!ndirents) {
118500edd09SGao Xiang 				erofs_put_metabuf(&buf);
1194f761fa2SGao Xiang 				erofs_err(dir->i_sb,
1204f761fa2SGao Xiang 					  "corrupted dir block %d @ nid %llu",
121a5876e24SGao Xiang 					  mid, EROFS_I(dir)->nid);
12247e4937aSGao Xiang 				DBG_BUGON(1);
123500edd09SGao Xiang 				de = ERR_PTR(-EFSCORRUPTED);
12447e4937aSGao Xiang 				goto out;
12547e4937aSGao Xiang 			}
12647e4937aSGao Xiang 
12747e4937aSGao Xiang 			matched = min(startprfx, endprfx);
12847e4937aSGao Xiang 
12947e4937aSGao Xiang 			dname.name = (u8 *)de + nameoff;
13047e4937aSGao Xiang 			if (ndirents == 1)
13147e4937aSGao Xiang 				dname.end = (u8 *)de + EROFS_BLKSIZ;
13247e4937aSGao Xiang 			else
13347e4937aSGao Xiang 				dname.end = (u8 *)de +
13447e4937aSGao Xiang 					nameoff_from_disk(de[1].nameoff,
13547e4937aSGao Xiang 							  EROFS_BLKSIZ);
13647e4937aSGao Xiang 
13747e4937aSGao Xiang 			/* string comparison without already matched prefix */
13899634bf3SGao Xiang 			diff = erofs_dirnamecmp(name, &dname, &matched);
13947e4937aSGao Xiang 
1408d8a09b0SGao Xiang 			if (!diff) {
14147e4937aSGao Xiang 				*_ndirents = 0;
14247e4937aSGao Xiang 				goto out;
14347e4937aSGao Xiang 			} else if (diff > 0) {
14447e4937aSGao Xiang 				head = mid + 1;
14547e4937aSGao Xiang 				startprfx = matched;
14647e4937aSGao Xiang 
14747e4937aSGao Xiang 				if (!IS_ERR(candidate))
148500edd09SGao Xiang 					erofs_put_metabuf(target);
149500edd09SGao Xiang 				*target = buf;
150500edd09SGao Xiang 				candidate = de;
15147e4937aSGao Xiang 				*_ndirents = ndirents;
15247e4937aSGao Xiang 			} else {
153500edd09SGao Xiang 				erofs_put_metabuf(&buf);
15447e4937aSGao Xiang 
15547e4937aSGao Xiang 				back = mid - 1;
15647e4937aSGao Xiang 				endprfx = matched;
15747e4937aSGao Xiang 			}
15847e4937aSGao Xiang 			continue;
15947e4937aSGao Xiang 		}
16047e4937aSGao Xiang out:		/* free if the candidate is valid */
16147e4937aSGao Xiang 		if (!IS_ERR(candidate))
162500edd09SGao Xiang 			erofs_put_metabuf(target);
163500edd09SGao Xiang 		return de;
16447e4937aSGao Xiang 	}
16547e4937aSGao Xiang 	return candidate;
16647e4937aSGao Xiang }
16747e4937aSGao Xiang 
1683e917cc3SHongnan Li int erofs_namei(struct inode *dir, const struct qstr *name, erofs_nid_t *nid,
1693e917cc3SHongnan Li 		unsigned int *d_type)
17047e4937aSGao Xiang {
17147e4937aSGao Xiang 	int ndirents;
172500edd09SGao Xiang 	struct erofs_buf buf = __EROFS_BUF_INITIALIZER;
17347e4937aSGao Xiang 	struct erofs_dirent *de;
17447e4937aSGao Xiang 	struct erofs_qstr qn;
17547e4937aSGao Xiang 
1768d8a09b0SGao Xiang 	if (!dir->i_size)
17747e4937aSGao Xiang 		return -ENOENT;
17847e4937aSGao Xiang 
17947e4937aSGao Xiang 	qn.name = name->name;
18047e4937aSGao Xiang 	qn.end = name->name + name->len;
18147e4937aSGao Xiang 
18247e4937aSGao Xiang 	ndirents = 0;
18347e4937aSGao Xiang 
184500edd09SGao Xiang 	de = find_target_block_classic(&buf, dir, &qn, &ndirents);
185500edd09SGao Xiang 	if (IS_ERR(de))
186500edd09SGao Xiang 		return PTR_ERR(de);
18747e4937aSGao Xiang 
18847e4937aSGao Xiang 	if (ndirents)
189500edd09SGao Xiang 		de = find_target_dirent(&qn, (u8 *)de, EROFS_BLKSIZ, ndirents);
19047e4937aSGao Xiang 
19147e4937aSGao Xiang 	if (!IS_ERR(de)) {
19247e4937aSGao Xiang 		*nid = le64_to_cpu(de->nid);
19347e4937aSGao Xiang 		*d_type = de->file_type;
19447e4937aSGao Xiang 	}
195500edd09SGao Xiang 	erofs_put_metabuf(&buf);
19647e4937aSGao Xiang 	return PTR_ERR_OR_ZERO(de);
19747e4937aSGao Xiang }
19847e4937aSGao Xiang 
199*53a7f996SGao Xiang static struct dentry *erofs_lookup(struct inode *dir, struct dentry *dentry,
20047e4937aSGao Xiang 				   unsigned int flags)
20147e4937aSGao Xiang {
20247e4937aSGao Xiang 	int err;
20347e4937aSGao Xiang 	erofs_nid_t nid;
20447e4937aSGao Xiang 	unsigned int d_type;
20547e4937aSGao Xiang 	struct inode *inode;
20647e4937aSGao Xiang 
20747e4937aSGao Xiang 	trace_erofs_lookup(dir, dentry, flags);
20847e4937aSGao Xiang 
2098d8a09b0SGao Xiang 	if (dentry->d_name.len > EROFS_NAME_LEN)
21047e4937aSGao Xiang 		return ERR_PTR(-ENAMETOOLONG);
21147e4937aSGao Xiang 
21247e4937aSGao Xiang 	err = erofs_namei(dir, &dentry->d_name, &nid, &d_type);
21347e4937aSGao Xiang 
21447e4937aSGao Xiang 	if (err == -ENOENT) {
21547e4937aSGao Xiang 		/* negative dentry */
21647e4937aSGao Xiang 		inode = NULL;
2178d8a09b0SGao Xiang 	} else if (err) {
21847e4937aSGao Xiang 		inode = ERR_PTR(err);
21947e4937aSGao Xiang 	} else {
220181b150fSAl Viro 		erofs_dbg("%s, %pd (nid %llu) found, d_type %u", __func__,
221181b150fSAl Viro 			  dentry, nid, d_type);
22247e4937aSGao Xiang 		inode = erofs_iget(dir->i_sb, nid, d_type == FT_DIR);
22347e4937aSGao Xiang 	}
22447e4937aSGao Xiang 	return d_splice_alias(inode, dentry);
22547e4937aSGao Xiang }
22647e4937aSGao Xiang 
22747e4937aSGao Xiang const struct inode_operations erofs_dir_iops = {
22847e4937aSGao Xiang 	.lookup = erofs_lookup,
22947e4937aSGao Xiang 	.getattr = erofs_getattr,
23047e4937aSGao Xiang 	.listxattr = erofs_listxattr,
23147e4937aSGao Xiang 	.get_acl = erofs_get_acl,
232eadcd6b5SGao Xiang 	.fiemap = erofs_fiemap,
23347e4937aSGao Xiang };
234