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