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' */ 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 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 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 102*eb2c5e41SGao Xiang buf.inode = dir; 103*eb2c5e41SGao 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 1338d8a09b0SGao Xiang if (!diff) { 13447e4937aSGao Xiang *_ndirents = 0; 13547e4937aSGao Xiang goto out; 13647e4937aSGao Xiang } else if (diff > 0) { 13747e4937aSGao Xiang head = mid + 1; 13847e4937aSGao Xiang startprfx = matched; 13947e4937aSGao Xiang 14047e4937aSGao Xiang if (!IS_ERR(candidate)) 141500edd09SGao Xiang erofs_put_metabuf(target); 142500edd09SGao Xiang *target = buf; 143500edd09SGao Xiang candidate = de; 14447e4937aSGao Xiang *_ndirents = ndirents; 14547e4937aSGao Xiang } else { 146500edd09SGao Xiang erofs_put_metabuf(&buf); 14747e4937aSGao Xiang 14847e4937aSGao Xiang back = mid - 1; 14947e4937aSGao Xiang endprfx = matched; 15047e4937aSGao Xiang } 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 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; 174*eb2c5e41SGao 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 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 20847e4937aSGao Xiang if (err == -ENOENT) { 20947e4937aSGao Xiang /* negative dentry */ 21047e4937aSGao Xiang inode = NULL; 2118d8a09b0SGao Xiang } else if (err) { 21247e4937aSGao Xiang inode = ERR_PTR(err); 21347e4937aSGao Xiang } else { 214181b150fSAl Viro erofs_dbg("%s, %pd (nid %llu) found, d_type %u", __func__, 215181b150fSAl Viro dentry, nid, d_type); 216312fe643SGao Xiang inode = erofs_iget(dir->i_sb, nid); 21747e4937aSGao Xiang } 21847e4937aSGao Xiang return d_splice_alias(inode, dentry); 21947e4937aSGao Xiang } 22047e4937aSGao Xiang 22147e4937aSGao Xiang const struct inode_operations erofs_dir_iops = { 22247e4937aSGao Xiang .lookup = erofs_lookup, 22347e4937aSGao Xiang .getattr = erofs_getattr, 22447e4937aSGao Xiang .listxattr = erofs_listxattr, 225cac2f8b8SChristian Brauner .get_inode_acl = erofs_get_acl, 226eadcd6b5SGao Xiang .fiemap = erofs_fiemap, 22747e4937aSGao Xiang }; 228