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