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/ 547e4937aSGao Xiang */ 647e4937aSGao Xiang #include "xattr.h" 747e4937aSGao Xiang 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 8947e4937aSGao Xiang static struct page *find_target_block_classic(struct inode *dir, 9047e4937aSGao Xiang struct erofs_qstr *name, 9147e4937aSGao Xiang int *_ndirents) 9247e4937aSGao Xiang { 9347e4937aSGao Xiang unsigned int startprfx, endprfx; 9447e4937aSGao Xiang int head, back; 9547e4937aSGao Xiang struct address_space *const mapping = dir->i_mapping; 9647e4937aSGao Xiang struct page *candidate = ERR_PTR(-ENOENT); 9747e4937aSGao Xiang 9847e4937aSGao Xiang startprfx = endprfx = 0; 9947e4937aSGao Xiang head = 0; 10099634bf3SGao Xiang back = erofs_inode_datablocks(dir) - 1; 10147e4937aSGao Xiang 10247e4937aSGao Xiang while (head <= back) { 10347e4937aSGao Xiang const int mid = head + (back - head) / 2; 10447e4937aSGao Xiang struct page *page = read_mapping_page(mapping, mid, NULL); 10547e4937aSGao Xiang 10647e4937aSGao Xiang if (!IS_ERR(page)) { 10747e4937aSGao Xiang struct erofs_dirent *de = kmap_atomic(page); 10847e4937aSGao Xiang const int nameoff = nameoff_from_disk(de->nameoff, 10947e4937aSGao Xiang EROFS_BLKSIZ); 11047e4937aSGao Xiang const int ndirents = nameoff / sizeof(*de); 11147e4937aSGao Xiang int diff; 11247e4937aSGao Xiang unsigned int matched; 11347e4937aSGao Xiang struct erofs_qstr dname; 11447e4937aSGao Xiang 1158d8a09b0SGao Xiang if (!ndirents) { 11647e4937aSGao Xiang kunmap_atomic(de); 11747e4937aSGao Xiang put_page(page); 1184f761fa2SGao Xiang erofs_err(dir->i_sb, 1194f761fa2SGao Xiang "corrupted dir block %d @ nid %llu", 120a5876e24SGao 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 */ 13799634bf3SGao Xiang diff = erofs_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 { 236181b150fSAl Viro erofs_dbg("%s, %pd (nid %llu) found, d_type %u", __func__, 237181b150fSAl Viro dentry, 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 .listxattr = erofs_listxattr, 24747e4937aSGao Xiang .get_acl = erofs_get_acl, 248*eadcd6b5SGao Xiang .fiemap = erofs_fiemap, 24947e4937aSGao Xiang }; 250