1 /*
2 * linux/fs/affs/dir.c
3 *
4 * (c) 1996 Hans-Joachim Widmaier - Rewritten
5 *
6 * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
7 *
8 * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
9 *
10 * (C) 1991 Linus Torvalds - minix filesystem
11 *
12 * affs directory handling functions
13 *
14 */
15
16 #include <asm/uaccess.h>
17 #include <linux/errno.h>
18 #include <linux/fs.h>
19 #include <linux/kernel.h>
20 #include <linux/affs_fs.h>
21 #include <linux/stat.h>
22 #include <linux/string.h>
23 #include <linux/mm.h>
24 #include <linux/amigaffs.h>
25
26 static int affs_readdir(struct file *, void *, filldir_t);
27
28 struct file_operations affs_dir_operations = {
29 read: generic_read_dir,
30 readdir: affs_readdir,
31 fsync: file_fsync,
32 };
33
34 /*
35 * directories can handle most operations...
36 */
37 struct inode_operations affs_dir_inode_operations = {
38 create: affs_create,
39 lookup: affs_lookup,
40 link: affs_link,
41 unlink: affs_unlink,
42 symlink: affs_symlink,
43 mkdir: affs_mkdir,
44 rmdir: affs_rmdir,
45 rename: affs_rename,
46 setattr: affs_notify_change,
47 };
48
49 static int
affs_readdir(struct file * filp,void * dirent,filldir_t filldir)50 affs_readdir(struct file *filp, void *dirent, filldir_t filldir)
51 {
52 struct inode *inode = filp->f_dentry->d_inode;
53 struct super_block *sb = inode->i_sb;
54 struct buffer_head *dir_bh;
55 struct buffer_head *fh_bh;
56 unsigned char *name;
57 int namelen;
58 u32 i;
59 int hash_pos;
60 int chain_pos;
61 u32 f_pos;
62 u32 ino;
63 int stored;
64 int res;
65
66 pr_debug("AFFS: readdir(ino=%lu,f_pos=%lx)\n",inode->i_ino,(unsigned long)filp->f_pos);
67
68 stored = 0;
69 res = -EIO;
70 dir_bh = NULL;
71 fh_bh = NULL;
72 f_pos = filp->f_pos;
73
74 if (f_pos == 0) {
75 filp->private_data = (void *)0;
76 if (filldir(dirent, ".", 1, f_pos, inode->i_ino, DT_DIR) < 0)
77 return 0;
78 filp->f_pos = f_pos = 1;
79 stored++;
80 }
81 if (f_pos == 1) {
82 if (filldir(dirent, "..", 2, f_pos, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
83 return stored;
84 filp->f_pos = f_pos = 2;
85 stored++;
86 }
87
88 affs_lock_dir(inode);
89 chain_pos = (f_pos - 2) & 0xffff;
90 hash_pos = (f_pos - 2) >> 16;
91 if (chain_pos == 0xffff) {
92 affs_warning(sb, "readdir", "More than 65535 entries in chain");
93 chain_pos = 0;
94 hash_pos++;
95 filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
96 }
97 dir_bh = affs_bread(sb, inode->i_ino);
98 if (!dir_bh)
99 goto readdir_out;
100
101 /* If the directory hasn't changed since the last call to readdir(),
102 * we can jump directly to where we left off.
103 */
104 ino = (u32)(long)filp->private_data;
105 if (ino && filp->f_version == inode->i_version) {
106 pr_debug("AFFS: readdir() left off=%d\n", ino);
107 goto inside;
108 }
109
110 ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
111 for (i = 0; ino && i < chain_pos; i++) {
112 fh_bh = affs_bread(sb, ino);
113 if (!fh_bh) {
114 affs_error(sb, "readdir","Cannot read block %d", i);
115 goto readdir_out;
116 }
117 ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
118 affs_brelse(fh_bh);
119 fh_bh = NULL;
120 }
121 if (ino)
122 goto inside;
123 hash_pos++;
124
125 for (; hash_pos < AFFS_SB->s_hashsize; hash_pos++) {
126 ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
127 if (!ino)
128 continue;
129 f_pos = (hash_pos << 16) + 2;
130 inside:
131 do {
132 fh_bh = affs_bread(sb, ino);
133 if (!fh_bh) {
134 affs_error(sb, "readdir","Cannot read block %d", ino);
135 goto readdir_done;
136 }
137
138 namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30);
139 name = AFFS_TAIL(sb, fh_bh)->name + 1;
140 pr_debug("AFFS: readdir(): filldir(\"%.*s\", ino=%u), hash=%d, f_pos=%x\n",
141 namelen, name, ino, hash_pos, f_pos);
142 if (filldir(dirent, name, namelen, f_pos, ino, DT_UNKNOWN) < 0)
143 goto readdir_done;
144 stored++;
145 f_pos++;
146 ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
147 affs_brelse(fh_bh);
148 fh_bh = NULL;
149 } while (ino);
150 }
151 readdir_done:
152 filp->f_pos = f_pos;
153 filp->f_version = inode->i_version;
154 filp->private_data = (void *)(long)ino;
155 res = stored;
156
157 readdir_out:
158 affs_brelse(dir_bh);
159 affs_brelse(fh_bh);
160 affs_unlock_dir(inode);
161 pr_debug("AFFS: readdir()=%d\n", stored);
162 return res;
163 }
164