1*47e4937aSGao Xiang // SPDX-License-Identifier: GPL-2.0-only 2*47e4937aSGao Xiang /* 3*47e4937aSGao Xiang * Copyright (C) 2017-2018 HUAWEI, Inc. 4*47e4937aSGao Xiang * http://www.huawei.com/ 5*47e4937aSGao Xiang * Created by Gao Xiang <gaoxiang25@huawei.com> 6*47e4937aSGao Xiang */ 7*47e4937aSGao Xiang #include "xattr.h" 8*47e4937aSGao Xiang 9*47e4937aSGao Xiang #include <trace/events/erofs.h> 10*47e4937aSGao Xiang 11*47e4937aSGao Xiang struct erofs_qstr { 12*47e4937aSGao Xiang const unsigned char *name; 13*47e4937aSGao Xiang const unsigned char *end; 14*47e4937aSGao Xiang }; 15*47e4937aSGao Xiang 16*47e4937aSGao Xiang /* based on the end of qn is accurate and it must have the trailing '\0' */ 17*47e4937aSGao Xiang static inline int dirnamecmp(const struct erofs_qstr *qn, 18*47e4937aSGao Xiang const struct erofs_qstr *qd, 19*47e4937aSGao Xiang unsigned int *matched) 20*47e4937aSGao Xiang { 21*47e4937aSGao Xiang unsigned int i = *matched; 22*47e4937aSGao Xiang 23*47e4937aSGao Xiang /* 24*47e4937aSGao Xiang * on-disk error, let's only BUG_ON in the debugging mode. 25*47e4937aSGao Xiang * otherwise, it will return 1 to just skip the invalid name 26*47e4937aSGao Xiang * and go on (in consideration of the lookup performance). 27*47e4937aSGao Xiang */ 28*47e4937aSGao Xiang DBG_BUGON(qd->name > qd->end); 29*47e4937aSGao Xiang 30*47e4937aSGao Xiang /* qd could not have trailing '\0' */ 31*47e4937aSGao Xiang /* However it is absolutely safe if < qd->end */ 32*47e4937aSGao Xiang while (qd->name + i < qd->end && qd->name[i] != '\0') { 33*47e4937aSGao Xiang if (qn->name[i] != qd->name[i]) { 34*47e4937aSGao Xiang *matched = i; 35*47e4937aSGao Xiang return qn->name[i] > qd->name[i] ? 1 : -1; 36*47e4937aSGao Xiang } 37*47e4937aSGao Xiang ++i; 38*47e4937aSGao Xiang } 39*47e4937aSGao Xiang *matched = i; 40*47e4937aSGao Xiang /* See comments in __d_alloc on the terminating NUL character */ 41*47e4937aSGao Xiang return qn->name[i] == '\0' ? 0 : 1; 42*47e4937aSGao Xiang } 43*47e4937aSGao Xiang 44*47e4937aSGao Xiang #define nameoff_from_disk(off, sz) (le16_to_cpu(off) & ((sz) - 1)) 45*47e4937aSGao Xiang 46*47e4937aSGao Xiang static struct erofs_dirent *find_target_dirent(struct erofs_qstr *name, 47*47e4937aSGao Xiang u8 *data, 48*47e4937aSGao Xiang unsigned int dirblksize, 49*47e4937aSGao Xiang const int ndirents) 50*47e4937aSGao Xiang { 51*47e4937aSGao Xiang int head, back; 52*47e4937aSGao Xiang unsigned int startprfx, endprfx; 53*47e4937aSGao Xiang struct erofs_dirent *const de = (struct erofs_dirent *)data; 54*47e4937aSGao Xiang 55*47e4937aSGao Xiang /* since the 1st dirent has been evaluated previously */ 56*47e4937aSGao Xiang head = 1; 57*47e4937aSGao Xiang back = ndirents - 1; 58*47e4937aSGao Xiang startprfx = endprfx = 0; 59*47e4937aSGao Xiang 60*47e4937aSGao Xiang while (head <= back) { 61*47e4937aSGao Xiang const int mid = head + (back - head) / 2; 62*47e4937aSGao Xiang const int nameoff = nameoff_from_disk(de[mid].nameoff, 63*47e4937aSGao Xiang dirblksize); 64*47e4937aSGao Xiang unsigned int matched = min(startprfx, endprfx); 65*47e4937aSGao Xiang struct erofs_qstr dname = { 66*47e4937aSGao Xiang .name = data + nameoff, 67*47e4937aSGao Xiang .end = unlikely(mid >= ndirents - 1) ? 68*47e4937aSGao Xiang data + dirblksize : 69*47e4937aSGao Xiang data + nameoff_from_disk(de[mid + 1].nameoff, 70*47e4937aSGao Xiang dirblksize) 71*47e4937aSGao Xiang }; 72*47e4937aSGao Xiang 73*47e4937aSGao Xiang /* string comparison without already matched prefix */ 74*47e4937aSGao Xiang int ret = dirnamecmp(name, &dname, &matched); 75*47e4937aSGao Xiang 76*47e4937aSGao Xiang if (unlikely(!ret)) { 77*47e4937aSGao Xiang return de + mid; 78*47e4937aSGao Xiang } else if (ret > 0) { 79*47e4937aSGao Xiang head = mid + 1; 80*47e4937aSGao Xiang startprfx = matched; 81*47e4937aSGao Xiang } else { 82*47e4937aSGao Xiang back = mid - 1; 83*47e4937aSGao Xiang endprfx = matched; 84*47e4937aSGao Xiang } 85*47e4937aSGao Xiang } 86*47e4937aSGao Xiang 87*47e4937aSGao Xiang return ERR_PTR(-ENOENT); 88*47e4937aSGao Xiang } 89*47e4937aSGao Xiang 90*47e4937aSGao Xiang static struct page *find_target_block_classic(struct inode *dir, 91*47e4937aSGao Xiang struct erofs_qstr *name, 92*47e4937aSGao Xiang int *_ndirents) 93*47e4937aSGao Xiang { 94*47e4937aSGao Xiang unsigned int startprfx, endprfx; 95*47e4937aSGao Xiang int head, back; 96*47e4937aSGao Xiang struct address_space *const mapping = dir->i_mapping; 97*47e4937aSGao Xiang struct page *candidate = ERR_PTR(-ENOENT); 98*47e4937aSGao Xiang 99*47e4937aSGao Xiang startprfx = endprfx = 0; 100*47e4937aSGao Xiang head = 0; 101*47e4937aSGao Xiang back = inode_datablocks(dir) - 1; 102*47e4937aSGao Xiang 103*47e4937aSGao Xiang while (head <= back) { 104*47e4937aSGao Xiang const int mid = head + (back - head) / 2; 105*47e4937aSGao Xiang struct page *page = read_mapping_page(mapping, mid, NULL); 106*47e4937aSGao Xiang 107*47e4937aSGao Xiang if (!IS_ERR(page)) { 108*47e4937aSGao Xiang struct erofs_dirent *de = kmap_atomic(page); 109*47e4937aSGao Xiang const int nameoff = nameoff_from_disk(de->nameoff, 110*47e4937aSGao Xiang EROFS_BLKSIZ); 111*47e4937aSGao Xiang const int ndirents = nameoff / sizeof(*de); 112*47e4937aSGao Xiang int diff; 113*47e4937aSGao Xiang unsigned int matched; 114*47e4937aSGao Xiang struct erofs_qstr dname; 115*47e4937aSGao Xiang 116*47e4937aSGao Xiang if (unlikely(!ndirents)) { 117*47e4937aSGao Xiang kunmap_atomic(de); 118*47e4937aSGao Xiang put_page(page); 119*47e4937aSGao Xiang errln("corrupted dir block %d @ nid %llu", 120*47e4937aSGao Xiang mid, EROFS_V(dir)->nid); 121*47e4937aSGao Xiang DBG_BUGON(1); 122*47e4937aSGao Xiang page = ERR_PTR(-EFSCORRUPTED); 123*47e4937aSGao Xiang goto out; 124*47e4937aSGao Xiang } 125*47e4937aSGao Xiang 126*47e4937aSGao Xiang matched = min(startprfx, endprfx); 127*47e4937aSGao Xiang 128*47e4937aSGao Xiang dname.name = (u8 *)de + nameoff; 129*47e4937aSGao Xiang if (ndirents == 1) 130*47e4937aSGao Xiang dname.end = (u8 *)de + EROFS_BLKSIZ; 131*47e4937aSGao Xiang else 132*47e4937aSGao Xiang dname.end = (u8 *)de + 133*47e4937aSGao Xiang nameoff_from_disk(de[1].nameoff, 134*47e4937aSGao Xiang EROFS_BLKSIZ); 135*47e4937aSGao Xiang 136*47e4937aSGao Xiang /* string comparison without already matched prefix */ 137*47e4937aSGao Xiang diff = dirnamecmp(name, &dname, &matched); 138*47e4937aSGao Xiang kunmap_atomic(de); 139*47e4937aSGao Xiang 140*47e4937aSGao Xiang if (unlikely(!diff)) { 141*47e4937aSGao Xiang *_ndirents = 0; 142*47e4937aSGao Xiang goto out; 143*47e4937aSGao Xiang } else if (diff > 0) { 144*47e4937aSGao Xiang head = mid + 1; 145*47e4937aSGao Xiang startprfx = matched; 146*47e4937aSGao Xiang 147*47e4937aSGao Xiang if (!IS_ERR(candidate)) 148*47e4937aSGao Xiang put_page(candidate); 149*47e4937aSGao Xiang candidate = page; 150*47e4937aSGao Xiang *_ndirents = ndirents; 151*47e4937aSGao Xiang } else { 152*47e4937aSGao Xiang put_page(page); 153*47e4937aSGao Xiang 154*47e4937aSGao Xiang back = mid - 1; 155*47e4937aSGao Xiang endprfx = matched; 156*47e4937aSGao Xiang } 157*47e4937aSGao Xiang continue; 158*47e4937aSGao Xiang } 159*47e4937aSGao Xiang out: /* free if the candidate is valid */ 160*47e4937aSGao Xiang if (!IS_ERR(candidate)) 161*47e4937aSGao Xiang put_page(candidate); 162*47e4937aSGao Xiang return page; 163*47e4937aSGao Xiang } 164*47e4937aSGao Xiang return candidate; 165*47e4937aSGao Xiang } 166*47e4937aSGao Xiang 167*47e4937aSGao Xiang int erofs_namei(struct inode *dir, 168*47e4937aSGao Xiang struct qstr *name, 169*47e4937aSGao Xiang erofs_nid_t *nid, unsigned int *d_type) 170*47e4937aSGao Xiang { 171*47e4937aSGao Xiang int ndirents; 172*47e4937aSGao Xiang struct page *page; 173*47e4937aSGao Xiang void *data; 174*47e4937aSGao Xiang struct erofs_dirent *de; 175*47e4937aSGao Xiang struct erofs_qstr qn; 176*47e4937aSGao Xiang 177*47e4937aSGao Xiang if (unlikely(!dir->i_size)) 178*47e4937aSGao Xiang return -ENOENT; 179*47e4937aSGao Xiang 180*47e4937aSGao Xiang qn.name = name->name; 181*47e4937aSGao Xiang qn.end = name->name + name->len; 182*47e4937aSGao Xiang 183*47e4937aSGao Xiang ndirents = 0; 184*47e4937aSGao Xiang page = find_target_block_classic(dir, &qn, &ndirents); 185*47e4937aSGao Xiang 186*47e4937aSGao Xiang if (IS_ERR(page)) 187*47e4937aSGao Xiang return PTR_ERR(page); 188*47e4937aSGao Xiang 189*47e4937aSGao Xiang data = kmap_atomic(page); 190*47e4937aSGao Xiang /* the target page has been mapped */ 191*47e4937aSGao Xiang if (ndirents) 192*47e4937aSGao Xiang de = find_target_dirent(&qn, data, EROFS_BLKSIZ, ndirents); 193*47e4937aSGao Xiang else 194*47e4937aSGao Xiang de = (struct erofs_dirent *)data; 195*47e4937aSGao Xiang 196*47e4937aSGao Xiang if (!IS_ERR(de)) { 197*47e4937aSGao Xiang *nid = le64_to_cpu(de->nid); 198*47e4937aSGao Xiang *d_type = de->file_type; 199*47e4937aSGao Xiang } 200*47e4937aSGao Xiang 201*47e4937aSGao Xiang kunmap_atomic(data); 202*47e4937aSGao Xiang put_page(page); 203*47e4937aSGao Xiang 204*47e4937aSGao Xiang return PTR_ERR_OR_ZERO(de); 205*47e4937aSGao Xiang } 206*47e4937aSGao Xiang 207*47e4937aSGao Xiang /* NOTE: i_mutex is already held by vfs */ 208*47e4937aSGao Xiang static struct dentry *erofs_lookup(struct inode *dir, 209*47e4937aSGao Xiang struct dentry *dentry, 210*47e4937aSGao Xiang unsigned int flags) 211*47e4937aSGao Xiang { 212*47e4937aSGao Xiang int err; 213*47e4937aSGao Xiang erofs_nid_t nid; 214*47e4937aSGao Xiang unsigned int d_type; 215*47e4937aSGao Xiang struct inode *inode; 216*47e4937aSGao Xiang 217*47e4937aSGao Xiang DBG_BUGON(!d_really_is_negative(dentry)); 218*47e4937aSGao Xiang /* dentry must be unhashed in lookup, no need to worry about */ 219*47e4937aSGao Xiang DBG_BUGON(!d_unhashed(dentry)); 220*47e4937aSGao Xiang 221*47e4937aSGao Xiang trace_erofs_lookup(dir, dentry, flags); 222*47e4937aSGao Xiang 223*47e4937aSGao Xiang /* file name exceeds fs limit */ 224*47e4937aSGao Xiang if (unlikely(dentry->d_name.len > EROFS_NAME_LEN)) 225*47e4937aSGao Xiang return ERR_PTR(-ENAMETOOLONG); 226*47e4937aSGao Xiang 227*47e4937aSGao Xiang /* false uninitialized warnings on gcc 4.8.x */ 228*47e4937aSGao Xiang err = erofs_namei(dir, &dentry->d_name, &nid, &d_type); 229*47e4937aSGao Xiang 230*47e4937aSGao Xiang if (err == -ENOENT) { 231*47e4937aSGao Xiang /* negative dentry */ 232*47e4937aSGao Xiang inode = NULL; 233*47e4937aSGao Xiang } else if (unlikely(err)) { 234*47e4937aSGao Xiang inode = ERR_PTR(err); 235*47e4937aSGao Xiang } else { 236*47e4937aSGao Xiang debugln("%s, %s (nid %llu) found, d_type %u", __func__, 237*47e4937aSGao Xiang dentry->d_name.name, nid, d_type); 238*47e4937aSGao Xiang inode = erofs_iget(dir->i_sb, nid, d_type == FT_DIR); 239*47e4937aSGao Xiang } 240*47e4937aSGao Xiang return d_splice_alias(inode, dentry); 241*47e4937aSGao Xiang } 242*47e4937aSGao Xiang 243*47e4937aSGao Xiang const struct inode_operations erofs_dir_iops = { 244*47e4937aSGao Xiang .lookup = erofs_lookup, 245*47e4937aSGao Xiang .getattr = erofs_getattr, 246*47e4937aSGao Xiang #ifdef CONFIG_EROFS_FS_XATTR 247*47e4937aSGao Xiang .listxattr = erofs_listxattr, 248*47e4937aSGao Xiang #endif 249*47e4937aSGao Xiang .get_acl = erofs_get_acl, 250*47e4937aSGao Xiang }; 251*47e4937aSGao Xiang 252