1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2017-2018 HUAWEI, Inc. 4 * https://www.huawei.com/ 5 * Copyright (C) 2022, Alibaba Cloud 6 */ 7 #include "internal.h" 8 #include <linux/filelock.h> 9 10 static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx, 11 void *dentry_blk, struct erofs_dirent *de, 12 unsigned int nameoff0, unsigned int maxsize) 13 { 14 const struct erofs_dirent *end = dentry_blk + nameoff0; 15 16 while (de < end) { 17 unsigned char d_type = fs_ftype_to_dtype(de->file_type); 18 unsigned int nameoff = le16_to_cpu(de->nameoff); 19 const char *de_name = (char *)dentry_blk + nameoff; 20 unsigned int de_namelen; 21 22 /* non-trailing dirent in the directory block? */ 23 if (de + 1 < end) 24 de_namelen = le16_to_cpu(de[1].nameoff) - nameoff; 25 else if (maxsize <= nameoff) 26 goto err_bogus; 27 else 28 de_namelen = strnlen(de_name, maxsize - nameoff); 29 30 /* a corrupted entry is found (including negative namelen) */ 31 if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) || 32 nameoff + de_namelen > maxsize) 33 goto err_bogus; 34 35 if (!dir_emit(ctx, de_name, de_namelen, 36 erofs_nid_to_ino64(EROFS_SB(dir->i_sb), 37 le64_to_cpu(de->nid)), d_type)) 38 return 1; 39 ++de; 40 ctx->pos += sizeof(struct erofs_dirent); 41 } 42 return 0; 43 err_bogus: 44 erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid); 45 DBG_BUGON(1); 46 return -EFSCORRUPTED; 47 } 48 49 static int erofs_readdir(struct file *f, struct dir_context *ctx) 50 { 51 struct inode *dir = file_inode(f); 52 struct erofs_buf buf = __EROFS_BUF_INITIALIZER; 53 struct super_block *sb = dir->i_sb; 54 struct file_ra_state *ra = &f->f_ra; 55 unsigned long bsz = sb->s_blocksize; 56 unsigned int ofs = erofs_blkoff(sb, ctx->pos); 57 pgoff_t ra_pages = DIV_ROUND_UP_POW2( 58 EROFS_I_SB(dir)->dir_ra_bytes, PAGE_SIZE); 59 pgoff_t nr_pages = DIV_ROUND_UP_POW2(dir->i_size, PAGE_SIZE); 60 int err = 0; 61 bool initial = true; 62 63 buf.mapping = dir->i_mapping; 64 while (ctx->pos < dir->i_size) { 65 erofs_off_t dbstart = ctx->pos - ofs; 66 struct erofs_dirent *de; 67 unsigned int nameoff, maxsize; 68 69 if (fatal_signal_pending(current)) { 70 err = -ERESTARTSYS; 71 break; 72 } 73 74 /* readahead blocks to enhance performance for large directories */ 75 if (ra_pages) { 76 pgoff_t idx = DIV_ROUND_UP_POW2(ctx->pos, PAGE_SIZE); 77 pgoff_t pages = min(nr_pages - idx, ra_pages); 78 79 if (pages > 1 && !ra_has_index(ra, idx)) 80 page_cache_sync_readahead(dir->i_mapping, ra, 81 f, idx, pages); 82 } 83 84 de = erofs_bread(&buf, dbstart, true); 85 if (IS_ERR(de)) { 86 erofs_err(sb, "failed to readdir of logical block %llu of nid %llu", 87 erofs_blknr(sb, dbstart), EROFS_I(dir)->nid); 88 err = PTR_ERR(de); 89 break; 90 } 91 92 nameoff = le16_to_cpu(de->nameoff); 93 if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) { 94 erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu", 95 nameoff, EROFS_I(dir)->nid); 96 err = -EFSCORRUPTED; 97 break; 98 } 99 100 maxsize = min_t(unsigned int, dir->i_size - dbstart, bsz); 101 /* search dirents at the arbitrary position */ 102 if (initial) { 103 initial = false; 104 ofs = roundup(ofs, sizeof(struct erofs_dirent)); 105 ctx->pos = dbstart + ofs; 106 } 107 108 err = erofs_fill_dentries(dir, ctx, de, (void *)de + ofs, 109 nameoff, maxsize); 110 if (err) 111 break; 112 ctx->pos = dbstart + maxsize; 113 ofs = 0; 114 cond_resched(); 115 } 116 erofs_put_metabuf(&buf); 117 if (EROFS_I(dir)->dot_omitted && ctx->pos == dir->i_size) { 118 if (!dir_emit_dot(f, ctx)) 119 return 0; 120 ++ctx->pos; 121 } 122 return err < 0 ? err : 0; 123 } 124 125 const struct file_operations erofs_dir_fops = { 126 .llseek = generic_file_llseek, 127 .read = generic_read_dir, 128 .iterate_shared = erofs_readdir, 129 .unlocked_ioctl = erofs_ioctl, 130 #ifdef CONFIG_COMPAT 131 .compat_ioctl = erofs_compat_ioctl, 132 #endif 133 .setlease = generic_setlease, 134 }; 135