]>
Commit | Line | Data |
---|---|---|
21c84b71 TT |
1 | /* |
2 | * dir_iterate.c --- ext2fs directory iteration operations | |
efc6f628 | 3 | * |
21c84b71 TT |
4 | * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. |
5 | * | |
6 | * %Begin-Header% | |
543547a5 TT |
7 | * This file may be redistributed under the terms of the GNU Library |
8 | * General Public License, version 2. | |
21c84b71 TT |
9 | * %End-Header% |
10 | */ | |
11 | ||
d1154eb4 | 12 | #include "config.h" |
21c84b71 TT |
13 | #include <stdio.h> |
14 | #include <string.h> | |
4cbe8af4 | 15 | #if HAVE_UNISTD_H |
21c84b71 | 16 | #include <unistd.h> |
4cbe8af4 | 17 | #endif |
21c84b71 TT |
18 | #if HAVE_ERRNO_H |
19 | #include <errno.h> | |
20 | #endif | |
21 | ||
b5abe6fa | 22 | #include "ext2_fs.h" |
21c84b71 TT |
23 | #include "ext2fsP.h" |
24 | ||
8a480350 TT |
25 | #define EXT4_MAX_REC_LEN ((1<<16)-1) |
26 | ||
27 | errcode_t ext2fs_get_rec_len(ext2_filsys fs, | |
28 | struct ext2_dir_entry *dirent, | |
29 | unsigned int *rec_len) | |
30 | { | |
31 | unsigned int len = dirent->rec_len; | |
32 | ||
a4fdf094 TT |
33 | if (fs->blocksize < 65536) |
34 | *rec_len = len; | |
35 | else if (len == EXT4_MAX_REC_LEN || len == 0) | |
8a480350 TT |
36 | *rec_len = fs->blocksize; |
37 | else | |
38 | *rec_len = (len & 65532) | ((len & 3) << 16); | |
39 | return 0; | |
40 | } | |
41 | ||
42 | errcode_t ext2fs_set_rec_len(ext2_filsys fs, | |
43 | unsigned int len, | |
44 | struct ext2_dir_entry *dirent) | |
45 | { | |
46 | if ((len > fs->blocksize) || (fs->blocksize > (1 << 18)) || (len & 3)) | |
47 | return EINVAL; | |
48 | if (len < 65536) { | |
49 | dirent->rec_len = len; | |
50 | return 0; | |
51 | } | |
52 | if (len == fs->blocksize) { | |
53 | if (fs->blocksize == 65536) | |
54 | dirent->rec_len = EXT4_MAX_REC_LEN; | |
55 | else | |
56 | dirent->rec_len = 0; | |
57 | } else | |
58 | dirent->rec_len = (len & 65532) | ((len >> 16) & 3); | |
59 | return 0; | |
60 | } | |
61 | ||
8bd0c959 TT |
62 | /* |
63 | * This function checks to see whether or not a potential deleted | |
64 | * directory entry looks valid. What we do is check the deleted entry | |
65 | * and each successive entry to make sure that they all look valid and | |
66 | * that the last deleted entry ends at the beginning of the next | |
67 | * undeleted entry. Returns 1 if the deleted entry looks valid, zero | |
68 | * if not valid. | |
69 | */ | |
6a8da46d NC |
70 | static int ext2fs_validate_entry(ext2_filsys fs, char *buf, |
71 | unsigned int offset, | |
72 | unsigned int final_offset) | |
8bd0c959 TT |
73 | { |
74 | struct ext2_dir_entry *dirent; | |
8a480350 | 75 | unsigned int rec_len; |
6a8da46d | 76 | #define DIRENT_MIN_LENGTH 12 |
efc6f628 | 77 | |
6a8da46d NC |
78 | while ((offset < final_offset) && |
79 | (offset <= fs->blocksize - DIRENT_MIN_LENGTH)) { | |
8bd0c959 | 80 | dirent = (struct ext2_dir_entry *)(buf + offset); |
8a480350 TT |
81 | if (ext2fs_get_rec_len(fs, dirent, &rec_len)) |
82 | return 0; | |
5dd77dbe TT |
83 | offset += rec_len; |
84 | if ((rec_len < 8) || | |
85 | ((rec_len % 4) != 0) || | |
25c7e0c3 | 86 | ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) |
8bd0c959 TT |
87 | return 0; |
88 | } | |
89 | return (offset == final_offset); | |
90 | } | |
91 | ||
92 | errcode_t ext2fs_dir_iterate2(ext2_filsys fs, | |
93 | ext2_ino_t dir, | |
94 | int flags, | |
95 | char *block_buf, | |
96 | int (*func)(ext2_ino_t dir, | |
97 | int entry, | |
98 | struct ext2_dir_entry *dirent, | |
99 | int offset, | |
100 | int blocksize, | |
101 | char *buf, | |
102 | void *priv_data), | |
103 | void *priv_data) | |
21c84b71 TT |
104 | { |
105 | struct dir_context ctx; | |
106 | errcode_t retval; | |
efc6f628 | 107 | |
21c84b71 TT |
108 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
109 | ||
110 | retval = ext2fs_check_directory(fs, dir); | |
111 | if (retval) | |
112 | return retval; | |
efc6f628 | 113 | |
21c84b71 TT |
114 | ctx.dir = dir; |
115 | ctx.flags = flags; | |
116 | if (block_buf) | |
117 | ctx.buf = block_buf; | |
118 | else { | |
c4e3d3f3 | 119 | retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); |
7b4e4534 TT |
120 | if (retval) |
121 | return retval; | |
21c84b71 TT |
122 | } |
123 | ctx.func = func; | |
b5abe6fa | 124 | ctx.priv_data = priv_data; |
21c84b71 | 125 | ctx.errcode = 0; |
ab13b5a9 | 126 | retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY, 0, |
36a43d67 | 127 | ext2fs_process_dir_block, &ctx); |
21c84b71 | 128 | if (!block_buf) |
c4e3d3f3 | 129 | ext2fs_free_mem(&ctx.buf); |
21c84b71 TT |
130 | if (retval) |
131 | return retval; | |
132 | return ctx.errcode; | |
133 | } | |
134 | ||
8bd0c959 TT |
135 | struct xlate { |
136 | int (*func)(struct ext2_dir_entry *dirent, | |
137 | int offset, | |
138 | int blocksize, | |
139 | char *buf, | |
140 | void *priv_data); | |
141 | void *real_private; | |
142 | }; | |
143 | ||
54434927 TT |
144 | static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), |
145 | int entry EXT2FS_ATTR((unused)), | |
8bd0c959 TT |
146 | struct ext2_dir_entry *dirent, int offset, |
147 | int blocksize, char *buf, void *priv_data) | |
148 | { | |
149 | struct xlate *xl = (struct xlate *) priv_data; | |
150 | ||
151 | return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); | |
152 | } | |
153 | ||
f404167d TT |
154 | errcode_t ext2fs_dir_iterate(ext2_filsys fs, |
155 | ext2_ino_t dir, | |
156 | int flags, | |
157 | char *block_buf, | |
158 | int (*func)(struct ext2_dir_entry *dirent, | |
159 | int offset, | |
160 | int blocksize, | |
161 | char *buf, | |
162 | void *priv_data), | |
163 | void *priv_data) | |
8bd0c959 TT |
164 | { |
165 | struct xlate xl; | |
efc6f628 | 166 | |
8bd0c959 TT |
167 | xl.real_private = priv_data; |
168 | xl.func = func; | |
169 | ||
170 | return ext2fs_dir_iterate2(fs, dir, flags, block_buf, | |
171 | xlate_func, &xl); | |
172 | } | |
173 | ||
174 | ||
21c84b71 TT |
175 | /* |
176 | * Helper function which is private to this module. Used by | |
177 | * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() | |
178 | */ | |
54434927 | 179 | int ext2fs_process_dir_block(ext2_filsys fs, |
ab13b5a9 | 180 | blk64_t *blocknr, |
54434927 | 181 | e2_blkcnt_t blockcnt, |
ab13b5a9 | 182 | blk64_t ref_block EXT2FS_ATTR((unused)), |
54434927 TT |
183 | int ref_offset EXT2FS_ATTR((unused)), |
184 | void *priv_data) | |
21c84b71 | 185 | { |
b5abe6fa | 186 | struct dir_context *ctx = (struct dir_context *) priv_data; |
54434927 TT |
187 | unsigned int offset = 0; |
188 | unsigned int next_real_entry = 0; | |
21c84b71 TT |
189 | int ret = 0; |
190 | int changed = 0; | |
191 | int do_abort = 0; | |
25c7e0c3 TT |
192 | unsigned int rec_len, size; |
193 | int entry; | |
21c84b71 TT |
194 | struct ext2_dir_entry *dirent; |
195 | ||
196 | if (blockcnt < 0) | |
197 | return 0; | |
198 | ||
36a43d67 | 199 | entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; |
efc6f628 | 200 | |
ab13b5a9 | 201 | ctx->errcode = ext2fs_read_dir_block3(fs, *blocknr, ctx->buf, 0); |
21c84b71 TT |
202 | if (ctx->errcode) |
203 | return BLOCK_ABORT; | |
204 | ||
21c84b71 TT |
205 | while (offset < fs->blocksize) { |
206 | dirent = (struct ext2_dir_entry *) (ctx->buf + offset); | |
8a480350 TT |
207 | if (ext2fs_get_rec_len(fs, dirent, &rec_len)) |
208 | return BLOCK_ABORT; | |
5dd77dbe TT |
209 | if (((offset + rec_len) > fs->blocksize) || |
210 | (rec_len < 8) || | |
211 | ((rec_len % 4) != 0) || | |
25c7e0c3 | 212 | ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) { |
813bbb25 TT |
213 | ctx->errcode = EXT2_ET_DIR_CORRUPTED; |
214 | return BLOCK_ABORT; | |
215 | } | |
21c84b71 TT |
216 | if (!dirent->inode && |
217 | !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) | |
218 | goto next; | |
219 | ||
8bd0c959 TT |
220 | ret = (ctx->func)(ctx->dir, |
221 | (next_real_entry > offset) ? | |
222 | DIRENT_DELETED_FILE : entry, | |
223 | dirent, offset, | |
224 | fs->blocksize, ctx->buf, | |
225 | ctx->priv_data); | |
226 | if (entry < DIRENT_OTHER_FILE) | |
227 | entry++; | |
efc6f628 | 228 | |
5dd77dbe | 229 | if (ret & DIRENT_CHANGED) { |
8a480350 TT |
230 | if (ext2fs_get_rec_len(fs, dirent, &rec_len)) |
231 | return BLOCK_ABORT; | |
21c84b71 | 232 | changed++; |
5dd77dbe | 233 | } |
21c84b71 TT |
234 | if (ret & DIRENT_ABORT) { |
235 | do_abort++; | |
236 | break; | |
237 | } | |
efc6f628 | 238 | next: |
8bd0c959 | 239 | if (next_real_entry == offset) |
5dd77dbe | 240 | next_real_entry += rec_len; |
efc6f628 | 241 | |
8bd0c959 TT |
242 | if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { |
243 | size = ((dirent->name_len & 0xFF) + 11) & ~3; | |
244 | ||
5dd77dbe | 245 | if (rec_len != size) { |
54434927 TT |
246 | unsigned int final_offset; |
247 | ||
5dd77dbe | 248 | final_offset = offset + rec_len; |
8bd0c959 TT |
249 | offset += size; |
250 | while (offset < final_offset && | |
5dd77dbe | 251 | !ext2fs_validate_entry(fs, ctx->buf, |
8bd0c959 TT |
252 | offset, |
253 | final_offset)) | |
254 | offset += 4; | |
255 | continue; | |
256 | } | |
257 | } | |
5dd77dbe | 258 | offset += rec_len; |
21c84b71 TT |
259 | } |
260 | ||
261 | if (changed) { | |
ab13b5a9 TT |
262 | ctx->errcode = ext2fs_write_dir_block3(fs, *blocknr, ctx->buf, |
263 | 0); | |
21c84b71 TT |
264 | if (ctx->errcode) |
265 | return BLOCK_ABORT; | |
266 | } | |
267 | if (do_abort) | |
268 | return BLOCK_ABORT; | |
269 | return 0; | |
270 | } | |
271 |