1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * dir.c 4 * 5 * Copyright (c) 1999 Al Smith 6 */ 7 8 #include <linux/buffer_head.h> 9 #include "efs.h" 10 11 static int efs_readdir(struct file *, struct dir_context *); 12 13 const struct file_operations efs_dir_operations = { 14 .llseek = generic_file_llseek, 15 .read = generic_read_dir, 16 .iterate_shared = efs_readdir, 17 }; 18 19 const struct inode_operations efs_dir_inode_operations = { 20 .lookup = efs_lookup, 21 }; 22 23 static int efs_readdir(struct file *file, struct dir_context *ctx) 24 { 25 struct inode *inode = file_inode(file); 26 efs_block_t block; 27 int slot; 28 29 if (inode->i_size & (EFS_DIRBSIZE-1)) 30 pr_warn("%s(): directory size not a multiple of EFS_DIRBSIZE\n", 31 __func__); 32 33 /* work out where this entry can be found */ 34 block = ctx->pos >> EFS_DIRBSIZE_BITS; 35 36 /* each block contains at most 256 slots */ 37 slot = ctx->pos & 0xff; 38 39 /* look at all blocks */ 40 while (block < inode->i_blocks) { 41 struct efs_dir *dirblock; 42 struct buffer_head *bh; 43 44 /* read the dir block */ 45 bh = sb_bread(inode->i_sb, efs_bmap(inode, block)); 46 47 if (!bh) { 48 pr_err("%s(): failed to read dir block %d\n", 49 __func__, block); 50 break; 51 } 52 53 dirblock = (struct efs_dir *) bh->b_data; 54 55 if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) { 56 pr_err("%s(): invalid directory block\n", __func__); 57 brelse(bh); 58 break; 59 } 60 61 for (; slot < dirblock->slots; slot++) { 62 struct efs_dentry *dirslot; 63 efs_ino_t inodenum; 64 const char *nameptr; 65 int namelen; 66 67 if (dirblock->space[slot] == 0) 68 continue; 69 70 dirslot = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot)); 71 72 inodenum = be32_to_cpu(dirslot->inode); 73 namelen = dirslot->namelen; 74 nameptr = dirslot->name; 75 pr_debug("%s(): block %d slot %d/%d: inode %u, name \"%s\", namelen %u\n", 76 __func__, block, slot, dirblock->slots-1, 77 inodenum, nameptr, namelen); 78 if (!namelen) 79 continue; 80 /* found the next entry */ 81 ctx->pos = (block << EFS_DIRBSIZE_BITS) | slot; 82 83 /* sanity check */ 84 if (nameptr - (char *) dirblock + namelen > EFS_DIRBSIZE) { 85 pr_warn("directory entry %d exceeds directory block\n", 86 slot); 87 continue; 88 } 89 90 /* copy filename and data in dirslot */ 91 if (!dir_emit(ctx, nameptr, namelen, inodenum, DT_UNKNOWN)) { 92 brelse(bh); 93 return 0; 94 } 95 } 96 brelse(bh); 97 98 slot = 0; 99 block++; 100 } 101 ctx->pos = (block << EFS_DIRBSIZE_BITS) | slot; 102 return 0; 103 } 104