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