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 19953a7f996SGao 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); 222*312fe643SGao Xiang inode = erofs_iget(dir->i_sb, nid); 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