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(f: 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(sb: inode->i_sb, block: 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, name: nameptr, namelen, ino: 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 | |