1 /*
2 * linux/fs/ext2/dir.c
3 *
4 * Copyright (C) 1992, 1993, 1994, 1995
5 * Remy Card (card@masi.ibp.fr)
6 * Laboratoire MASI - Institut Blaise Pascal
7 * Universite Pierre et Marie Curie (Paris VI)
8 *
9 * from
10 *
11 * linux/fs/minix/dir.c
12 *
13 * Copyright (C) 1991, 1992 Linus Torvalds
14 *
15 * ext2 directory handling functions
16 */
17
18 #include <asm/segment.h>
19
20 #include <linux/errno.h>
21 #include <linux/fs.h>
22 #include <linux/ext2_fs.h>
23 #include <linux/sched.h>
24 #include <linux/stat.h>
25
26 static int ext2_dir_read (struct inode * inode, struct file * filp,
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
27 char * buf, int count)
28 {
29 return -EISDIR;
30 }
31
32 static int ext2_readdir (struct inode *, struct file *, void *, filldir_t);
33
34 static struct file_operations ext2_dir_operations = {
35 NULL, /* lseek - default */
36 ext2_dir_read, /* read */
37 NULL, /* write - bad */
38 ext2_readdir, /* readdir */
39 NULL, /* select - default */
40 ext2_ioctl, /* ioctl */
41 NULL, /* mmap */
42 NULL, /* no special open code */
43 NULL, /* no special release code */
44 file_fsync, /* fsync */
45 NULL, /* fasync */
46 NULL, /* check_media_change */
47 NULL /* revalidate */
48 };
49
50 /*
51 * directories can handle most operations...
52 */
53 struct inode_operations ext2_dir_inode_operations = {
54 &ext2_dir_operations, /* default directory file-ops */
55 ext2_create, /* create */
56 ext2_lookup, /* lookup */
57 ext2_link, /* link */
58 ext2_unlink, /* unlink */
59 ext2_symlink, /* symlink */
60 ext2_mkdir, /* mkdir */
61 ext2_rmdir, /* rmdir */
62 ext2_mknod, /* mknod */
63 ext2_rename, /* rename */
64 NULL, /* readlink */
65 NULL, /* follow_link */
66 NULL, /* bmap */
67 ext2_truncate, /* truncate */
68 ext2_permission, /* permission */
69 NULL /* smap */
70 };
71
72 int ext2_check_dir_entry (const char * function, struct inode * dir,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
73 struct ext2_dir_entry * de, struct buffer_head * bh,
74 unsigned long offset)
75 {
76 const char * error_msg = NULL;
77
78 if (de->rec_len < EXT2_DIR_REC_LEN(1))
79 error_msg = "rec_len is smaller than minimal";
80 else if (de->rec_len % 4 != 0)
81 error_msg = "rec_len % 4 != 0";
82 else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
83 error_msg = "rec_len is too small for name_len";
84 else if (dir && ((char *) de - bh->b_data) + de->rec_len >
85 dir->i_sb->s_blocksize)
86 error_msg = "directory entry across blocks";
87 else if (dir && de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
88 error_msg = "inode out of bounds";
89
90 if (error_msg != NULL)
91 ext2_error (dir->i_sb, function, "bad entry in directory #%lu: %s - "
92 "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
93 dir->i_ino, error_msg, offset, (unsigned long) de->inode,
94 de->rec_len, de->name_len);
95 return error_msg == NULL ? 1 : 0;
96 }
97
98 static int ext2_readdir (struct inode * inode, struct file * filp,
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
99 void * dirent, filldir_t filldir)
100 {
101 int error = 0;
102 unsigned long offset, blk;
103 int i, num, stored;
104 struct buffer_head * bh, * tmp, * bha[16];
105 struct ext2_dir_entry * de;
106 struct super_block * sb;
107 int err;
108
109 if (!inode || !S_ISDIR(inode->i_mode))
110 return -EBADF;
111 sb = inode->i_sb;
112
113 stored = 0;
114 bh = NULL;
115 offset = filp->f_pos & (sb->s_blocksize - 1);
116
117 while (!error && !stored && filp->f_pos < inode->i_size) {
118 blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb);
119 bh = ext2_bread (inode, blk, 0, &err);
120 if (!bh) {
121 ext2_error (sb, "ext2_readdir",
122 "directory #%lu contains a hole at offset %lu",
123 inode->i_ino, (unsigned long)filp->f_pos);
124 filp->f_pos += sb->s_blocksize - offset;
125 continue;
126 }
127
128 /*
129 * Do the readahead
130 */
131 if (!offset) {
132 for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0;
133 i > 0; i--) {
134 tmp = ext2_getblk (inode, ++blk, 0, &err);
135 if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
136 bha[num++] = tmp;
137 else
138 brelse (tmp);
139 }
140 if (num) {
141 ll_rw_block (READA, num, bha);
142 for (i = 0; i < num; i++)
143 brelse (bha[i]);
144 }
145 }
146
147 revalidate:
148 /* If the dir block has changed since the last call to
149 * readdir(2), then we might be pointing to an invalid
150 * dirent right now. Scan from the start of the block
151 * to make sure. */
152 if (filp->f_version != inode->i_version) {
153 for (i = 0; i < sb->s_blocksize && i < offset; ) {
154 de = (struct ext2_dir_entry *)
155 (bh->b_data + i);
156 /* It's too expensive to do a full
157 * dirent test each time round this
158 * loop, but we do have to test at
159 * least that it is non-zero. A
160 * failure will be detected in the
161 * dirent test below. */
162 if (de->rec_len < EXT2_DIR_REC_LEN(1))
163 break;
164 i += de->rec_len;
165 }
166 offset = i;
167 filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
168 | offset;
169 filp->f_version = inode->i_version;
170 }
171
172 while (!error && filp->f_pos < inode->i_size
173 && offset < sb->s_blocksize) {
174 de = (struct ext2_dir_entry *) (bh->b_data + offset);
175 if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
176 bh, offset)) {
177 /* On error, skip the f_pos to the
178 next block. */
179 filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
180 + sb->s_blocksize;
181 brelse (bh);
182 return stored;
183 }
184 offset += de->rec_len;
185 if (de->inode) {
186 /* We might block in the next section
187 * if the data destination is
188 * currently swapped out. So, use a
189 * version stamp to detect whether or
190 * not the directory has been modified
191 * during the copy operation. */
192 unsigned long version;
193 dcache_add(inode, de->name, de->name_len, de->inode);
194 version = inode->i_version;
195 error = filldir(dirent, de->name, de->name_len, filp->f_pos, de->inode);
196 if (error)
197 break;
198 if (version != inode->i_version)
199 goto revalidate;
200 stored ++;
201 }
202 filp->f_pos += de->rec_len;
203 }
204 offset = 0;
205 brelse (bh);
206 }
207 if (!IS_RDONLY(inode)) {
208 inode->i_atime = CURRENT_TIME;
209 inode->i_dirt = 1;
210 }
211 return 0;
212 }