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