xref: /openbmc/linux/fs/erofs/namei.c (revision a5876e24f13f13483fbd602b972d35801fb80b74)
147e4937aSGao Xiang // SPDX-License-Identifier: GPL-2.0-only
247e4937aSGao Xiang /*
347e4937aSGao Xiang  * Copyright (C) 2017-2018 HUAWEI, Inc.
447e4937aSGao Xiang  *             http://www.huawei.com/
547e4937aSGao Xiang  * Created by Gao Xiang <gaoxiang25@huawei.com>
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' */
1747e4937aSGao Xiang static inline int 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 */
7447e4937aSGao Xiang 		int ret = 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 
9047e4937aSGao Xiang static struct page *find_target_block_classic(struct inode *dir,
9147e4937aSGao Xiang 					      struct erofs_qstr *name,
9247e4937aSGao Xiang 					      int *_ndirents)
9347e4937aSGao Xiang {
9447e4937aSGao Xiang 	unsigned int startprfx, endprfx;
9547e4937aSGao Xiang 	int head, back;
9647e4937aSGao Xiang 	struct address_space *const mapping = dir->i_mapping;
9747e4937aSGao Xiang 	struct page *candidate = ERR_PTR(-ENOENT);
9847e4937aSGao Xiang 
9947e4937aSGao Xiang 	startprfx = endprfx = 0;
10047e4937aSGao Xiang 	head = 0;
10147e4937aSGao Xiang 	back = inode_datablocks(dir) - 1;
10247e4937aSGao Xiang 
10347e4937aSGao Xiang 	while (head <= back) {
10447e4937aSGao Xiang 		const int mid = head + (back - head) / 2;
10547e4937aSGao Xiang 		struct page *page = read_mapping_page(mapping, mid, NULL);
10647e4937aSGao Xiang 
10747e4937aSGao Xiang 		if (!IS_ERR(page)) {
10847e4937aSGao Xiang 			struct erofs_dirent *de = kmap_atomic(page);
10947e4937aSGao Xiang 			const int nameoff = nameoff_from_disk(de->nameoff,
11047e4937aSGao Xiang 							      EROFS_BLKSIZ);
11147e4937aSGao Xiang 			const int ndirents = nameoff / sizeof(*de);
11247e4937aSGao Xiang 			int diff;
11347e4937aSGao Xiang 			unsigned int matched;
11447e4937aSGao Xiang 			struct erofs_qstr dname;
11547e4937aSGao Xiang 
1168d8a09b0SGao Xiang 			if (!ndirents) {
11747e4937aSGao Xiang 				kunmap_atomic(de);
11847e4937aSGao Xiang 				put_page(page);
11947e4937aSGao Xiang 				errln("corrupted dir block %d @ nid %llu",
120*a5876e24SGao Xiang 				      mid, EROFS_I(dir)->nid);
12147e4937aSGao Xiang 				DBG_BUGON(1);
12247e4937aSGao Xiang 				page = ERR_PTR(-EFSCORRUPTED);
12347e4937aSGao Xiang 				goto out;
12447e4937aSGao Xiang 			}
12547e4937aSGao Xiang 
12647e4937aSGao Xiang 			matched = min(startprfx, endprfx);
12747e4937aSGao Xiang 
12847e4937aSGao Xiang 			dname.name = (u8 *)de + nameoff;
12947e4937aSGao Xiang 			if (ndirents == 1)
13047e4937aSGao Xiang 				dname.end = (u8 *)de + EROFS_BLKSIZ;
13147e4937aSGao Xiang 			else
13247e4937aSGao Xiang 				dname.end = (u8 *)de +
13347e4937aSGao Xiang 					nameoff_from_disk(de[1].nameoff,
13447e4937aSGao Xiang 							  EROFS_BLKSIZ);
13547e4937aSGao Xiang 
13647e4937aSGao Xiang 			/* string comparison without already matched prefix */
13747e4937aSGao Xiang 			diff = dirnamecmp(name, &dname, &matched);
13847e4937aSGao Xiang 			kunmap_atomic(de);
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))
14847e4937aSGao Xiang 					put_page(candidate);
14947e4937aSGao Xiang 				candidate = page;
15047e4937aSGao Xiang 				*_ndirents = ndirents;
15147e4937aSGao Xiang 			} else {
15247e4937aSGao Xiang 				put_page(page);
15347e4937aSGao Xiang 
15447e4937aSGao Xiang 				back = mid - 1;
15547e4937aSGao Xiang 				endprfx = matched;
15647e4937aSGao Xiang 			}
15747e4937aSGao Xiang 			continue;
15847e4937aSGao Xiang 		}
15947e4937aSGao Xiang out:		/* free if the candidate is valid */
16047e4937aSGao Xiang 		if (!IS_ERR(candidate))
16147e4937aSGao Xiang 			put_page(candidate);
16247e4937aSGao Xiang 		return page;
16347e4937aSGao Xiang 	}
16447e4937aSGao Xiang 	return candidate;
16547e4937aSGao Xiang }
16647e4937aSGao Xiang 
16747e4937aSGao Xiang int erofs_namei(struct inode *dir,
16847e4937aSGao Xiang 		struct qstr *name,
16947e4937aSGao Xiang 		erofs_nid_t *nid, unsigned int *d_type)
17047e4937aSGao Xiang {
17147e4937aSGao Xiang 	int ndirents;
17247e4937aSGao Xiang 	struct page *page;
17347e4937aSGao Xiang 	void *data;
17447e4937aSGao Xiang 	struct erofs_dirent *de;
17547e4937aSGao Xiang 	struct erofs_qstr qn;
17647e4937aSGao Xiang 
1778d8a09b0SGao Xiang 	if (!dir->i_size)
17847e4937aSGao Xiang 		return -ENOENT;
17947e4937aSGao Xiang 
18047e4937aSGao Xiang 	qn.name = name->name;
18147e4937aSGao Xiang 	qn.end = name->name + name->len;
18247e4937aSGao Xiang 
18347e4937aSGao Xiang 	ndirents = 0;
18447e4937aSGao Xiang 	page = find_target_block_classic(dir, &qn, &ndirents);
18547e4937aSGao Xiang 
18647e4937aSGao Xiang 	if (IS_ERR(page))
18747e4937aSGao Xiang 		return PTR_ERR(page);
18847e4937aSGao Xiang 
18947e4937aSGao Xiang 	data = kmap_atomic(page);
19047e4937aSGao Xiang 	/* the target page has been mapped */
19147e4937aSGao Xiang 	if (ndirents)
19247e4937aSGao Xiang 		de = find_target_dirent(&qn, data, EROFS_BLKSIZ, ndirents);
19347e4937aSGao Xiang 	else
19447e4937aSGao Xiang 		de = (struct erofs_dirent *)data;
19547e4937aSGao Xiang 
19647e4937aSGao Xiang 	if (!IS_ERR(de)) {
19747e4937aSGao Xiang 		*nid = le64_to_cpu(de->nid);
19847e4937aSGao Xiang 		*d_type = de->file_type;
19947e4937aSGao Xiang 	}
20047e4937aSGao Xiang 
20147e4937aSGao Xiang 	kunmap_atomic(data);
20247e4937aSGao Xiang 	put_page(page);
20347e4937aSGao Xiang 
20447e4937aSGao Xiang 	return PTR_ERR_OR_ZERO(de);
20547e4937aSGao Xiang }
20647e4937aSGao Xiang 
20747e4937aSGao Xiang /* NOTE: i_mutex is already held by vfs */
20847e4937aSGao Xiang static struct dentry *erofs_lookup(struct inode *dir,
20947e4937aSGao Xiang 				   struct dentry *dentry,
21047e4937aSGao Xiang 				   unsigned int flags)
21147e4937aSGao Xiang {
21247e4937aSGao Xiang 	int err;
21347e4937aSGao Xiang 	erofs_nid_t nid;
21447e4937aSGao Xiang 	unsigned int d_type;
21547e4937aSGao Xiang 	struct inode *inode;
21647e4937aSGao Xiang 
21747e4937aSGao Xiang 	DBG_BUGON(!d_really_is_negative(dentry));
21847e4937aSGao Xiang 	/* dentry must be unhashed in lookup, no need to worry about */
21947e4937aSGao Xiang 	DBG_BUGON(!d_unhashed(dentry));
22047e4937aSGao Xiang 
22147e4937aSGao Xiang 	trace_erofs_lookup(dir, dentry, flags);
22247e4937aSGao Xiang 
22347e4937aSGao Xiang 	/* file name exceeds fs limit */
2248d8a09b0SGao Xiang 	if (dentry->d_name.len > EROFS_NAME_LEN)
22547e4937aSGao Xiang 		return ERR_PTR(-ENAMETOOLONG);
22647e4937aSGao Xiang 
22747e4937aSGao Xiang 	/* false uninitialized warnings on gcc 4.8.x */
22847e4937aSGao Xiang 	err = erofs_namei(dir, &dentry->d_name, &nid, &d_type);
22947e4937aSGao Xiang 
23047e4937aSGao Xiang 	if (err == -ENOENT) {
23147e4937aSGao Xiang 		/* negative dentry */
23247e4937aSGao Xiang 		inode = NULL;
2338d8a09b0SGao Xiang 	} else if (err) {
23447e4937aSGao Xiang 		inode = ERR_PTR(err);
23547e4937aSGao Xiang 	} else {
23647e4937aSGao Xiang 		debugln("%s, %s (nid %llu) found, d_type %u", __func__,
23747e4937aSGao Xiang 			dentry->d_name.name, nid, d_type);
23847e4937aSGao Xiang 		inode = erofs_iget(dir->i_sb, nid, d_type == FT_DIR);
23947e4937aSGao Xiang 	}
24047e4937aSGao Xiang 	return d_splice_alias(inode, dentry);
24147e4937aSGao Xiang }
24247e4937aSGao Xiang 
24347e4937aSGao Xiang const struct inode_operations erofs_dir_iops = {
24447e4937aSGao Xiang 	.lookup = erofs_lookup,
24547e4937aSGao Xiang 	.getattr = erofs_getattr,
24647e4937aSGao Xiang #ifdef CONFIG_EROFS_FS_XATTR
24747e4937aSGao Xiang 	.listxattr = erofs_listxattr,
24847e4937aSGao Xiang #endif
24947e4937aSGao Xiang 	.get_acl = erofs_get_acl,
25047e4937aSGao Xiang };
25147e4937aSGao Xiang 
252