]>
Commit | Line | Data |
---|---|---|
3839e657 TT |
1 | /* |
2 | * pass2.c --- check directory structure | |
efc6f628 | 3 | * |
21c84b71 TT |
4 | * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o |
5 | * | |
6 | * %Begin-Header% | |
7 | * This file may be redistributed under the terms of the GNU Public | |
8 | * License. | |
9 | * %End-Header% | |
efc6f628 | 10 | * |
3839e657 TT |
11 | * Pass 2 of e2fsck iterates through all active directory inodes, and |
12 | * applies to following tests to each directory entry in the directory | |
13 | * blocks in the inodes: | |
14 | * | |
15 | * - The length of the directory entry (rec_len) should be at | |
16 | * least 8 bytes, and no more than the remaining space | |
17 | * left in the directory block. | |
18 | * - The length of the name in the directory entry (name_len) | |
efc6f628 | 19 | * should be less than (rec_len - 8). |
3839e657 TT |
20 | * - The inode number in the directory entry should be within |
21 | * legal bounds. | |
22 | * - The inode number should refer to a in-use inode. | |
23 | * - The first entry should be '.', and its inode should be | |
24 | * the inode of the directory. | |
25 | * - The second entry should be '..'. | |
26 | * | |
27 | * To minimize disk seek time, the directory blocks are processed in | |
28 | * sorted order of block numbers. | |
29 | * | |
30 | * Pass 2 also collects the following information: | |
31 | * - The inode numbers of the subdirectories for each directory. | |
32 | * | |
33 | * Pass 2 relies on the following information from previous passes: | |
34 | * - The directory information collected in pass 1. | |
35 | * - The inode_used_map bitmap | |
36 | * - The inode_bad_map bitmap | |
37 | * - The inode_dir_map bitmap | |
2ba05753 | 38 | * - The encrypted_file_info |
06b83bbd | 39 | * - The inode_casefold_map bitmap |
3839e657 TT |
40 | * |
41 | * Pass 2 frees the following data structures | |
42 | * - The inode_bad_map bitmap | |
aa4115a4 | 43 | * - The inode_reg_map bitmap |
2ba05753 | 44 | * - The encrypted_file_info |
06b83bbd | 45 | * - The inode_casefold_map bitmap |
3839e657 TT |
46 | */ |
47 | ||
b969b1b8 | 48 | #define _GNU_SOURCE 1 /* get strnlen() */ |
d1154eb4 | 49 | #include "config.h" |
48e6e813 TT |
50 | #include <string.h> |
51 | ||
3839e657 | 52 | #include "e2fsck.h" |
21c84b71 | 53 | #include "problem.h" |
3dca12fb | 54 | #include "support/dict.h" |
3839e657 | 55 | |
aa4115a4 TT |
56 | #ifdef NO_INLINE_FUNCS |
57 | #define _INLINE_ | |
58 | #else | |
59 | #define _INLINE_ inline | |
60 | #endif | |
61 | ||
ad4fa466 | 62 | /* #define DX_DEBUG */ |
8fdc9985 | 63 | |
3839e657 TT |
64 | /* |
65 | * Keeps track of how many times an inode is referenced. | |
66 | */ | |
4035f40b | 67 | static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf); |
a5abfe03 DW |
68 | static int check_dir_block2(ext2_filsys fs, |
69 | struct ext2_db_entry2 *dir_blocks_info, | |
70 | void *priv_data); | |
3839e657 | 71 | static int check_dir_block(ext2_filsys fs, |
6dc64392 | 72 | struct ext2_db_entry2 *dir_blocks_info, |
54dc7ca2 | 73 | void *priv_data); |
1b6bf175 | 74 | static int allocate_dir_block(e2fsck_t ctx, |
6dc64392 | 75 | struct ext2_db_entry2 *dir_blocks_info, |
21c84b71 | 76 | char *buf, struct problem_context *pctx); |
8fdc9985 | 77 | static void clear_htree(e2fsck_t ctx, ext2_ino_t ino); |
abeb84e3 AD |
78 | static short htree_depth(struct dx_dir_info *dx_dir, |
79 | struct dx_dirblock_info *dx_db); | |
ea1959f0 | 80 | static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b); |
3839e657 | 81 | |
21c84b71 TT |
82 | struct check_dir_struct { |
83 | char *buf; | |
84 | struct problem_context pctx; | |
f8188fff | 85 | int count, max; |
1b6bf175 | 86 | e2fsck_t ctx; |
a5abfe03 DW |
87 | unsigned long long list_offset; |
88 | unsigned long long ra_entries; | |
89 | unsigned long long next_ra_off; | |
efc6f628 | 90 | }; |
21c84b71 | 91 | |
ae9efd05 AB |
92 | static void update_parents(struct dx_dir_info *dx_dir, int type) |
93 | { | |
94 | struct dx_dirblock_info *dx_db, *dx_parent, *dx_previous; | |
937650f7 | 95 | blk_t b; |
ae9efd05 AB |
96 | |
97 | for (b = 0, dx_db = dx_dir->dx_block; | |
98 | b < dx_dir->numblocks; | |
99 | b++, dx_db++) { | |
100 | dx_parent = &dx_dir->dx_block[dx_db->parent]; | |
101 | if (dx_db->type != type) | |
102 | continue; | |
103 | ||
104 | /* | |
105 | * XXX Make sure dx_parent->min_hash > dx_db->min_hash | |
106 | */ | |
107 | if (dx_db->flags & DX_FLAG_FIRST) { | |
108 | dx_parent->min_hash = dx_db->min_hash; | |
109 | if (dx_parent->previous) { | |
110 | dx_previous = | |
111 | &dx_dir->dx_block[dx_parent->previous]; | |
112 | dx_previous->node_max_hash = | |
113 | dx_parent->min_hash; | |
114 | } | |
115 | } | |
116 | /* | |
117 | * XXX Make sure dx_parent->max_hash < dx_db->max_hash | |
118 | */ | |
119 | if (dx_db->flags & DX_FLAG_LAST) { | |
120 | dx_parent->max_hash = dx_db->max_hash; | |
121 | } | |
122 | } | |
123 | } | |
124 | ||
08b21301 | 125 | void e2fsck_pass2(e2fsck_t ctx) |
3839e657 | 126 | { |
a4742691 TT |
127 | struct ext2_super_block *sb = ctx->fs->super; |
128 | struct problem_context pctx; | |
129 | ext2_filsys fs = ctx->fs; | |
686994eb | 130 | char *buf = NULL; |
8bf191e8 | 131 | #ifdef RESOURCE_TRACK |
3839e657 | 132 | struct resource_track rtrack; |
8bf191e8 | 133 | #endif |
21c84b71 | 134 | struct check_dir_struct cd; |
8fdc9985 | 135 | struct dx_dir_info *dx_dir; |
ac3256fd | 136 | struct dx_dirblock_info *dx_db; |
937650f7 | 137 | blk_t b; |
74fbba1f | 138 | ext2_ino_t i; |
abeb84e3 | 139 | short depth; |
8fdc9985 TT |
140 | problem_t code; |
141 | int bad_dir; | |
a5abfe03 DW |
142 | int (*check_dir_func)(ext2_filsys fs, |
143 | struct ext2_db_entry2 *dir_blocks_info, | |
144 | void *priv_data); | |
8fdc9985 | 145 | |
6d96b00d | 146 | init_resource_track(&rtrack, ctx->fs->io); |
1b6bf175 TT |
147 | clear_problem_context(&cd.pctx); |
148 | ||
3839e657 TT |
149 | #ifdef MTRACE |
150 | mtrace_print("Pass 2"); | |
151 | #endif | |
152 | ||
b84eee32 | 153 | fs->flags |= EXT2_FLAG_IGNORE_SWAP_DIRENT; |
1b6bf175 TT |
154 | if (!(ctx->options & E2F_OPT_PREEN)) |
155 | fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx); | |
156 | ||
749f0712 TT |
157 | cd.pctx.errcode = e2fsck_setup_icount(ctx, "inode_count", |
158 | EXT2_ICOUNT_OPT_INCREMENT, | |
159 | ctx->inode_link_info, &ctx->inode_count); | |
1b6bf175 TT |
160 | if (cd.pctx.errcode) { |
161 | fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx); | |
08b21301 | 162 | ctx->flags |= E2F_FLAG_ABORT; |
a54733d2 | 163 | goto cleanup; |
21c84b71 | 164 | } |
bcf9c5d4 | 165 | buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize, |
54dc7ca2 | 166 | "directory scan buffer"); |
3839e657 | 167 | |
21c84b71 TT |
168 | /* |
169 | * Set up the parent pointer for the root directory, if | |
170 | * present. (If the root directory is not present, we will | |
171 | * create it in pass 3.) | |
172 | */ | |
28db82a8 | 173 | (void) e2fsck_dir_info_set_parent(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO); |
21c84b71 TT |
174 | |
175 | cd.buf = buf; | |
1b6bf175 | 176 | cd.ctx = ctx; |
f75c28de | 177 | cd.count = 1; |
6dc64392 | 178 | cd.max = ext2fs_dblist_count2(fs->dblist); |
a5abfe03 DW |
179 | cd.list_offset = 0; |
180 | cd.ra_entries = ctx->readahead_kb * 1024 / ctx->fs->blocksize; | |
181 | cd.next_ra_off = 0; | |
f75c28de TT |
182 | |
183 | if (ctx->progress) | |
184 | (void) (ctx->progress)(ctx, 2, 0, cd.max); | |
ea1959f0 | 185 | |
86f3b6cf | 186 | if (ext2fs_has_feature_dir_index(fs->super)) |
6dc64392 | 187 | ext2fs_dblist_sort2(fs->dblist, special_dir_block_cmp); |
efc6f628 | 188 | |
a5abfe03 DW |
189 | check_dir_func = cd.ra_entries ? check_dir_block2 : check_dir_block; |
190 | cd.pctx.errcode = ext2fs_dblist_iterate2(fs->dblist, check_dir_func, | |
6dc64392 | 191 | &cd); |
6267ee49 AD |
192 | if (ctx->flags & E2F_FLAG_RESTART_LATER) { |
193 | ctx->flags |= E2F_FLAG_RESTART; | |
db3d8718 | 194 | ctx->flags &= ~E2F_FLAG_RESTART_LATER; |
6267ee49 AD |
195 | } |
196 | ||
db3d8718 | 197 | if (ctx->flags & E2F_FLAG_RUN_RETURN) |
a54733d2 | 198 | goto cleanup; |
db3d8718 | 199 | |
1b6bf175 TT |
200 | if (cd.pctx.errcode) { |
201 | fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx); | |
08b21301 | 202 | ctx->flags |= E2F_FLAG_ABORT; |
a54733d2 | 203 | goto cleanup; |
7ac02a5e | 204 | } |
8fdc9985 | 205 | |
8fdc9985 | 206 | for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) { |
4cae0452 | 207 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
a54733d2 | 208 | goto cleanup; |
d02d0195 DW |
209 | if (e2fsck_dir_will_be_rehashed(ctx, dx_dir->ino) || |
210 | dx_dir->numblocks == 0) | |
8fdc9985 TT |
211 | continue; |
212 | clear_problem_context(&pctx); | |
213 | bad_dir = 0; | |
214 | pctx.dir = dx_dir->ino; | |
215 | dx_db = dx_dir->dx_block; | |
216 | if (dx_db->flags & DX_FLAG_REFERENCED) | |
217 | dx_db->flags |= DX_FLAG_DUP_REF; | |
218 | else | |
219 | dx_db->flags |= DX_FLAG_REFERENCED; | |
220 | /* | |
221 | * Find all of the first and last leaf blocks, and | |
222 | * update their parent's min and max hash values | |
223 | */ | |
ae9efd05 AB |
224 | update_parents(dx_dir, DX_DIRBLOCK_LEAF); |
225 | ||
226 | /* for 3 level htree: update 2 level parent's min | |
227 | * and max hash values */ | |
228 | update_parents(dx_dir, DX_DIRBLOCK_NODE); | |
efc6f628 | 229 | |
8fdc9985 TT |
230 | for (b=0, dx_db = dx_dir->dx_block; |
231 | b < dx_dir->numblocks; | |
232 | b++, dx_db++) { | |
233 | pctx.blkcount = b; | |
234 | pctx.group = dx_db->parent; | |
235 | code = 0; | |
236 | if (!(dx_db->flags & DX_FLAG_FIRST) && | |
237 | (dx_db->min_hash < dx_db->node_min_hash)) { | |
238 | pctx.blk = dx_db->min_hash; | |
239 | pctx.blk2 = dx_db->node_min_hash; | |
240 | code = PR_2_HTREE_MIN_HASH; | |
241 | fix_problem(ctx, code, &pctx); | |
242 | bad_dir++; | |
243 | } | |
ad4fa466 TT |
244 | if (dx_db->type == DX_DIRBLOCK_LEAF) { |
245 | depth = htree_depth(dx_dir, dx_db); | |
246 | if (depth != dx_dir->depth) { | |
e5e12db9 | 247 | pctx.num = dx_dir->depth; |
ad4fa466 TT |
248 | code = PR_2_HTREE_BAD_DEPTH; |
249 | fix_problem(ctx, code, &pctx); | |
250 | bad_dir++; | |
251 | } | |
252 | } | |
8fdc9985 | 253 | /* |
efc6f628 | 254 | * This test doesn't apply for the root block |
8fdc9985 TT |
255 | * at block #0 |
256 | */ | |
257 | if (b && | |
258 | (dx_db->max_hash > dx_db->node_max_hash)) { | |
259 | pctx.blk = dx_db->max_hash; | |
260 | pctx.blk2 = dx_db->node_max_hash; | |
261 | code = PR_2_HTREE_MAX_HASH; | |
262 | fix_problem(ctx, code, &pctx); | |
503f9e7f | 263 | bad_dir++; |
8fdc9985 TT |
264 | } |
265 | if (!(dx_db->flags & DX_FLAG_REFERENCED)) { | |
266 | code = PR_2_HTREE_NOTREF; | |
267 | fix_problem(ctx, code, &pctx); | |
268 | bad_dir++; | |
269 | } else if (dx_db->flags & DX_FLAG_DUP_REF) { | |
270 | code = PR_2_HTREE_DUPREF; | |
271 | fix_problem(ctx, code, &pctx); | |
272 | bad_dir++; | |
273 | } | |
8fdc9985 TT |
274 | } |
275 | if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) { | |
276 | clear_htree(ctx, dx_dir->ino); | |
62acaa1d | 277 | dx_dir->numblocks = 0; |
8fdc9985 | 278 | } |
8fdc9985 | 279 | } |
23f75f6e | 280 | e2fsck_free_dx_dir_info(ctx); |
149640fa | 281 | |
c4e3d3f3 | 282 | ext2fs_free_mem(&buf); |
21c84b71 TT |
283 | ext2fs_free_dblist(fs->dblist); |
284 | ||
1b6bf175 TT |
285 | if (ctx->inode_bad_map) { |
286 | ext2fs_free_inode_bitmap(ctx->inode_bad_map); | |
287 | ctx->inode_bad_map = 0; | |
3839e657 | 288 | } |
aa4115a4 TT |
289 | if (ctx->inode_reg_map) { |
290 | ext2fs_free_inode_bitmap(ctx->inode_reg_map); | |
291 | ctx->inode_reg_map = 0; | |
292 | } | |
06b83bbd GKB |
293 | if (ctx->inode_casefold_map) { |
294 | ext2fs_free_inode_bitmap(ctx->inode_casefold_map); | |
295 | ctx->inode_casefold_map = 0; | |
296 | } | |
2ba05753 | 297 | destroy_encrypted_file_info(ctx); |
61421ee5 DR |
298 | if (ctx->casefolded_dirs) { |
299 | ext2fs_u32_list_free(ctx->casefolded_dirs); | |
300 | ctx->casefolded_dirs = 0; | |
301 | } | |
a4742691 TT |
302 | |
303 | clear_problem_context(&pctx); | |
304 | if (ctx->large_files) { | |
86f3b6cf | 305 | if (!ext2fs_has_feature_large_file(sb) && |
a4742691 | 306 | fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) { |
86f3b6cf | 307 | ext2fs_set_feature_large_file(sb); |
0cfce7f7 | 308 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; |
a4742691 TT |
309 | ext2fs_mark_super_dirty(fs); |
310 | } | |
311 | if (sb->s_rev_level == EXT2_GOOD_OLD_REV && | |
312 | fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) { | |
313 | ext2fs_update_dynamic_rev(fs); | |
314 | ext2fs_mark_super_dirty(fs); | |
315 | } | |
a4742691 | 316 | } |
efc6f628 | 317 | |
9facd076 | 318 | print_resource_track(ctx, _("Pass 2"), &rtrack, fs->io); |
a54733d2 TT |
319 | cleanup: |
320 | ext2fs_free_mem(&buf); | |
b84eee32 | 321 | fs->flags &= ~EXT2_FLAG_IGNORE_SWAP_DIRENT; |
3839e657 TT |
322 | } |
323 | ||
ad4fa466 | 324 | #define MAX_DEPTH 32000 |
abeb84e3 AD |
325 | static short htree_depth(struct dx_dir_info *dx_dir, |
326 | struct dx_dirblock_info *dx_db) | |
ad4fa466 | 327 | { |
abeb84e3 | 328 | short depth = 0; |
ad4fa466 TT |
329 | |
330 | while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) { | |
331 | dx_db = &dx_dir->dx_block[dx_db->parent]; | |
332 | depth++; | |
333 | } | |
334 | return depth; | |
335 | } | |
336 | ||
b5f2be81 EB |
337 | static int dict_de_cmp(const void *cmp_ctx EXT2FS_ATTR((unused)), |
338 | const void *a, const void *b) | |
0926668d | 339 | { |
520ead37 | 340 | const struct ext2_dir_entry *de_a, *de_b; |
0926668d TT |
341 | int a_len, b_len; |
342 | ||
520ead37 | 343 | de_a = (const struct ext2_dir_entry *) a; |
70f4632b | 344 | a_len = ext2fs_dirent_name_len(de_a); |
520ead37 | 345 | de_b = (const struct ext2_dir_entry *) b; |
70f4632b | 346 | b_len = ext2fs_dirent_name_len(de_b); |
0926668d TT |
347 | |
348 | if (a_len != b_len) | |
349 | return (a_len - b_len); | |
350 | ||
baa14bd1 | 351 | return memcmp(de_a->name, de_b->name, a_len); |
0926668d | 352 | } |
ad4fa466 | 353 | |
782df523 GKB |
354 | static int dict_de_cf_cmp(const void *cmp_ctx, const void *a, const void *b) |
355 | { | |
356 | const struct ext2fs_nls_table *tbl = cmp_ctx; | |
357 | const struct ext2_dir_entry *de_a, *de_b; | |
358 | int a_len, b_len; | |
359 | ||
360 | de_a = (const struct ext2_dir_entry *) a; | |
361 | a_len = ext2fs_dirent_name_len(de_a); | |
362 | de_b = (const struct ext2_dir_entry *) b; | |
363 | b_len = ext2fs_dirent_name_len(de_b); | |
364 | ||
f158f896 TT |
365 | return ext2fs_casefold_cmp(tbl, |
366 | (const unsigned char *) de_a->name, a_len, | |
367 | (const unsigned char *) de_b->name, b_len); | |
782df523 GKB |
368 | } |
369 | ||
ea1959f0 TT |
370 | /* |
371 | * This is special sort function that makes sure that directory blocks | |
372 | * with a dirblock of zero are sorted to the beginning of the list. | |
373 | * This guarantees that the root node of the htree directories are | |
374 | * processed first, so we know what hash version to use. | |
375 | */ | |
376 | static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b) | |
377 | { | |
6dc64392 VAH |
378 | const struct ext2_db_entry2 *db_a = |
379 | (const struct ext2_db_entry2 *) a; | |
380 | const struct ext2_db_entry2 *db_b = | |
381 | (const struct ext2_db_entry2 *) b; | |
ea1959f0 TT |
382 | |
383 | if (db_a->blockcnt && !db_b->blockcnt) | |
384 | return 1; | |
385 | ||
386 | if (!db_a->blockcnt && db_b->blockcnt) | |
387 | return -1; | |
efc6f628 | 388 | |
ea1959f0 TT |
389 | if (db_a->blk != db_b->blk) |
390 | return (int) (db_a->blk - db_b->blk); | |
efc6f628 | 391 | |
ea1959f0 TT |
392 | if (db_a->ino != db_b->ino) |
393 | return (int) (db_a->ino - db_b->ino); | |
394 | ||
395 | return (int) (db_a->blockcnt - db_b->blockcnt); | |
396 | } | |
397 | ||
398 | ||
3839e657 TT |
399 | /* |
400 | * Make sure the first entry in the directory is '.', and that the | |
401 | * directory entry is sane. | |
402 | */ | |
1b6bf175 | 403 | static int check_dot(e2fsck_t ctx, |
3839e657 | 404 | struct ext2_dir_entry *dirent, |
86c627ec | 405 | ext2_ino_t ino, struct problem_context *pctx) |
3839e657 TT |
406 | { |
407 | struct ext2_dir_entry *nextdir; | |
8a480350 | 408 | unsigned int rec_len, new_len; |
3c7c6d73 TT |
409 | int status = 0; |
410 | int created = 0; | |
411 | problem_t problem = 0; | |
63f44aaf | 412 | int ftype = EXT2_FT_DIR; |
efc6f628 | 413 | |
21c84b71 TT |
414 | if (!dirent->inode) |
415 | problem = PR_2_MISSING_DOT; | |
70f4632b | 416 | else if ((ext2fs_dirent_name_len(dirent) != 1) || |
21c84b71 TT |
417 | (dirent->name[0] != '.')) |
418 | problem = PR_2_1ST_NOT_DOT; | |
419 | else if (dirent->name[1] != '\0') | |
420 | problem = PR_2_DOT_NULL_TERM; | |
5dd77dbe | 421 | |
8a480350 | 422 | (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); |
21c84b71 | 423 | if (problem) { |
63f44aaf AD |
424 | if (!ext2fs_has_feature_filetype(ctx->fs->super)) |
425 | ftype = EXT2_FT_UNKNOWN; | |
1b6bf175 | 426 | if (fix_problem(ctx, problem, pctx)) { |
5dd77dbe TT |
427 | if (rec_len < 12) |
428 | rec_len = dirent->rec_len = 12; | |
3839e657 | 429 | dirent->inode = ino; |
70f4632b | 430 | ext2fs_dirent_set_name_len(dirent, 1); |
63f44aaf | 431 | ext2fs_dirent_set_file_type(dirent, ftype); |
3839e657 | 432 | dirent->name[0] = '.'; |
21c84b71 | 433 | dirent->name[1] = '\0'; |
3839e657 TT |
434 | status = 1; |
435 | created = 1; | |
3839e657 TT |
436 | } |
437 | } | |
3839e657 | 438 | if (dirent->inode != ino) { |
1b6bf175 | 439 | if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) { |
3839e657 TT |
440 | dirent->inode = ino; |
441 | status = 1; | |
21c84b71 | 442 | } |
3839e657 | 443 | } |
5dd77dbe TT |
444 | if (rec_len > 12) { |
445 | new_len = rec_len - 12; | |
3839e657 | 446 | if (new_len > 12) { |
3839e657 | 447 | if (created || |
f8188fff | 448 | fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { |
3839e657 TT |
449 | nextdir = (struct ext2_dir_entry *) |
450 | ((char *) dirent + 12); | |
451 | dirent->rec_len = 12; | |
63f44aaf AD |
452 | /* if the next entry looks like "..", leave it |
453 | * and let check_dotdot() verify the dirent, | |
454 | * otherwise zap the following entry. */ | |
455 | if (strncmp(nextdir->name, "..", 3) != 0) { | |
456 | (void)ext2fs_set_rec_len(ctx->fs, | |
457 | new_len, | |
458 | nextdir); | |
459 | nextdir->inode = 0; | |
460 | ext2fs_dirent_set_name_len(nextdir, 0); | |
461 | ext2fs_dirent_set_file_type(nextdir, | |
462 | ftype); | |
225e5d09 TT |
463 | #ifdef WORDS_BIGENDIAN |
464 | } else { | |
465 | (void) ext2fs_dirent_swab_in2(ctx->fs, | |
466 | (char *) nextdir, | |
467 | ctx->fs->blocksize - 12, 0); | |
468 | #endif | |
63f44aaf | 469 | } |
3839e657 TT |
470 | status = 1; |
471 | } | |
472 | } | |
473 | } | |
474 | return status; | |
475 | } | |
476 | ||
477 | /* | |
478 | * Make sure the second entry in the directory is '..', and that the | |
479 | * directory entry is sane. We do not check the inode number of '..' | |
480 | * here; this gets done in pass 3. | |
481 | */ | |
1b6bf175 | 482 | static int check_dotdot(e2fsck_t ctx, |
3839e657 | 483 | struct ext2_dir_entry *dirent, |
28db82a8 | 484 | ext2_ino_t ino, struct problem_context *pctx) |
3839e657 | 485 | { |
3c7c6d73 | 486 | problem_t problem = 0; |
cf5301d7 | 487 | unsigned int rec_len; |
63f44aaf | 488 | int ftype = EXT2_FT_DIR; |
efc6f628 | 489 | |
21c84b71 TT |
490 | if (!dirent->inode) |
491 | problem = PR_2_MISSING_DOT_DOT; | |
70f4632b | 492 | else if ((ext2fs_dirent_name_len(dirent) != 2) || |
21c84b71 TT |
493 | (dirent->name[0] != '.') || |
494 | (dirent->name[1] != '.')) | |
495 | problem = PR_2_2ND_NOT_DOT_DOT; | |
496 | else if (dirent->name[2] != '\0') | |
497 | problem = PR_2_DOT_DOT_NULL_TERM; | |
498 | ||
8a480350 | 499 | (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); |
21c84b71 | 500 | if (problem) { |
63f44aaf AD |
501 | if (!ext2fs_has_feature_filetype(ctx->fs->super)) |
502 | ftype = EXT2_FT_UNKNOWN; | |
1b6bf175 | 503 | if (fix_problem(ctx, problem, pctx)) { |
5dd77dbe | 504 | if (rec_len < 12) |
21c84b71 | 505 | dirent->rec_len = 12; |
3839e657 TT |
506 | /* |
507 | * Note: we don't have the parent inode just | |
508 | * yet, so we will fill it in with the root | |
509 | * inode. This will get fixed in pass 3. | |
510 | */ | |
511 | dirent->inode = EXT2_ROOT_INO; | |
70f4632b | 512 | ext2fs_dirent_set_name_len(dirent, 2); |
63f44aaf | 513 | ext2fs_dirent_set_file_type(dirent, ftype); |
3839e657 TT |
514 | dirent->name[0] = '.'; |
515 | dirent->name[1] = '.'; | |
21c84b71 | 516 | dirent->name[2] = '\0'; |
3839e657 | 517 | return 1; |
efc6f628 | 518 | } |
3839e657 TT |
519 | return 0; |
520 | } | |
28db82a8 TT |
521 | if (e2fsck_dir_info_set_dotdot(ctx, ino, dirent->inode)) { |
522 | fix_problem(ctx, PR_2_NO_DIRINFO, pctx); | |
523 | return -1; | |
524 | } | |
3839e657 TT |
525 | return 0; |
526 | } | |
527 | ||
528 | /* | |
529 | * Check to make sure a directory entry doesn't contain any illegal | |
530 | * characters. | |
531 | */ | |
1b6bf175 | 532 | static int check_name(e2fsck_t ctx, |
3839e657 | 533 | struct ext2_dir_entry *dirent, |
54434927 | 534 | struct problem_context *pctx) |
3839e657 TT |
535 | { |
536 | int i; | |
537 | int fixup = -1; | |
3839e657 | 538 | int ret = 0; |
efc6f628 | 539 | |
70f4632b | 540 | for ( i = 0; i < ext2fs_dirent_name_len(dirent); i++) { |
dbff534e TT |
541 | if (dirent->name[i] != '/' && dirent->name[i] != '\0') |
542 | continue; | |
dbff534e TT |
543 | if (fixup < 0) |
544 | fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx); | |
545 | if (fixup == 0) | |
546 | return 0; | |
547 | dirent->name[i] = '.'; | |
548 | ret = 1; | |
3839e657 TT |
549 | } |
550 | return ret; | |
551 | } | |
552 | ||
e3dd5c6f | 553 | static int encrypted_check_name(e2fsck_t ctx, |
2ba05753 | 554 | const struct ext2_dir_entry *dirent, |
e3dd5c6f TT |
555 | struct problem_context *pctx) |
556 | { | |
557 | if (ext2fs_dirent_name_len(dirent) < EXT4_CRYPTO_BLOCK_SIZE) { | |
2ba05753 | 558 | if (fix_problem(ctx, PR_2_BAD_ENCRYPTED_NAME, pctx)) |
e3dd5c6f | 559 | return 1; |
e3dd5c6f TT |
560 | ext2fs_unmark_valid(ctx->fs); |
561 | } | |
562 | return 0; | |
563 | } | |
564 | ||
06b83bbd GKB |
565 | static int encoded_check_name(e2fsck_t ctx, |
566 | struct ext2_dir_entry *dirent, | |
567 | struct problem_context *pctx) | |
568 | { | |
569 | const struct ext2fs_nls_table *tbl = ctx->fs->encoding; | |
570 | int ret; | |
571 | int len = ext2fs_dirent_name_len(dirent); | |
572 | char *pos, *end; | |
573 | ||
574 | ret = ext2fs_check_encoded_name(tbl, dirent->name, len, &pos); | |
575 | if (ret < 0) { | |
576 | fatal_error(ctx, _("NLS is broken.")); | |
577 | } else if(ret > 0) { | |
578 | ret = fix_problem(ctx, PR_2_BAD_ENCODED_NAME, pctx); | |
579 | if (ret) { | |
580 | end = &dirent->name[len]; | |
581 | for (; *pos && pos != end; pos++) | |
582 | *pos = '.'; | |
583 | } | |
584 | } | |
585 | ||
586 | return (ret || check_name(ctx, dirent, pctx)); | |
587 | } | |
588 | ||
aa4115a4 TT |
589 | /* |
590 | * Check the directory filetype (if present) | |
591 | */ | |
592 | static _INLINE_ int check_filetype(e2fsck_t ctx, | |
54434927 TT |
593 | struct ext2_dir_entry *dirent, |
594 | ext2_ino_t dir_ino EXT2FS_ATTR((unused)), | |
595 | struct problem_context *pctx) | |
aa4115a4 | 596 | { |
70f4632b | 597 | int filetype = ext2fs_dirent_file_type(dirent); |
aa4115a4 TT |
598 | int should_be = EXT2_FT_UNKNOWN; |
599 | struct ext2_inode inode; | |
600 | ||
86f3b6cf | 601 | if (!ext2fs_has_feature_filetype(ctx->fs->super)) { |
7847c1d4 TT |
602 | if (filetype == 0 || |
603 | !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx)) | |
604 | return 0; | |
70f4632b | 605 | ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); |
7847c1d4 TT |
606 | return 1; |
607 | } | |
aa4115a4 | 608 | |
c5d2f50d | 609 | if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dirent->inode)) { |
aa4115a4 | 610 | should_be = EXT2_FT_DIR; |
c5d2f50d | 611 | } else if (ext2fs_test_inode_bitmap2(ctx->inode_reg_map, |
aa4115a4 TT |
612 | dirent->inode)) { |
613 | should_be = EXT2_FT_REG_FILE; | |
614 | } else if (ctx->inode_bad_map && | |
c5d2f50d | 615 | ext2fs_test_inode_bitmap2(ctx->inode_bad_map, |
aa4115a4 TT |
616 | dirent->inode)) |
617 | should_be = 0; | |
618 | else { | |
619 | e2fsck_read_inode(ctx, dirent->inode, &inode, | |
620 | "check_filetype"); | |
6fdc7a32 | 621 | should_be = ext2_file_type(inode.i_mode); |
aa4115a4 TT |
622 | } |
623 | if (filetype == should_be) | |
624 | return 0; | |
625 | pctx->num = should_be; | |
626 | ||
627 | if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, | |
628 | pctx) == 0) | |
629 | return 0; | |
efc6f628 | 630 | |
70f4632b | 631 | ext2fs_dirent_set_file_type(dirent, should_be); |
aa4115a4 TT |
632 | return 1; |
633 | } | |
634 | ||
8fdc9985 | 635 | static void parse_int_node(ext2_filsys fs, |
6dc64392 | 636 | struct ext2_db_entry2 *db, |
8fdc9985 TT |
637 | struct check_dir_struct *cd, |
638 | struct dx_dir_info *dx_dir, | |
07307114 | 639 | char *block_buf, int failed_csum) |
8fdc9985 | 640 | { |
937650f7 AD |
641 | struct ext2_dx_root_info *root; |
642 | struct ext2_dx_entry *ent; | |
8fdc9985 TT |
643 | struct ext2_dx_countlimit *limit; |
644 | struct dx_dirblock_info *dx_db; | |
ad4fa466 | 645 | int i, expect_limit, count; |
8fdc9985 TT |
646 | blk_t blk; |
647 | ext2_dirhash_t min_hash = 0xffffffff; | |
648 | ext2_dirhash_t max_hash = 0; | |
ad4fa466 | 649 | ext2_dirhash_t hash = 0, prev_hash; |
07307114 | 650 | int csum_size = 0; |
8fdc9985 TT |
651 | |
652 | if (db->blockcnt == 0) { | |
653 | root = (struct ext2_dx_root_info *) (block_buf + 24); | |
efc6f628 | 654 | |
8fdc9985 TT |
655 | #ifdef DX_DEBUG |
656 | printf("Root node dump:\n"); | |
8deb80a5 | 657 | printf("\t Reserved zero: %u\n", root->reserved_zero); |
74fbba1f AD |
658 | printf("\t Hash Version: %u\n", root->hash_version); |
659 | printf("\t Info length: %u\n", root->info_length); | |
660 | printf("\t Indirect levels: %u\n", root->indirect_levels); | |
661 | printf("\t Flags: %x\n", root->unused_flags); | |
8fdc9985 TT |
662 | #endif |
663 | ||
664 | ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length); | |
07307114 DW |
665 | |
666 | if (failed_csum && | |
667 | (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || | |
668 | fix_problem(cd->ctx, PR_2_HTREE_ROOT_CSUM_INVALID, | |
669 | &cd->pctx))) | |
670 | goto clear_and_exit; | |
8fdc9985 TT |
671 | } else { |
672 | ent = (struct ext2_dx_entry *) (block_buf+8); | |
07307114 DW |
673 | |
674 | if (failed_csum && | |
675 | (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || | |
676 | fix_problem(cd->ctx, PR_2_HTREE_NODE_CSUM_INVALID, | |
677 | &cd->pctx))) | |
678 | goto clear_and_exit; | |
8fdc9985 | 679 | } |
07307114 | 680 | |
8fdc9985 TT |
681 | limit = (struct ext2_dx_countlimit *) ent; |
682 | ||
683 | #ifdef DX_DEBUG | |
efc6f628 | 684 | printf("Number of entries (count): %d\n", |
8132d840 | 685 | ext2fs_le16_to_cpu(limit->count)); |
efc6f628 | 686 | printf("Number of entries (limit): %d\n", |
8132d840 | 687 | ext2fs_le16_to_cpu(limit->limit)); |
8fdc9985 TT |
688 | #endif |
689 | ||
8132d840 | 690 | count = ext2fs_le16_to_cpu(limit->count); |
86f3b6cf | 691 | if (ext2fs_has_feature_metadata_csum(fs->super)) |
07307114 DW |
692 | csum_size = sizeof(struct ext2_dx_tail); |
693 | expect_limit = (fs->blocksize - | |
694 | (csum_size + ((char *) ent - block_buf))) / | |
695 | sizeof(struct ext2_dx_entry); | |
8132d840 TT |
696 | if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) { |
697 | cd->pctx.num = ext2fs_le16_to_cpu(limit->limit); | |
ad4fa466 TT |
698 | if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx)) |
699 | goto clear_and_exit; | |
700 | } | |
8132d840 TT |
701 | if (count > expect_limit) { |
702 | cd->pctx.num = count; | |
ad4fa466 TT |
703 | if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx)) |
704 | goto clear_and_exit; | |
705 | count = expect_limit; | |
706 | } | |
efc6f628 | 707 | |
ad4fa466 TT |
708 | for (i=0; i < count; i++) { |
709 | prev_hash = hash; | |
8132d840 | 710 | hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0; |
8fdc9985 | 711 | #ifdef DX_DEBUG |
8deb80a5 | 712 | printf("Entry #%d: Hash 0x%08x, block %u\n", i, |
8132d840 | 713 | hash, ext2fs_le32_to_cpu(ent[i].block)); |
8fdc9985 | 714 | #endif |
1d9ec88d | 715 | blk = ext2fs_le32_to_cpu(ent[i].block) & EXT4_DX_BLOCK_MASK; |
8fdc9985 | 716 | /* Check to make sure the block is valid */ |
937650f7 | 717 | if (blk >= dx_dir->numblocks) { |
b7a00563 | 718 | cd->pctx.blk = blk; |
8fdc9985 | 719 | if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK, |
ad4fa466 TT |
720 | &cd->pctx)) |
721 | goto clear_and_exit; | |
977ac873 | 722 | continue; |
8fdc9985 | 723 | } |
ad4fa466 TT |
724 | if (hash < prev_hash && |
725 | fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx)) | |
726 | goto clear_and_exit; | |
8fdc9985 TT |
727 | dx_db = &dx_dir->dx_block[blk]; |
728 | if (dx_db->flags & DX_FLAG_REFERENCED) { | |
729 | dx_db->flags |= DX_FLAG_DUP_REF; | |
730 | } else { | |
731 | dx_db->flags |= DX_FLAG_REFERENCED; | |
732 | dx_db->parent = db->blockcnt; | |
733 | } | |
ae9efd05 AB |
734 | |
735 | dx_db->previous = | |
1d9ec88d LC |
736 | i ? (ext2fs_le32_to_cpu(ent[i-1].block) & |
737 | EXT4_DX_BLOCK_MASK) : 0; | |
ae9efd05 | 738 | |
8fdc9985 TT |
739 | if (hash < min_hash) |
740 | min_hash = hash; | |
741 | if (hash > max_hash) | |
742 | max_hash = hash; | |
743 | dx_db->node_min_hash = hash; | |
8132d840 | 744 | if ((i+1) < count) |
efc6f628 | 745 | dx_db->node_max_hash = |
8132d840 | 746 | ext2fs_le32_to_cpu(ent[i+1].hash) & ~1; |
8fdc9985 TT |
747 | else { |
748 | dx_db->node_max_hash = 0xfffffffe; | |
749 | dx_db->flags |= DX_FLAG_LAST; | |
750 | } | |
751 | if (i == 0) | |
752 | dx_db->flags |= DX_FLAG_FIRST; | |
753 | } | |
754 | #ifdef DX_DEBUG | |
755 | printf("Blockcnt = %d, min hash 0x%08x, max hash 0x%08x\n", | |
756 | db->blockcnt, min_hash, max_hash); | |
757 | #endif | |
758 | dx_db = &dx_dir->dx_block[db->blockcnt]; | |
759 | dx_db->min_hash = min_hash; | |
760 | dx_db->max_hash = max_hash; | |
ad4fa466 TT |
761 | return; |
762 | ||
763 | clear_and_exit: | |
764 | clear_htree(cd->ctx, cd->pctx.ino); | |
765 | dx_dir->numblocks = 0; | |
07307114 | 766 | e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino); |
8fdc9985 | 767 | } |
aa4115a4 | 768 | |
e70ae99e TT |
769 | /* |
770 | * Given a busted directory, try to salvage it somehow. | |
efc6f628 | 771 | * |
e70ae99e | 772 | */ |
ad4fa466 | 773 | static void salvage_directory(ext2_filsys fs, |
e70ae99e TT |
774 | struct ext2_dir_entry *dirent, |
775 | struct ext2_dir_entry *prev, | |
82ad476d | 776 | unsigned int *offset, |
61421ee5 DR |
777 | unsigned int block_len, |
778 | int hash_in_dirent) | |
e70ae99e TT |
779 | { |
780 | char *cp = (char *) dirent; | |
8a480350 TT |
781 | int left; |
782 | unsigned int rec_len, prev_rec_len; | |
4a3dc1f0 | 783 | unsigned int name_len; |
e70ae99e | 784 | |
4a3dc1f0 DW |
785 | /* |
786 | * If the space left for the entry is too small to be an entry, | |
787 | * we can't access dirent's fields, so plumb in the values needed | |
788 | * so that the previous entry absorbs this one. | |
789 | */ | |
790 | if (block_len - *offset < EXT2_DIR_ENTRY_HEADER_LEN) { | |
791 | name_len = 0; | |
792 | rec_len = block_len - *offset; | |
793 | } else { | |
794 | name_len = ext2fs_dirent_name_len(dirent); | |
795 | (void) ext2fs_get_rec_len(fs, dirent, &rec_len); | |
796 | } | |
82ad476d | 797 | left = block_len - *offset - rec_len; |
5dd77dbe | 798 | |
e70ae99e TT |
799 | /* |
800 | * Special case of directory entry of size 8: copy what's left | |
801 | * of the directory block up to cover up the invalid hole. | |
802 | */ | |
f158f896 | 803 | if ((left >= (int) ext2fs_dir_rec_len(1, hash_in_dirent)) && |
61421ee5 | 804 | (rec_len == EXT2_DIR_ENTRY_HEADER_LEN)) { |
4a3dc1f0 DW |
805 | memmove(cp, cp+EXT2_DIR_ENTRY_HEADER_LEN, left); |
806 | memset(cp + left, 0, EXT2_DIR_ENTRY_HEADER_LEN); | |
ad4fa466 TT |
807 | return; |
808 | } | |
809 | /* | |
810 | * If the directory entry overruns the end of the directory | |
811 | * block, and the name is small enough to fit, then adjust the | |
812 | * record length. | |
813 | */ | |
814 | if ((left < 0) && | |
4a3dc1f0 | 815 | ((int) rec_len + left > EXT2_DIR_ENTRY_HEADER_LEN) && |
61421ee5 | 816 | ((int) ext2fs_dir_rec_len(name_len, hash_in_dirent) <= (int) rec_len + left) && |
ad4fa466 TT |
817 | dirent->inode <= fs->super->s_inodes_count && |
818 | strnlen(dirent->name, name_len) == name_len) { | |
8a480350 | 819 | (void) ext2fs_set_rec_len(fs, (int) rec_len + left, dirent); |
ad4fa466 | 820 | return; |
e70ae99e TT |
821 | } |
822 | /* | |
575307cc KS |
823 | * If the record length of the directory entry is a multiple |
824 | * of four, and not too big, such that it is valid, let the | |
825 | * previous directory entry absorb the invalid one. | |
e70ae99e | 826 | */ |
5dd77dbe | 827 | if (prev && rec_len && (rec_len % 4) == 0 && |
82ad476d | 828 | (*offset + rec_len <= block_len)) { |
8a480350 TT |
829 | (void) ext2fs_get_rec_len(fs, prev, &prev_rec_len); |
830 | prev_rec_len += rec_len; | |
831 | (void) ext2fs_set_rec_len(fs, prev_rec_len, prev); | |
5dd77dbe | 832 | *offset += rec_len; |
ad4fa466 | 833 | return; |
e70ae99e TT |
834 | } |
835 | /* | |
836 | * Default salvage method --- kill all of the directory | |
837 | * entries for the rest of the block. We will either try to | |
838 | * absorb it into the previous directory entry, or create a | |
839 | * new empty directory entry the rest of the directory block. | |
840 | */ | |
841 | if (prev) { | |
8a480350 | 842 | (void) ext2fs_get_rec_len(fs, prev, &prev_rec_len); |
82ad476d | 843 | prev_rec_len += block_len - *offset; |
8a480350 | 844 | (void) ext2fs_set_rec_len(fs, prev_rec_len, prev); |
ad4fa466 | 845 | *offset = fs->blocksize; |
e70ae99e | 846 | } else { |
82ad476d | 847 | rec_len = block_len - *offset; |
8a480350 | 848 | (void) ext2fs_set_rec_len(fs, rec_len, dirent); |
70f4632b JK |
849 | ext2fs_dirent_set_name_len(dirent, 0); |
850 | ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); | |
e70ae99e | 851 | dirent->inode = 0; |
e70ae99e | 852 | } |
e70ae99e TT |
853 | } |
854 | ||
d3eb1502 | 855 | #define NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len)) |
17641bf2 DW |
856 | static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf) |
857 | { | |
858 | struct ext2_dir_entry *d; | |
859 | void *top; | |
860 | struct ext2_dir_entry_tail *t; | |
17641bf2 DW |
861 | |
862 | d = dirbuf; | |
863 | top = EXT2_DIRENT_TAIL(dirbuf, fs->blocksize); | |
864 | ||
d3eb1502 DW |
865 | while (d->rec_len && !(d->rec_len & 0x3) && NEXT_DIRENT(d) <= top) |
866 | d = NEXT_DIRENT(d); | |
17641bf2 DW |
867 | |
868 | if (d != top) { | |
62f9bd0e | 869 | unsigned int min_size = EXT2_DIR_REC_LEN( |
17641bf2 | 870 | ext2fs_dirent_name_len(dirbuf)); |
62f9bd0e | 871 | if (min_size > (char *)top - (char *)d) |
17641bf2 | 872 | return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; |
62f9bd0e | 873 | d->rec_len = (char *)top - (char *)d; |
17641bf2 DW |
874 | } |
875 | ||
876 | t = (struct ext2_dir_entry_tail *)top; | |
877 | if (t->det_reserved_zero1 || | |
878 | t->det_rec_len != sizeof(struct ext2_dir_entry_tail) || | |
879 | t->det_reserved_name_len != EXT2_DIR_NAME_LEN_CSUM) | |
880 | ext2fs_initialize_dirent_tail(fs, t); | |
881 | ||
882 | return 0; | |
883 | } | |
d3eb1502 | 884 | #undef NEXT_DIRENT |
17641bf2 | 885 | |
52b0c6e6 DW |
886 | static errcode_t fix_inline_dir_size(e2fsck_t ctx, ext2_ino_t ino, |
887 | size_t *inline_data_size, | |
888 | struct problem_context *pctx, | |
889 | char *buf) | |
890 | { | |
891 | ext2_filsys fs = ctx->fs; | |
892 | struct ext2_inode inode; | |
893 | size_t new_size, old_size; | |
894 | errcode_t retval; | |
895 | ||
896 | old_size = *inline_data_size; | |
0ac4b397 DW |
897 | /* |
898 | * If there's not enough bytes to start the "second" dir block | |
899 | * (in the EA space) then truncate everything to the first block. | |
900 | */ | |
901 | if (old_size > EXT4_MIN_INLINE_DATA_SIZE && | |
902 | old_size < EXT4_MIN_INLINE_DATA_SIZE + | |
903 | EXT2_DIR_REC_LEN(1)) { | |
904 | old_size = EXT4_MIN_INLINE_DATA_SIZE; | |
905 | new_size = old_size; | |
906 | } else | |
907 | /* Increase to the next four-byte boundary for salvaging */ | |
908 | new_size = old_size + (4 - (old_size & 3)); | |
52b0c6e6 DW |
909 | memset(buf + old_size, 0, new_size - old_size); |
910 | retval = ext2fs_inline_data_set(fs, ino, 0, buf, new_size); | |
911 | if (retval == EXT2_ET_INLINE_DATA_NO_SPACE) { | |
0ac4b397 | 912 | /* Or we can't, so truncate. */ |
52b0c6e6 DW |
913 | new_size -= 4; |
914 | retval = ext2fs_inline_data_set(fs, ino, 0, buf, new_size); | |
915 | if (retval) { | |
916 | if (fix_problem(ctx, PR_2_FIX_INLINE_DIR_FAILED, | |
917 | pctx)) { | |
918 | new_size = 0; | |
919 | goto write_inode; | |
920 | } | |
921 | goto err; | |
922 | } | |
923 | } else if (retval) { | |
924 | if (fix_problem(ctx, PR_2_FIX_INLINE_DIR_FAILED, | |
925 | pctx)) { | |
926 | new_size = 0; | |
927 | goto write_inode; | |
928 | } | |
929 | goto err; | |
930 | } | |
931 | ||
932 | write_inode: | |
933 | retval = ext2fs_read_inode(fs, ino, &inode); | |
934 | if (retval) | |
935 | goto err; | |
936 | ||
937 | retval = ext2fs_inode_size_set(fs, &inode, new_size); | |
938 | if (retval) | |
939 | goto err; | |
940 | if (new_size == 0) | |
941 | inode.i_flags &= ~EXT4_INLINE_DATA_FL; | |
942 | retval = ext2fs_write_inode(fs, ino, &inode); | |
943 | if (retval) | |
944 | goto err; | |
945 | *inline_data_size = new_size; | |
946 | ||
947 | err: | |
948 | return retval; | |
949 | } | |
950 | ||
2ba05753 EB |
951 | /* Return true if this type of file needs encryption */ |
952 | static int needs_encryption(e2fsck_t ctx, const struct ext2_dir_entry *dirent) | |
953 | { | |
954 | int filetype = ext2fs_dirent_file_type(dirent); | |
955 | ext2_ino_t ino = dirent->inode; | |
956 | struct ext2_inode inode; | |
957 | ||
958 | if (filetype != EXT2_FT_UNKNOWN) | |
959 | return filetype == EXT2_FT_REG_FILE || | |
960 | filetype == EXT2_FT_DIR || | |
961 | filetype == EXT2_FT_SYMLINK; | |
962 | ||
963 | if (ext2fs_test_inode_bitmap2(ctx->inode_reg_map, ino) || | |
964 | ext2fs_test_inode_bitmap2(ctx->inode_dir_map, ino)) | |
965 | return 1; | |
966 | ||
967 | e2fsck_read_inode(ctx, ino, &inode, "check_encryption_policy"); | |
968 | return LINUX_S_ISREG(inode.i_mode) || | |
969 | LINUX_S_ISDIR(inode.i_mode) || | |
970 | LINUX_S_ISLNK(inode.i_mode); | |
971 | } | |
972 | ||
973 | /* | |
974 | * All regular files, directories, and symlinks in encrypted directories must be | |
975 | * encrypted using the same encryption policy as their directory. | |
976 | * | |
977 | * Returns 1 if the dirent should be cleared, otherwise 0. | |
978 | */ | |
979 | static int check_encryption_policy(e2fsck_t ctx, | |
980 | const struct ext2_dir_entry *dirent, | |
981 | __u32 dir_encpolicy_id, | |
982 | struct problem_context *pctx) | |
983 | { | |
984 | __u32 file_encpolicy_id = find_encryption_policy(ctx, dirent->inode); | |
985 | ||
986 | /* Same policy or both UNRECOGNIZED_ENCRYPTION_POLICY? */ | |
987 | if (file_encpolicy_id == dir_encpolicy_id) | |
988 | return 0; | |
989 | ||
990 | if (file_encpolicy_id == NO_ENCRYPTION_POLICY) { | |
991 | if (!needs_encryption(ctx, dirent)) | |
992 | return 0; | |
993 | return fix_problem(ctx, PR_2_UNENCRYPTED_FILE, pctx); | |
994 | } | |
995 | ||
996 | return fix_problem(ctx, PR_2_INCONSISTENT_ENCRYPTION_POLICY, pctx); | |
997 | } | |
998 | ||
999 | /* | |
1000 | * Check an encrypted directory entry. | |
1001 | * | |
1002 | * Returns 1 if the dirent should be cleared, otherwise 0. | |
1003 | */ | |
1004 | static int check_encrypted_dirent(e2fsck_t ctx, | |
1005 | const struct ext2_dir_entry *dirent, | |
1006 | __u32 dir_encpolicy_id, | |
1007 | struct problem_context *pctx) | |
1008 | { | |
1009 | if (encrypted_check_name(ctx, dirent, pctx)) | |
1010 | return 1; | |
1011 | if (check_encryption_policy(ctx, dirent, dir_encpolicy_id, pctx)) | |
1012 | return 1; | |
1013 | return 0; | |
1014 | } | |
1015 | ||
a5abfe03 DW |
1016 | static int check_dir_block2(ext2_filsys fs, |
1017 | struct ext2_db_entry2 *db, | |
1018 | void *priv_data) | |
1019 | { | |
1020 | int err; | |
1021 | struct check_dir_struct *cd = priv_data; | |
1022 | ||
1023 | if (cd->ra_entries && cd->list_offset >= cd->next_ra_off) { | |
1024 | err = e2fsck_readahead_dblist(fs, | |
1025 | E2FSCK_RA_DBLIST_IGNORE_BLOCKCNT, | |
1026 | fs->dblist, | |
1027 | cd->list_offset + cd->ra_entries / 8, | |
1028 | cd->ra_entries); | |
1029 | if (err) | |
1030 | cd->ra_entries = 0; | |
1031 | cd->next_ra_off = cd->list_offset + (cd->ra_entries * 7 / 8); | |
1032 | } | |
1033 | ||
1034 | err = check_dir_block(fs, db, priv_data); | |
1035 | cd->list_offset++; | |
1036 | return err; | |
1037 | } | |
1038 | ||
3839e657 | 1039 | static int check_dir_block(ext2_filsys fs, |
6dc64392 | 1040 | struct ext2_db_entry2 *db, |
54dc7ca2 | 1041 | void *priv_data) |
3839e657 | 1042 | { |
8fdc9985 | 1043 | struct dx_dir_info *dx_dir; |
8fdc9985 | 1044 | struct dx_dirblock_info *dx_db = 0; |
6582dbe9 | 1045 | struct ext2_dir_entry *dirent, *prev, dot, dotdot; |
8fdc9985 | 1046 | ext2_dirhash_t hash; |
54434927 | 1047 | unsigned int offset = 0; |
3839e657 | 1048 | int dir_modified = 0; |
21c84b71 | 1049 | int dot_state; |
03fa6f8a | 1050 | unsigned int rec_len; |
6dc64392 | 1051 | blk64_t block_nr = db->blk; |
86c627ec | 1052 | ext2_ino_t ino = db->ino; |
28db82a8 | 1053 | ext2_ino_t subdir_parent; |
21c84b71 | 1054 | __u16 links; |
54dc7ca2 | 1055 | struct check_dir_struct *cd; |
0ac4b397 | 1056 | char *buf, *ibuf; |
1b6bf175 | 1057 | e2fsck_t ctx; |
3c7c6d73 | 1058 | problem_t problem; |
ea1959f0 | 1059 | struct ext2_dx_root_info *root; |
e8254bfd | 1060 | struct ext2_dx_countlimit *limit; |
0926668d TT |
1061 | static dict_t de_dict; |
1062 | struct problem_context pctx; | |
1063 | int dups_found = 0; | |
28db82a8 | 1064 | int ret; |
e8548796 | 1065 | int dx_csum_size = 0, de_csum_size = 0; |
81683c6a | 1066 | int failed_csum = 0; |
e8548796 | 1067 | int is_leaf = 1; |
24997f1c | 1068 | size_t inline_data_size = 0; |
6582dbe9 | 1069 | int filetype = 0; |
2ba05753 | 1070 | __u32 dir_encpolicy_id = NO_ENCRYPTION_POLICY; |
61421ee5 DR |
1071 | int hash_in_dirent = 0; |
1072 | int casefolded = 0; | |
0ac4b397 | 1073 | size_t max_block_size; |
28b44ef0 | 1074 | int hash_flags = 0; |
c4e73242 | 1075 | static char *eop_read_dirblock = NULL; |
06b83bbd | 1076 | int cf_dir = 0; |
1b6bf175 | 1077 | |
54dc7ca2 | 1078 | cd = (struct check_dir_struct *) priv_data; |
0ac4b397 | 1079 | ibuf = buf = cd->buf; |
1b6bf175 | 1080 | ctx = cd->ctx; |
f8188fff | 1081 | |
06b83bbd | 1082 | /* We only want filename encoding verification on strict |
1e6a3a32 | 1083 | * mode or if explicitly requested by user. */ |
06b83bbd | 1084 | if (ext2fs_test_inode_bitmap2(ctx->inode_casefold_map, ino) && |
1e6a3a32 GKB |
1085 | ((ctx->fs->super->s_encoding_flags & EXT4_ENC_STRICT_MODE_FL) || |
1086 | (ctx->options & E2F_OPT_CHECK_ENCODING))) | |
06b83bbd GKB |
1087 | cf_dir = 1; |
1088 | ||
db3d8718 | 1089 | if (ctx->flags & E2F_FLAG_RUN_RETURN) |
4cae0452 | 1090 | return DIRENT_ABORT; |
efc6f628 | 1091 | |
4cae0452 TT |
1092 | if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) |
1093 | return DIRENT_ABORT; | |
efc6f628 | 1094 | |
86f3b6cf | 1095 | if (ext2fs_has_feature_metadata_csum(fs->super)) { |
07307114 | 1096 | dx_csum_size = sizeof(struct ext2_dx_tail); |
e8548796 DW |
1097 | de_csum_size = sizeof(struct ext2_dir_entry_tail); |
1098 | } | |
07307114 | 1099 | |
86f3b6cf | 1100 | if (ext2fs_has_feature_filetype(fs->super)) |
6582dbe9 ZL |
1101 | filetype = EXT2_FT_DIR << 8; |
1102 | ||
3839e657 | 1103 | /* |
efc6f628 | 1104 | * Make sure the inode is still in use (could have been |
3839e657 TT |
1105 | * deleted in the duplicate/bad blocks pass. |
1106 | */ | |
c5d2f50d | 1107 | if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, ino))) |
3839e657 | 1108 | return 0; |
50e1e10f | 1109 | |
21c84b71 TT |
1110 | cd->pctx.ino = ino; |
1111 | cd->pctx.blk = block_nr; | |
1112 | cd->pctx.blkcount = db->blockcnt; | |
1113 | cd->pctx.ino2 = 0; | |
1114 | cd->pctx.dirent = 0; | |
1115 | cd->pctx.num = 0; | |
1116 | ||
86f3b6cf | 1117 | if (ext2fs_has_feature_inline_data(fs->super)) { |
6582dbe9 ZL |
1118 | errcode_t ec; |
1119 | ||
1120 | ec = ext2fs_inline_data_size(fs, ino, &inline_data_size); | |
1121 | if (ec && ec != EXT2_ET_NO_INLINE_DATA) | |
1122 | return DIRENT_ABORT; | |
1123 | } | |
1124 | ||
ae9efd05 AB |
1125 | /* This will allow (at some point in the future) to punch out empty |
1126 | * directory blocks and reduce the space used by a directory that grows | |
1127 | * very large and then the files are deleted. For now, all that is | |
1128 | * needed is to avoid e2fsck filling in these holes as part of | |
1129 | * feature flag. */ | |
10c5c5e5 AB |
1130 | if (db->blk == 0 && ext2fs_has_feature_largedir(fs->super) && |
1131 | !ext2fs_has_feature_inline_data(fs->super)) | |
ae9efd05 AB |
1132 | return 0; |
1133 | ||
6582dbe9 | 1134 | if (db->blk == 0 && !inline_data_size) { |
1b6bf175 | 1135 | if (allocate_dir_block(ctx, db, buf, &cd->pctx)) |
50e1e10f TT |
1136 | return 0; |
1137 | block_nr = db->blk; | |
1138 | } | |
efc6f628 | 1139 | |
3839e657 TT |
1140 | if (db->blockcnt) |
1141 | dot_state = 2; | |
1142 | else | |
1143 | dot_state = 0; | |
1144 | ||
0926668d TT |
1145 | if (ctx->dirs_to_hash && |
1146 | ext2fs_u32_list_test(ctx->dirs_to_hash, ino)) | |
1147 | dups_found++; | |
1148 | ||
3839e657 | 1149 | #if 0 |
f3db3566 | 1150 | printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr, |
3839e657 TT |
1151 | db->blockcnt, ino); |
1152 | #endif | |
efc6f628 | 1153 | |
c4e73242 TT |
1154 | if (!eop_read_dirblock) |
1155 | eop_read_dirblock = (char *) _("reading directory block"); | |
1156 | ehandler_operation(eop_read_dirblock); | |
cd971869 | 1157 | if (inline_data_size) { |
4a3dc1f0 | 1158 | memset(buf, 0, fs->blocksize - inline_data_size); |
6582dbe9 | 1159 | cd->pctx.errcode = ext2fs_inline_data_get(fs, ino, 0, buf, 0); |
cd971869 DW |
1160 | if (cd->pctx.errcode) |
1161 | goto inline_read_fail; | |
1162 | #ifdef WORDS_BIGENDIAN | |
0ac4b397 DW |
1163 | if (db->blockcnt) |
1164 | goto skip_first_read_swab; | |
cd971869 DW |
1165 | *((__u32 *)buf) = ext2fs_le32_to_cpu(*((__u32 *)buf)); |
1166 | cd->pctx.errcode = ext2fs_dirent_swab_in2(fs, | |
1167 | buf + EXT4_INLINE_DATA_DOTDOT_SIZE, | |
0ac4b397 DW |
1168 | EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE, |
1169 | 0); | |
1170 | if (cd->pctx.errcode) | |
1171 | goto inline_read_fail; | |
1172 | skip_first_read_swab: | |
1173 | if (inline_data_size <= EXT4_MIN_INLINE_DATA_SIZE || | |
1174 | !db->blockcnt) | |
1175 | goto inline_read_fail; | |
1176 | cd->pctx.errcode = ext2fs_dirent_swab_in2(fs, | |
1177 | buf + EXT4_MIN_INLINE_DATA_SIZE, | |
1178 | inline_data_size - EXT4_MIN_INLINE_DATA_SIZE, | |
cd971869 DW |
1179 | 0); |
1180 | #endif | |
1181 | } else | |
6582dbe9 ZL |
1182 | cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, |
1183 | buf, 0, ino); | |
cd971869 | 1184 | inline_read_fail: |
52b0c6e6 DW |
1185 | pctx.ino = ino; |
1186 | pctx.num = inline_data_size; | |
0ac4b397 DW |
1187 | if (((inline_data_size & 3) || |
1188 | (inline_data_size > EXT4_MIN_INLINE_DATA_SIZE && | |
1189 | inline_data_size < EXT4_MIN_INLINE_DATA_SIZE + | |
1190 | EXT2_DIR_REC_LEN(1))) && | |
52b0c6e6 DW |
1191 | fix_problem(ctx, PR_2_BAD_INLINE_DIR_SIZE, &pctx)) { |
1192 | errcode_t err = fix_inline_dir_size(ctx, ino, | |
1193 | &inline_data_size, &pctx, | |
1194 | buf); | |
1195 | if (err) | |
1196 | return DIRENT_ABORT; | |
1197 | ||
1198 | } | |
e94bc631 | 1199 | ehandler_operation(0); |
b9852cd8 TT |
1200 | if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) |
1201 | cd->pctx.errcode = 0; /* We'll handle this ourselves */ | |
81683c6a DW |
1202 | else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) { |
1203 | cd->pctx.errcode = 0; /* We'll handle this ourselves */ | |
1204 | failed_csum = 1; | |
1205 | } | |
1b6bf175 | 1206 | if (cd->pctx.errcode) { |
e8548796 | 1207 | char *buf2; |
08b21301 TT |
1208 | if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { |
1209 | ctx->flags |= E2F_FLAG_ABORT; | |
1210 | return DIRENT_ABORT; | |
1211 | } | |
e8548796 DW |
1212 | ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0, |
1213 | EXT2_ROOT_INO, &buf2); | |
1214 | memcpy(buf, buf2, fs->blocksize); | |
1215 | ext2fs_free_mem(&buf2); | |
3839e657 | 1216 | } |
8fdc9985 | 1217 | dx_dir = e2fsck_get_dx_dir_info(ctx, ino); |
62acaa1d | 1218 | if (dx_dir && dx_dir->numblocks) { |
8fdc9985 | 1219 | if (db->blockcnt >= dx_dir->numblocks) { |
ea9085c7 | 1220 | pctx.dir = ino; |
efc6f628 | 1221 | if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK, |
d45edec0 TT |
1222 | &pctx)) { |
1223 | clear_htree(ctx, ino); | |
1224 | dx_dir->numblocks = 0; | |
1225 | dx_db = 0; | |
1226 | goto out_htree; | |
1227 | } | |
1228 | fatal_error(ctx, _("Can not continue.")); | |
8fdc9985 TT |
1229 | } |
1230 | dx_db = &dx_dir->dx_block[db->blockcnt]; | |
1231 | dx_db->type = DX_DIRBLOCK_LEAF; | |
1232 | dx_db->phys = block_nr; | |
1233 | dx_db->min_hash = ~0; | |
1234 | dx_db->max_hash = 0; | |
efc6f628 | 1235 | |
8fdc9985 | 1236 | dirent = (struct ext2_dir_entry *) buf; |
8a480350 | 1237 | (void) ext2fs_get_rec_len(fs, dirent, &rec_len); |
e8254bfd | 1238 | limit = (struct ext2_dx_countlimit *) (buf+8); |
8fdc9985 | 1239 | if (db->blockcnt == 0) { |
ea1959f0 | 1240 | root = (struct ext2_dx_root_info *) (buf + 24); |
8fdc9985 TT |
1241 | dx_db->type = DX_DIRBLOCK_ROOT; |
1242 | dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST; | |
f15b1aaf AD |
1243 | |
1244 | /* large_dir was set in pass1 if large dirs were found, | |
1245 | * so ext2_dir_htree_level() should now be correct */ | |
ea1959f0 TT |
1246 | if ((root->reserved_zero || |
1247 | root->info_length < 8 || | |
3f0cf647 AB |
1248 | root->indirect_levels >= |
1249 | ext2_dir_htree_level(fs)) && | |
ea1959f0 TT |
1250 | fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) { |
1251 | clear_htree(ctx, ino); | |
1252 | dx_dir->numblocks = 0; | |
354545a7 | 1253 | dx_db = NULL; |
f77704e4 | 1254 | } |
ea1959f0 | 1255 | dx_dir->hashversion = root->hash_version; |
f77704e4 TT |
1256 | if ((dx_dir->hashversion <= EXT2_HASH_TEA) && |
1257 | (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)) | |
1258 | dx_dir->hashversion += 3; | |
ad4fa466 | 1259 | dx_dir->depth = root->indirect_levels + 1; |
8fdc9985 | 1260 | } else if ((dirent->inode == 0) && |
5dd77dbe | 1261 | (rec_len == fs->blocksize) && |
70f4632b | 1262 | (ext2fs_dirent_name_len(dirent) == 0) && |
efc6f628 | 1263 | (ext2fs_le16_to_cpu(limit->limit) == |
07307114 | 1264 | ((fs->blocksize - (8 + dx_csum_size)) / |
354545a7 | 1265 | sizeof(struct ext2_dx_entry)))) { |
8fdc9985 | 1266 | dx_db->type = DX_DIRBLOCK_NODE; |
354545a7 AB |
1267 | } |
1268 | is_leaf = dx_db ? (dx_db->type == DX_DIRBLOCK_LEAF) : 0; | |
8fdc9985 | 1269 | } |
d45edec0 | 1270 | out_htree: |
3839e657 | 1271 | |
7f43a46f DW |
1272 | /* Leaf node with no space for csum? Rebuild dirs in pass 3A. */ |
1273 | if (is_leaf && !inline_data_size && failed_csum && | |
1274 | !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) { | |
1275 | de_csum_size = 0; | |
d02d0195 DW |
1276 | if (e2fsck_dir_will_be_rehashed(ctx, ino)) { |
1277 | failed_csum = 0; | |
1278 | goto skip_checksum; | |
1279 | } | |
1280 | if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM, | |
7f43a46f | 1281 | &cd->pctx)) |
e8548796 | 1282 | goto skip_checksum; |
7f43a46f | 1283 | e2fsck_rehash_dir_later(ctx, ino); |
d02d0195 | 1284 | failed_csum = 0; |
7f43a46f | 1285 | goto skip_checksum; |
e8548796 DW |
1286 | } |
1287 | /* htree nodes don't use fake dirents to store checksums */ | |
1288 | if (!is_leaf) | |
1289 | de_csum_size = 0; | |
1290 | ||
1291 | skip_checksum: | |
0ac4b397 DW |
1292 | if (inline_data_size) { |
1293 | if (db->blockcnt) { | |
1294 | buf += EXT4_MIN_INLINE_DATA_SIZE; | |
1295 | max_block_size = inline_data_size - EXT4_MIN_INLINE_DATA_SIZE; | |
1296 | /* Zero-length second block, just exit */ | |
1297 | if (max_block_size == 0) | |
1298 | return 0; | |
1299 | } else { | |
1300 | max_block_size = EXT4_MIN_INLINE_DATA_SIZE; | |
1301 | } | |
1302 | } else | |
1303 | max_block_size = fs->blocksize - de_csum_size; | |
1304 | ||
2ba05753 | 1305 | dir_encpolicy_id = find_encryption_policy(ctx, ino); |
62ad2480 | 1306 | |
782df523 GKB |
1307 | if (cf_dir) { |
1308 | dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cf_cmp); | |
f158f896 | 1309 | dict_set_cmp_context(&de_dict, (const void *)ctx->fs->encoding); |
782df523 GKB |
1310 | } else { |
1311 | dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); | |
1312 | } | |
61421ee5 DR |
1313 | if (ctx->casefolded_dirs) |
1314 | casefolded = ext2fs_u32_list_test(ctx->casefolded_dirs, ino); | |
6be3ce7a TT |
1315 | hash_in_dirent = (casefolded && |
1316 | (dir_encpolicy_id != NO_ENCRYPTION_POLICY)); | |
782df523 | 1317 | |
e70ae99e | 1318 | prev = 0; |
3839e657 | 1319 | do { |
3971bfe8 | 1320 | dgrp_t group; |
49a7360b | 1321 | ext2_ino_t first_unused_inode; |
70f4632b | 1322 | unsigned int name_len; |
61421ee5 DR |
1323 | /* csum entry is not checked here, so don't worry about it */ |
1324 | int extended = (dot_state > 1) && hash_in_dirent; | |
f158f896 | 1325 | unsigned int min_dir_len = ext2fs_dir_rec_len(1, extended); |
49a7360b | 1326 | |
1b6bf175 | 1327 | problem = 0; |
6582dbe9 ZL |
1328 | if (!inline_data_size || dot_state > 1) { |
1329 | dirent = (struct ext2_dir_entry *) (buf + offset); | |
4a3dc1f0 DW |
1330 | /* |
1331 | * If there's not even space for the entry header, | |
1332 | * force salvaging this dir. | |
1333 | */ | |
1334 | if (max_block_size - offset < EXT2_DIR_ENTRY_HEADER_LEN) | |
61421ee5 | 1335 | rec_len = ext2fs_dir_rec_len(1, extended); |
4a3dc1f0 DW |
1336 | else |
1337 | (void) ext2fs_get_rec_len(fs, dirent, &rec_len); | |
6582dbe9 ZL |
1338 | cd->pctx.dirent = dirent; |
1339 | cd->pctx.num = offset; | |
4a3dc1f0 | 1340 | if ((offset + rec_len > max_block_size) || |
61421ee5 | 1341 | (rec_len < min_dir_len) || |
6582dbe9 | 1342 | ((rec_len % 4) != 0) || |
61421ee5 | 1343 | ((ext2fs_dir_rec_len(ext2fs_dirent_name_len(dirent), |
79a7b5e1 TT |
1344 | extended)) > rec_len)) |
1345 | problem = PR_2_DIR_CORRUPTED; | |
1346 | if (problem) { | |
1347 | if ((offset == 0) && | |
1348 | (rec_len == fs->blocksize) && | |
1349 | (dirent->inode == 0) && | |
1350 | e2fsck_dir_will_be_rehashed(ctx, ino)) { | |
1351 | problem = 0; | |
1352 | max_block_size = fs->blocksize; | |
1353 | } | |
1354 | } | |
1355 | if (problem) { | |
4348709c | 1356 | #ifdef WORDS_BIGENDIAN |
2c280578 | 1357 | int need_reswab = 0; |
4348709c | 1358 | #endif |
2c280578 TT |
1359 | |
1360 | if (!fix_problem(ctx, PR_2_DIR_CORRUPTED, | |
1361 | &cd->pctx)) | |
1362 | goto abort_free_dict; | |
4348709c | 1363 | #ifdef WORDS_BIGENDIAN |
2c280578 TT |
1364 | /* |
1365 | * On big-endian systems, if the dirent | |
1366 | * swap routine finds a rec_len that it | |
1367 | * doesn't like, it continues processing | |
1368 | * the block as if rec_len == | |
1369 | * EXT2_DIR_ENTRY_HEADER_LEN. This means | |
1370 | * that the name field gets byte swapped, | |
1371 | * which means that salvage will not detect | |
1372 | * the correct name length (unless the name | |
1373 | * has a length that's an exact multiple of | |
1374 | * four bytes), and it'll discard the entry | |
1375 | * (unnecessarily) and the rest of the | |
1376 | * dirent block. Therefore, swap the rest | |
1377 | * of the block back to disk order, run | |
1378 | * salvage, and re-swap anything after the | |
1379 | * salvaged dirent. | |
1380 | */ | |
1381 | if (rec_len < EXT2_DIR_ENTRY_HEADER_LEN || | |
1382 | rec_len % 4) { | |
1383 | need_reswab = 1; | |
1384 | ext2fs_dirent_swab_in2(fs, | |
1385 | ((char *)dirent) + EXT2_DIR_ENTRY_HEADER_LEN, | |
1386 | max_block_size - offset - EXT2_DIR_ENTRY_HEADER_LEN, 0); | |
1387 | } | |
4348709c | 1388 | #endif |
2c280578 TT |
1389 | salvage_directory(fs, dirent, prev, &offset, |
1390 | max_block_size, | |
1391 | hash_in_dirent); | |
1392 | #ifdef WORDS_BIGENDIAN | |
1393 | if (need_reswab) { | |
1394 | unsigned int len; | |
1395 | ||
1396 | (void) ext2fs_get_rec_len(fs, dirent, | |
1397 | &len); | |
1398 | len += offset; | |
1399 | if (max_block_size > len) | |
1400 | ext2fs_dirent_swab_in2(fs, | |
1401 | ((char *)dirent) + len, max_block_size - len, 0); | |
1402 | } | |
1403 | #endif | |
1404 | dir_modified++; | |
1405 | continue; | |
6582dbe9 ZL |
1406 | } |
1407 | } else { | |
1408 | if (dot_state == 0) { | |
1409 | memset(&dot, 0, sizeof(dot)); | |
1410 | dirent = ˙ | |
1411 | dirent->inode = ino; | |
1412 | dirent->rec_len = EXT2_DIR_REC_LEN(1); | |
1413 | dirent->name_len = 1 | filetype; | |
1414 | dirent->name[0] = '.'; | |
1415 | } else if (dot_state == 1) { | |
1416 | memset(&dotdot, 0, sizeof(dotdot)); | |
1417 | dirent = &dotdot; | |
1418 | dirent->inode = | |
1419 | ((struct ext2_dir_entry *)buf)->inode; | |
1420 | dirent->rec_len = EXT2_DIR_REC_LEN(2); | |
1421 | dirent->name_len = 2 | filetype; | |
1422 | dirent->name[0] = '.'; | |
1423 | dirent->name[1] = '.'; | |
1424 | } else { | |
1425 | fatal_error(ctx, _("Can not continue.")); | |
1426 | } | |
1427 | cd->pctx.dirent = dirent; | |
1428 | cd->pctx.num = offset; | |
3839e657 | 1429 | } |
50e1e10f | 1430 | |
e70ae99e | 1431 | if (dot_state == 0) { |
1b6bf175 | 1432 | if (check_dot(ctx, dirent, ino, &cd->pctx)) |
3839e657 | 1433 | dir_modified++; |
e70ae99e | 1434 | } else if (dot_state == 1) { |
28db82a8 TT |
1435 | ret = check_dotdot(ctx, dirent, ino, &cd->pctx); |
1436 | if (ret < 0) | |
0926668d | 1437 | goto abort_free_dict; |
28db82a8 | 1438 | if (ret) |
3839e657 TT |
1439 | dir_modified++; |
1440 | } else if (dirent->inode == ino) { | |
1b6bf175 TT |
1441 | problem = PR_2_LINK_DOT; |
1442 | if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { | |
3839e657 TT |
1443 | dirent->inode = 0; |
1444 | dir_modified++; | |
21c84b71 | 1445 | goto next; |
3839e657 TT |
1446 | } |
1447 | } | |
efc6f628 | 1448 | if (!dirent->inode) |
3839e657 | 1449 | goto next; |
efc6f628 | 1450 | |
3839e657 TT |
1451 | /* |
1452 | * Make sure the inode listed is a legal one. | |
efc6f628 | 1453 | */ |
70f4632b | 1454 | name_len = ext2fs_dirent_name_len(dirent); |
3839e657 | 1455 | if (((dirent->inode != EXT2_ROOT_INO) && |
7f88b043 | 1456 | (dirent->inode < EXT2_FIRST_INODE(fs->super))) || |
dc28833c JK |
1457 | (dirent->inode > fs->super->s_inodes_count) || |
1458 | (dirent->inode == fs->super->s_usr_quota_inum) || | |
1459 | (dirent->inode == fs->super->s_grp_quota_inum) || | |
d0c52ffb JK |
1460 | (dirent->inode == fs->super->s_prj_quota_inum) || |
1461 | (dirent->inode == fs->super->s_orphan_file_inum)) { | |
1b6bf175 | 1462 | problem = PR_2_BAD_INO; |
1b6bf175 | 1463 | } else if (ctx->inode_bb_map && |
c5d2f50d | 1464 | (ext2fs_test_inode_bitmap2(ctx->inode_bb_map, |
1b6bf175 TT |
1465 | dirent->inode))) { |
1466 | /* | |
1467 | * If the inode is in a bad block, offer to | |
1468 | * clear it. | |
1469 | */ | |
1470 | problem = PR_2_BB_INODE; | |
70f4632b | 1471 | } else if ((dot_state > 1) && (name_len == 1) && |
1b6bf175 TT |
1472 | (dirent->name[0] == '.')) { |
1473 | /* | |
1474 | * If there's a '.' entry in anything other | |
1475 | * than the first directory entry, it's a | |
1476 | * duplicate entry that should be removed. | |
1477 | */ | |
1478 | problem = PR_2_DUP_DOT; | |
70f4632b | 1479 | } else if ((dot_state > 1) && (name_len == 2) && |
efc6f628 | 1480 | (dirent->name[0] == '.') && |
1b6bf175 TT |
1481 | (dirent->name[1] == '.')) { |
1482 | /* | |
1483 | * If there's a '..' entry in anything other | |
1484 | * than the second directory entry, it's a | |
1485 | * duplicate entry that should be removed. | |
1486 | */ | |
1487 | problem = PR_2_DUP_DOT_DOT; | |
e70ae99e | 1488 | } else if ((dot_state > 1) && |
1b6bf175 TT |
1489 | (dirent->inode == EXT2_ROOT_INO)) { |
1490 | /* | |
1491 | * Don't allow links to the root directory. | |
1492 | * We check this specially to make sure we | |
1493 | * catch this error case even if the root | |
1494 | * directory hasn't been created yet. | |
1495 | */ | |
1496 | problem = PR_2_LINK_ROOT; | |
70f4632b | 1497 | } else if ((dot_state > 1) && (name_len == 0)) { |
c40db6d5 TT |
1498 | /* |
1499 | * Don't allow zero-length directory names. | |
1500 | */ | |
1501 | problem = PR_2_NULL_NAME; | |
21c84b71 TT |
1502 | } |
1503 | ||
1b6bf175 TT |
1504 | if (problem) { |
1505 | if (fix_problem(ctx, problem, &cd->pctx)) { | |
21c84b71 TT |
1506 | dirent->inode = 0; |
1507 | dir_modified++; | |
1508 | goto next; | |
1b6bf175 TT |
1509 | } else { |
1510 | ext2fs_unmark_valid(fs); | |
1511 | if (problem == PR_2_BAD_INO) | |
1512 | goto next; | |
21c84b71 | 1513 | } |
3839e657 TT |
1514 | } |
1515 | ||
1516 | /* | |
1517 | * If the inode was marked as having bad fields in | |
1518 | * pass1, process it and offer to fix/clear it. | |
1519 | * (We wait until now so that we can display the | |
1520 | * pathname to the user.) | |
1521 | */ | |
1b6bf175 | 1522 | if (ctx->inode_bad_map && |
c5d2f50d | 1523 | ext2fs_test_inode_bitmap2(ctx->inode_bad_map, |
3839e657 | 1524 | dirent->inode)) { |
e72a9ba3 | 1525 | if (e2fsck_process_bad_inode(ctx, ino, |
bcf9c5d4 | 1526 | dirent->inode, |
8798bbb8 | 1527 | cd->buf + fs->blocksize)) { |
3839e657 TT |
1528 | dirent->inode = 0; |
1529 | dir_modified++; | |
1530 | goto next; | |
1531 | } | |
a02ce9df | 1532 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 1533 | return DIRENT_ABORT; |
3839e657 TT |
1534 | } |
1535 | ||
49a7360b JS |
1536 | group = ext2fs_group_of_ino(fs, dirent->inode); |
1537 | first_unused_inode = group * fs->super->s_inodes_per_group + | |
1538 | 1 + fs->super->s_inodes_per_group - | |
d7cca6b0 | 1539 | ext2fs_bg_itable_unused(fs, group); |
49a7360b JS |
1540 | cd->pctx.group = group; |
1541 | ||
1542 | /* | |
42e89ce7 TT |
1543 | * Check if the inode was missed out because |
1544 | * _INODE_UNINIT flag was set or bg_itable_unused was | |
1545 | * incorrect. If so, clear the _INODE_UNINIT flag and | |
1546 | * restart e2fsck. In the future it would be nice if | |
1547 | * we could call a function in pass1.c that checks the | |
1548 | * newly visible inodes. | |
49a7360b | 1549 | */ |
cd65a24e | 1550 | if (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)) { |
6267ee49 | 1551 | pctx.num = dirent->inode; |
49a7360b JS |
1552 | if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT, |
1553 | &cd->pctx)){ | |
e633b58a | 1554 | ext2fs_bg_flags_clear(fs, group, |
732c8cd5 | 1555 | EXT2_BG_INODE_UNINIT); |
755e1da8 | 1556 | ext2fs_group_desc_csum_set(fs, group); |
42e89ce7 | 1557 | ext2fs_mark_super_dirty(fs); |
6267ee49 | 1558 | ctx->flags |= E2F_FLAG_RESTART_LATER; |
49a7360b JS |
1559 | } else { |
1560 | ext2fs_unmark_valid(fs); | |
1561 | if (problem == PR_2_BAD_INO) | |
1562 | goto next; | |
1563 | } | |
1564 | } else if (dirent->inode >= first_unused_inode) { | |
6267ee49 | 1565 | pctx.num = dirent->inode; |
49a7360b | 1566 | if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){ |
d7cca6b0 | 1567 | ext2fs_bg_itable_unused_set(fs, group, 0); |
755e1da8 | 1568 | ext2fs_group_desc_csum_set(fs, group); |
49a7360b | 1569 | ext2fs_mark_super_dirty(fs); |
6267ee49 | 1570 | ctx->flags |= E2F_FLAG_RESTART_LATER; |
49a7360b JS |
1571 | } else { |
1572 | ext2fs_unmark_valid(fs); | |
1573 | if (problem == PR_2_BAD_INO) | |
1574 | goto next; | |
1575 | } | |
1576 | } | |
1577 | ||
0433c1f1 TT |
1578 | /* |
1579 | * Offer to clear unused inodes; if we are going to be | |
1580 | * restarting the scan due to bg_itable_unused being | |
1581 | * wrong, then don't clear any inodes to avoid zapping | |
1582 | * inodes that were skipped during pass1 due to an | |
1583 | * incorrect bg_itable_unused; we'll get any real | |
1584 | * problems after we restart. | |
1585 | */ | |
1586 | if (!(ctx->flags & E2F_FLAG_RESTART_LATER) && | |
97d26ce9 | 1587 | !(ext2fs_test_inode_bitmap2(ctx->inode_used_map, |
79a7b5e1 TT |
1588 | dirent->inode)) |
1589 | ) | |
49a7360b | 1590 | problem = PR_2_UNUSED_INODE; |
49a7360b JS |
1591 | |
1592 | if (problem) { | |
1593 | if (fix_problem(ctx, problem, &cd->pctx)) { | |
1594 | dirent->inode = 0; | |
1595 | dir_modified++; | |
1596 | goto next; | |
1597 | } else { | |
1598 | ext2fs_unmark_valid(fs); | |
1599 | if (problem == PR_2_BAD_INO) | |
1600 | goto next; | |
1601 | } | |
1602 | } | |
1603 | ||
2ba05753 | 1604 | if (check_filetype(ctx, dirent, ino, &cd->pctx)) |
1b6bf175 TT |
1605 | dir_modified++; |
1606 | ||
06b83bbd | 1607 | if (dir_encpolicy_id != NO_ENCRYPTION_POLICY) { |
2ba05753 EB |
1608 | /* Encrypted directory */ |
1609 | if (dot_state > 1 && | |
1610 | check_encrypted_dirent(ctx, dirent, | |
1611 | dir_encpolicy_id, | |
1612 | &cd->pctx)) { | |
1613 | dirent->inode = 0; | |
1614 | dir_modified++; | |
1615 | goto next; | |
1616 | } | |
06b83bbd GKB |
1617 | } else if (cf_dir) { |
1618 | /* Casefolded directory */ | |
1619 | if (encoded_check_name(ctx, dirent, &cd->pctx)) | |
1620 | dir_modified++; | |
1621 | } else { | |
1622 | /* Unencrypted and uncasefolded directory */ | |
1623 | if (check_name(ctx, dirent, &cd->pctx)) | |
1624 | dir_modified++; | |
e3dd5c6f TT |
1625 | } |
1626 | ||
8fdc9985 | 1627 | if (dx_db) { |
28b44ef0 GKB |
1628 | if (dx_dir->casefolded_hash) |
1629 | hash_flags = EXT4_CASEFOLD_FL; | |
1630 | ||
61421ee5 DR |
1631 | if (dx_dir->hashversion == EXT2_HASH_SIPHASH) { |
1632 | if (dot_state > 1) | |
1633 | hash = EXT2_DIRENT_HASH(dirent); | |
1634 | } else { | |
1635 | ext2fs_dirhash2(dx_dir->hashversion, | |
1636 | dirent->name, | |
1637 | ext2fs_dirent_name_len(dirent), | |
1638 | fs->encoding, hash_flags, | |
1639 | fs->super->s_hash_seed, | |
1640 | &hash, 0); | |
1641 | } | |
8fdc9985 TT |
1642 | if (hash < dx_db->min_hash) |
1643 | dx_db->min_hash = hash; | |
1644 | if (hash > dx_db->max_hash) | |
1645 | dx_db->max_hash = hash; | |
1646 | } | |
8fdc9985 | 1647 | |
3839e657 TT |
1648 | /* |
1649 | * If this is a directory, then mark its parent in its | |
1650 | * dir_info structure. If the parent field is already | |
1651 | * filled in, then this directory has more than one | |
1652 | * hard link. We assume the first link is correct, | |
1653 | * and ask the user if he/she wants to clear this one. | |
1654 | */ | |
e70ae99e | 1655 | if ((dot_state > 1) && |
c5d2f50d | 1656 | (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, |
3839e657 | 1657 | dirent->inode))) { |
28db82a8 TT |
1658 | if (e2fsck_dir_info_get_parent(ctx, dirent->inode, |
1659 | &subdir_parent)) { | |
1b6bf175 TT |
1660 | cd->pctx.ino = dirent->inode; |
1661 | fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); | |
0926668d | 1662 | goto abort_free_dict; |
3839e657 | 1663 | } |
28db82a8 TT |
1664 | if (subdir_parent) { |
1665 | cd->pctx.ino2 = subdir_parent; | |
1b6bf175 | 1666 | if (fix_problem(ctx, PR_2_LINK_DIR, |
21c84b71 | 1667 | &cd->pctx)) { |
3839e657 TT |
1668 | dirent->inode = 0; |
1669 | dir_modified++; | |
1670 | goto next; | |
21c84b71 TT |
1671 | } |
1672 | cd->pctx.ino2 = 0; | |
28db82a8 | 1673 | } else { |
efc6f628 | 1674 | (void) e2fsck_dir_info_set_parent(ctx, |
28db82a8 TT |
1675 | dirent->inode, ino); |
1676 | } | |
3839e657 | 1677 | } |
0926668d TT |
1678 | |
1679 | if (dups_found) { | |
1680 | ; | |
1681 | } else if (dict_lookup(&de_dict, dirent)) { | |
1682 | clear_problem_context(&pctx); | |
1683 | pctx.ino = ino; | |
1684 | pctx.dirent = dirent; | |
1685 | fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); | |
e8548796 | 1686 | e2fsck_rehash_dir_later(ctx, ino); |
0926668d TT |
1687 | dups_found++; |
1688 | } else | |
1689 | dict_alloc_insert(&de_dict, dirent, dirent); | |
efc6f628 | 1690 | |
1b6bf175 TT |
1691 | ext2fs_icount_increment(ctx->inode_count, dirent->inode, |
1692 | &links); | |
21c84b71 | 1693 | if (links > 1) |
1b6bf175 TT |
1694 | ctx->fs_links_count++; |
1695 | ctx->fs_total_count++; | |
3839e657 | 1696 | next: |
e70ae99e | 1697 | prev = dirent; |
5dd77dbe | 1698 | if (dir_modified) |
8a480350 | 1699 | (void) ext2fs_get_rec_len(fs, dirent, &rec_len); |
6582dbe9 ZL |
1700 | if (!inline_data_size || dot_state > 1) { |
1701 | offset += rec_len; | |
1702 | } else { | |
6698374c | 1703 | if (dot_state == 1) { |
6582dbe9 | 1704 | offset = 4; |
6698374c DW |
1705 | /* |
1706 | * If we get here, we're checking an inline | |
1707 | * directory and we've just checked a (fake) | |
1708 | * dotdot entry that we created on the stack. | |
1709 | * Therefore set 'prev' to NULL so that if we | |
1710 | * call salvage_directory on the next entry, | |
1711 | * it won't try to absorb the next entry into | |
1712 | * the on-stack dotdot entry. | |
1713 | */ | |
1714 | prev = NULL; | |
1715 | } | |
6582dbe9 | 1716 | } |
e70ae99e | 1717 | dot_state++; |
0ac4b397 | 1718 | } while (offset < max_block_size); |
3839e657 TT |
1719 | #if 0 |
1720 | printf("\n"); | |
1721 | #endif | |
8fdc9985 TT |
1722 | if (dx_db) { |
1723 | #ifdef DX_DEBUG | |
1724 | printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n", | |
1725 | db->blockcnt, dx_db->type, | |
1726 | dx_db->min_hash, dx_db->max_hash); | |
1727 | #endif | |
b7a00563 | 1728 | cd->pctx.dir = cd->pctx.ino; |
8fdc9985 TT |
1729 | if ((dx_db->type == DX_DIRBLOCK_ROOT) || |
1730 | (dx_db->type == DX_DIRBLOCK_NODE)) | |
81683c6a | 1731 | parse_int_node(fs, db, cd, dx_dir, buf, failed_csum); |
8fdc9985 | 1732 | } |
e8548796 | 1733 | |
0ac4b397 DW |
1734 | if (offset != max_block_size) { |
1735 | cd->pctx.num = rec_len + offset - max_block_size; | |
1736 | if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { | |
1737 | dirent->rec_len = cd->pctx.num; | |
1738 | dir_modified++; | |
1b6bf175 | 1739 | } |
3839e657 TT |
1740 | } |
1741 | if (dir_modified) { | |
2e9d8391 | 1742 | int flags, will_rehash; |
e8548796 | 1743 | /* leaf block with no tail? Rehash dirs later. */ |
86f3b6cf | 1744 | if (ext2fs_has_feature_metadata_csum(fs->super) && |
e8548796 | 1745 | is_leaf && |
17641bf2 DW |
1746 | !inline_data_size && |
1747 | !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) { | |
1748 | if (insert_dirent_tail(fs, buf) == 0) | |
1749 | goto write_and_fix; | |
e8548796 | 1750 | e2fsck_rehash_dir_later(ctx, ino); |
17641bf2 | 1751 | } |
e8548796 DW |
1752 | |
1753 | write_and_fix: | |
2e9d8391 DW |
1754 | will_rehash = e2fsck_dir_will_be_rehashed(ctx, ino); |
1755 | if (will_rehash) { | |
1756 | flags = ctx->fs->flags; | |
e8548796 | 1757 | ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; |
2e9d8391 | 1758 | } |
6582dbe9 | 1759 | if (inline_data_size) { |
0ac4b397 | 1760 | buf = ibuf; |
cd971869 | 1761 | #ifdef WORDS_BIGENDIAN |
0ac4b397 DW |
1762 | if (db->blockcnt) |
1763 | goto skip_first_write_swab; | |
cd971869 DW |
1764 | *((__u32 *)buf) = ext2fs_le32_to_cpu(*((__u32 *)buf)); |
1765 | cd->pctx.errcode = ext2fs_dirent_swab_out2(fs, | |
1766 | buf + EXT4_INLINE_DATA_DOTDOT_SIZE, | |
0ac4b397 | 1767 | EXT4_MIN_INLINE_DATA_SIZE - |
cd971869 DW |
1768 | EXT4_INLINE_DATA_DOTDOT_SIZE, |
1769 | 0); | |
0ac4b397 DW |
1770 | if (cd->pctx.errcode) |
1771 | goto skip_second_write_swab; | |
1772 | skip_first_write_swab: | |
1773 | if (inline_data_size <= EXT4_MIN_INLINE_DATA_SIZE || | |
1774 | !db->blockcnt) | |
1775 | goto skip_second_write_swab; | |
1776 | cd->pctx.errcode = ext2fs_dirent_swab_out2(fs, | |
1777 | buf + EXT4_MIN_INLINE_DATA_SIZE, | |
1778 | inline_data_size - | |
1779 | EXT4_MIN_INLINE_DATA_SIZE, | |
1780 | 0); | |
1781 | skip_second_write_swab: | |
cd971869 DW |
1782 | if (cd->pctx.errcode && |
1783 | !fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx)) | |
1784 | goto abort_free_dict; | |
1785 | #endif | |
6582dbe9 ZL |
1786 | cd->pctx.errcode = |
1787 | ext2fs_inline_data_set(fs, ino, 0, buf, | |
1788 | inline_data_size); | |
1789 | } else | |
1790 | cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, | |
1791 | buf, 0, ino); | |
2e9d8391 DW |
1792 | if (will_rehash) |
1793 | ctx->fs->flags = (flags & | |
1794 | EXT2_FLAG_IGNORE_CSUM_ERRORS) | | |
1795 | (ctx->fs->flags & | |
1796 | ~EXT2_FLAG_IGNORE_CSUM_ERRORS); | |
1b6bf175 | 1797 | if (cd->pctx.errcode) { |
08b21301 | 1798 | if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, |
0926668d TT |
1799 | &cd->pctx)) |
1800 | goto abort_free_dict; | |
3839e657 TT |
1801 | } |
1802 | ext2fs_mark_changed(fs); | |
e8548796 DW |
1803 | } else if (is_leaf && failed_csum && !dir_modified) { |
1804 | /* | |
1805 | * If a leaf node that fails csum makes it this far without | |
1806 | * alteration, ask the user if the checksum should be fixed. | |
1807 | */ | |
1808 | if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID, | |
1809 | &cd->pctx)) | |
1810 | goto write_and_fix; | |
3839e657 | 1811 | } |
0926668d | 1812 | dict_free_nodes(&de_dict); |
3839e657 | 1813 | return 0; |
0926668d | 1814 | abort_free_dict: |
0926668d | 1815 | ctx->flags |= E2F_FLAG_ABORT; |
49a7360b | 1816 | dict_free_nodes(&de_dict); |
0926668d | 1817 | return DIRENT_ABORT; |
3839e657 TT |
1818 | } |
1819 | ||
624e4a64 AK |
1820 | struct del_block { |
1821 | e2fsck_t ctx; | |
1822 | e2_blkcnt_t num; | |
9d248022 | 1823 | blk64_t last_cluster; |
624e4a64 AK |
1824 | }; |
1825 | ||
3839e657 | 1826 | /* |
20654197 | 1827 | * This function is called to deallocate a block, and is an iterator |
3839e657 TT |
1828 | * functioned called by deallocate inode via ext2fs_iterate_block(). |
1829 | */ | |
1830 | static int deallocate_inode_block(ext2_filsys fs, | |
6dc64392 | 1831 | blk64_t *block_nr, |
54434927 | 1832 | e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), |
6dc64392 | 1833 | blk64_t ref_block EXT2FS_ATTR((unused)), |
54434927 | 1834 | int ref_offset EXT2FS_ATTR((unused)), |
133a56dc | 1835 | void *priv_data) |
3839e657 | 1836 | { |
624e4a64 | 1837 | struct del_block *p = priv_data; |
9d248022 | 1838 | blk64_t cluster = EXT2FS_B2C(fs, *block_nr); |
efc6f628 | 1839 | |
4a05268c | 1840 | if (*block_nr == 0) |
3839e657 | 1841 | return 0; |
9d248022 | 1842 | |
1843 | if (cluster == p->last_cluster) | |
1844 | return 0; | |
1845 | ||
1846 | p->last_cluster = cluster; | |
1ba7a2f2 | 1847 | if ((*block_nr < fs->super->s_first_data_block) || |
4efbac6f | 1848 | (*block_nr >= ext2fs_blocks_count(fs->super))) |
1ba7a2f2 | 1849 | return 0; |
9d248022 | 1850 | |
1851 | ext2fs_block_alloc_stats2(fs, *block_nr, -1); | |
624e4a64 | 1852 | p->num++; |
3839e657 TT |
1853 | return 0; |
1854 | } | |
efc6f628 | 1855 | |
3839e657 | 1856 | /* |
eb0680de LHS |
1857 | * This function reverts various counters and bitmaps incremented in |
1858 | * pass1 for the inode, blocks, and quotas before it was decided the | |
1859 | * inode was corrupt and needed to be cleared. This avoids the need | |
1860 | * to run e2fsck a second time (or have it restart itself) to repair | |
1861 | * these counters. | |
1862 | * | |
1863 | * It does not modify any on-disk state, so even if the inode is bad | |
1864 | * it _should_ reset in-memory state to before the inode was first | |
1865 | * processed. | |
3839e657 | 1866 | */ |
4035f40b | 1867 | static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) |
3839e657 | 1868 | { |
1b6bf175 | 1869 | ext2_filsys fs = ctx->fs; |
eb0680de | 1870 | struct ext2_inode_large inode; |
1b6bf175 | 1871 | struct problem_context pctx; |
0684a4f3 | 1872 | __u32 count; |
624e4a64 | 1873 | struct del_block del_block; |
efc6f628 | 1874 | |
eb0680de LHS |
1875 | e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode), |
1876 | sizeof(inode), "deallocate_inode"); | |
1b6bf175 TT |
1877 | clear_problem_context(&pctx); |
1878 | pctx.ino = ino; | |
f3db3566 | 1879 | |
3839e657 TT |
1880 | /* |
1881 | * Fix up the bitmaps... | |
1882 | */ | |
f8188fff | 1883 | e2fsck_read_bitmaps(ctx); |
0684a4f3 TT |
1884 | ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); |
1885 | ||
eb0680de | 1886 | if (ext2fs_file_acl_block(fs, EXT2_INODE(&inode)) && |
86f3b6cf | 1887 | ext2fs_has_feature_xattr(fs->super)) { |
39f5659a | 1888 | pctx.errcode = ext2fs_adjust_ea_refcount3(fs, |
eb0680de | 1889 | ext2fs_file_acl_block(fs, EXT2_INODE(&inode)), |
39f5659a | 1890 | block_buf, -1, &count, ino); |
0684a4f3 TT |
1891 | if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { |
1892 | pctx.errcode = 0; | |
1893 | count = 1; | |
1894 | } | |
1895 | if (pctx.errcode) { | |
eb0680de | 1896 | pctx.blk = ext2fs_file_acl_block(fs, EXT2_INODE(&inode)); |
0684a4f3 TT |
1897 | fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx); |
1898 | ctx->flags |= E2F_FLAG_ABORT; | |
1899 | return; | |
1900 | } | |
1901 | if (count == 0) { | |
48f23054 | 1902 | ext2fs_block_alloc_stats2(fs, |
eb0680de | 1903 | ext2fs_file_acl_block(fs, EXT2_INODE(&inode)), -1); |
0684a4f3 | 1904 | } |
eb0680de | 1905 | ext2fs_file_acl_block_set(fs, EXT2_INODE(&inode), 0); |
0684a4f3 | 1906 | } |
3839e657 | 1907 | |
eb0680de | 1908 | if (!ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(&inode))) |
c8ec2bad | 1909 | goto clear_inode; |
a4742691 | 1910 | |
2ece8390 DW |
1911 | /* Inline data inodes don't have blocks to iterate */ |
1912 | if (inode.i_flags & EXT4_INLINE_DATA_FL) | |
1913 | goto clear_inode; | |
1914 | ||
f15b1aaf AD |
1915 | if (ext2fs_needs_large_file_feature(EXT2_I_SIZE(&inode))) { |
1916 | if (LINUX_S_ISREG(inode.i_mode)) | |
1917 | ctx->large_files--; | |
1918 | else if (LINUX_S_ISDIR(inode.i_mode)) | |
1919 | ctx->large_dirs--; | |
1920 | } | |
a4742691 | 1921 | |
624e4a64 AK |
1922 | del_block.ctx = ctx; |
1923 | del_block.num = 0; | |
9d248022 | 1924 | del_block.last_cluster = 0; |
6dc64392 | 1925 | pctx.errcode = ext2fs_block_iterate3(fs, ino, 0, block_buf, |
624e4a64 AK |
1926 | deallocate_inode_block, |
1927 | &del_block); | |
1b6bf175 TT |
1928 | if (pctx.errcode) { |
1929 | fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); | |
08b21301 TT |
1930 | ctx->flags |= E2F_FLAG_ABORT; |
1931 | return; | |
1b6bf175 | 1932 | } |
eb0680de LHS |
1933 | |
1934 | if ((ino != quota_type2inum(PRJQUOTA, fs->super)) && | |
1935 | (ino != fs->super->s_orphan_file_inum) && | |
1936 | (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) && | |
1937 | !(inode.i_flags & EXT4_EA_INODE_FL)) { | |
1938 | if (del_block.num > 0) | |
1939 | quota_data_sub(ctx->qctx, &inode, ino, | |
1940 | del_block.num * EXT2_CLUSTER_SIZE(fs->super)); | |
1941 | quota_data_inodes(ctx->qctx, (struct ext2_inode_large *)&inode, | |
1942 | ino, -1); | |
1943 | } | |
1944 | ||
c8ec2bad TT |
1945 | clear_inode: |
1946 | /* Inode may have changed by block_iterate, so reread it */ | |
eb0680de LHS |
1947 | e2fsck_read_inode(ctx, ino, EXT2_INODE(&inode), "deallocate_inode"); |
1948 | e2fsck_clear_inode(ctx, ino, EXT2_INODE(&inode), 0, "deallocate_inode"); | |
3839e657 TT |
1949 | } |
1950 | ||
8fdc9985 | 1951 | /* |
055866d8 | 1952 | * This function clears the htree flag on an inode |
8fdc9985 TT |
1953 | */ |
1954 | static void clear_htree(e2fsck_t ctx, ext2_ino_t ino) | |
1955 | { | |
1956 | struct ext2_inode inode; | |
efc6f628 | 1957 | |
8fdc9985 TT |
1958 | e2fsck_read_inode(ctx, ino, &inode, "clear_htree"); |
1959 | inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL; | |
1960 | e2fsck_write_inode(ctx, ino, &inode, "clear_htree"); | |
b7a00563 TT |
1961 | if (ctx->dirs_to_hash) |
1962 | ext2fs_u32_list_add(ctx->dirs_to_hash, ino); | |
8fdc9985 TT |
1963 | } |
1964 | ||
1965 | ||
f404167d TT |
1966 | int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, |
1967 | ext2_ino_t ino, char *buf) | |
3839e657 | 1968 | { |
1b6bf175 | 1969 | ext2_filsys fs = ctx->fs; |
3839e657 | 1970 | struct ext2_inode inode; |
3839e657 | 1971 | int inode_modified = 0; |
6c313fd4 | 1972 | int not_fixed = 0; |
1e3472c5 | 1973 | unsigned char *frag, *fsize; |
21c84b71 | 1974 | struct problem_context pctx; |
3c7c6d73 | 1975 | problem_t problem = 0; |
3839e657 | 1976 | |
08b21301 | 1977 | e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); |
21c84b71 TT |
1978 | |
1979 | clear_problem_context(&pctx); | |
1980 | pctx.ino = ino; | |
1981 | pctx.dir = dir; | |
1982 | pctx.inode = &inode; | |
1983 | ||
0c80c44b | 1984 | if (ext2fs_file_acl_block(fs, &inode) && |
86f3b6cf | 1985 | !ext2fs_has_feature_xattr(fs->super)) { |
f76344fb | 1986 | if (fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { |
0c80c44b | 1987 | ext2fs_file_acl_block_set(fs, &inode, 0); |
f76344fb TT |
1988 | inode_modified++; |
1989 | } else | |
1990 | not_fixed++; | |
1991 | } | |
6c313fd4 | 1992 | |
50e1e10f TT |
1993 | if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && |
1994 | !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && | |
1995 | !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && | |
08b21301 TT |
1996 | !(LINUX_S_ISSOCK(inode.i_mode))) |
1997 | problem = PR_2_BAD_MODE; | |
fdbdea09 | 1998 | else if (LINUX_S_ISCHR(inode.i_mode) |
0684a4f3 | 1999 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
08b21301 | 2000 | problem = PR_2_BAD_CHAR_DEV; |
fdbdea09 | 2001 | else if (LINUX_S_ISBLK(inode.i_mode) |
0684a4f3 | 2002 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
08b21301 | 2003 | problem = PR_2_BAD_BLOCK_DEV; |
fdbdea09 | 2004 | else if (LINUX_S_ISFIFO(inode.i_mode) |
0684a4f3 | 2005 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
1dde43f0 | 2006 | problem = PR_2_BAD_FIFO; |
fdbdea09 | 2007 | else if (LINUX_S_ISSOCK(inode.i_mode) |
0684a4f3 | 2008 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
1dde43f0 | 2009 | problem = PR_2_BAD_SOCKET; |
fdbdea09 | 2010 | else if (LINUX_S_ISLNK(inode.i_mode) |
7cadc577 | 2011 | && !e2fsck_pass1_check_symlink(fs, ino, &inode, buf)) { |
bcf9c5d4 | 2012 | problem = PR_2_INVALID_SYMLINK; |
67052a8a | 2013 | } |
1dde43f0 | 2014 | |
08b21301 TT |
2015 | if (problem) { |
2016 | if (fix_problem(ctx, problem, &pctx)) { | |
1b6bf175 | 2017 | deallocate_inode(ctx, ino, 0); |
a02ce9df | 2018 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 2019 | return 0; |
7cf73dcd | 2020 | return 1; |
6c313fd4 TT |
2021 | } else |
2022 | not_fixed++; | |
08b21301 | 2023 | problem = 0; |
7cf73dcd | 2024 | } |
efc6f628 | 2025 | |
6c313fd4 TT |
2026 | if (inode.i_faddr) { |
2027 | if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) { | |
2028 | inode.i_faddr = 0; | |
2029 | inode_modified++; | |
2030 | } else | |
2031 | not_fixed++; | |
3839e657 | 2032 | } |
1e3472c5 TT |
2033 | |
2034 | switch (fs->super->s_creator_os) { | |
1e3472c5 TT |
2035 | case EXT2_OS_HURD: |
2036 | frag = &inode.osd2.hurd2.h_i_frag; | |
2037 | fsize = &inode.osd2.hurd2.h_i_fsize; | |
2038 | break; | |
1e3472c5 TT |
2039 | default: |
2040 | frag = fsize = 0; | |
2041 | } | |
21c84b71 TT |
2042 | if (frag && *frag) { |
2043 | pctx.num = *frag; | |
1b6bf175 | 2044 | if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) { |
21c84b71 TT |
2045 | *frag = 0; |
2046 | inode_modified++; | |
7e0282c5 TT |
2047 | } else |
2048 | not_fixed++; | |
21c84b71 TT |
2049 | pctx.num = 0; |
2050 | } | |
2051 | if (fsize && *fsize) { | |
2052 | pctx.num = *fsize; | |
1b6bf175 | 2053 | if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) { |
21c84b71 TT |
2054 | *fsize = 0; |
2055 | inode_modified++; | |
6c313fd4 TT |
2056 | } else |
2057 | not_fixed++; | |
21c84b71 TT |
2058 | pctx.num = 0; |
2059 | } | |
2060 | ||
5d17119d | 2061 | if ((fs->super->s_creator_os == EXT2_OS_LINUX) && |
86f3b6cf | 2062 | !ext2fs_has_feature_huge_file(fs->super) && |
5d17119d TT |
2063 | (inode.osd2.linux2.l_i_blocks_hi != 0)) { |
2064 | pctx.num = inode.osd2.linux2.l_i_blocks_hi; | |
2065 | if (fix_problem(ctx, PR_2_BLOCKS_HI_ZERO, &pctx)) { | |
2066 | inode.osd2.linux2.l_i_blocks_hi = 0; | |
2067 | inode_modified++; | |
2068 | } | |
2069 | } | |
2070 | ||
36769c60 | 2071 | if ((fs->super->s_creator_os == EXT2_OS_LINUX) && |
86f3b6cf | 2072 | !ext2fs_has_feature_64bit(fs->super) && |
911ec626 TT |
2073 | inode.osd2.linux2.l_i_file_acl_high != 0) { |
2074 | pctx.num = inode.osd2.linux2.l_i_file_acl_high; | |
2075 | if (fix_problem(ctx, PR_2_I_FILE_ACL_HI_ZERO, &pctx)) { | |
2076 | inode.osd2.linux2.l_i_file_acl_high = 0; | |
2077 | inode_modified++; | |
2078 | } else | |
2079 | not_fixed++; | |
2080 | } | |
2081 | ||
0c80c44b TT |
2082 | if (ext2fs_file_acl_block(fs, &inode) && |
2083 | ((ext2fs_file_acl_block(fs, &inode) < fs->super->s_first_data_block) || | |
2084 | (ext2fs_file_acl_block(fs, &inode) >= ext2fs_blocks_count(fs->super)))) { | |
6c313fd4 | 2085 | if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) { |
0c80c44b | 2086 | ext2fs_file_acl_block_set(fs, &inode, 0); |
6c313fd4 TT |
2087 | inode_modified++; |
2088 | } else | |
2089 | not_fixed++; | |
342d847d | 2090 | } |
3f0cf647 | 2091 | if (inode.i_size_high && !ext2fs_has_feature_largedir(fs->super) && |
f15b1aaf | 2092 | inode.i_blocks < 1ULL << (29 - EXT2_BLOCK_SIZE_BITS(fs->super)) && |
6c313fd4 | 2093 | LINUX_S_ISDIR(inode.i_mode)) { |
578fcbfd AB |
2094 | if (fix_problem(ctx, PR_2_DIR_SIZE_HIGH_ZERO, &pctx)) { |
2095 | inode.i_size_high = 0; | |
6c313fd4 TT |
2096 | inode_modified++; |
2097 | } else | |
2098 | not_fixed++; | |
21c84b71 | 2099 | } |
6c313fd4 | 2100 | |
f3db3566 | 2101 | if (inode_modified) |
08b21301 | 2102 | e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); |
f76344fb | 2103 | if (!not_fixed && ctx->inode_bad_map) |
c5d2f50d | 2104 | ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino); |
3839e657 TT |
2105 | return 0; |
2106 | } | |
2107 | ||
50e1e10f TT |
2108 | /* |
2109 | * allocate_dir_block --- this function allocates a new directory | |
2110 | * block for a particular inode; this is done if a directory has | |
2111 | * a "hole" in it, or if a directory has a illegal block number | |
2112 | * that was zeroed out and now needs to be replaced. | |
2113 | */ | |
1b6bf175 | 2114 | static int allocate_dir_block(e2fsck_t ctx, |
6dc64392 | 2115 | struct ext2_db_entry2 *db, |
efc6f628 | 2116 | char *buf EXT2FS_ATTR((unused)), |
54434927 | 2117 | struct problem_context *pctx) |
50e1e10f | 2118 | { |
1b6bf175 | 2119 | ext2_filsys fs = ctx->fs; |
ff11309e | 2120 | blk64_t blk = 0; |
50e1e10f TT |
2121 | char *block; |
2122 | struct ext2_inode inode; | |
50e1e10f | 2123 | |
1b6bf175 | 2124 | if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0) |
50e1e10f TT |
2125 | return 1; |
2126 | ||
2127 | /* | |
2128 | * Read the inode and block bitmaps in; we'll be messing with | |
2129 | * them. | |
2130 | */ | |
f8188fff | 2131 | e2fsck_read_bitmaps(ctx); |
efc6f628 | 2132 | |
50e1e10f TT |
2133 | /* |
2134 | * First, find a free block | |
2135 | */ | |
ff11309e DW |
2136 | e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block"); |
2137 | pctx->errcode = ext2fs_map_cluster_block(fs, db->ino, &inode, | |
2138 | db->blockcnt, &blk); | |
2139 | if (pctx->errcode || blk == 0) { | |
7b486ec0 DW |
2140 | blk = ext2fs_find_inode_goal(fs, db->ino, &inode, db->blockcnt); |
2141 | pctx->errcode = ext2fs_new_block2(fs, blk, | |
ff11309e DW |
2142 | ctx->block_found_map, &blk); |
2143 | if (pctx->errcode) { | |
2144 | pctx->str = "ext2fs_new_block"; | |
2145 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
2146 | return 1; | |
2147 | } | |
50e1e10f | 2148 | } |
c5d2f50d VAH |
2149 | ext2fs_mark_block_bitmap2(ctx->block_found_map, blk); |
2150 | ext2fs_mark_block_bitmap2(fs->block_map, blk); | |
50e1e10f TT |
2151 | ext2fs_mark_bb_dirty(fs); |
2152 | ||
2153 | /* | |
2154 | * Now let's create the actual data block for the inode | |
2155 | */ | |
2156 | if (db->blockcnt) | |
1b6bf175 | 2157 | pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block); |
50e1e10f | 2158 | else |
1b6bf175 TT |
2159 | pctx->errcode = ext2fs_new_dir_block(fs, db->ino, |
2160 | EXT2_ROOT_INO, &block); | |
50e1e10f | 2161 | |
1b6bf175 TT |
2162 | if (pctx->errcode) { |
2163 | pctx->str = "ext2fs_new_dir_block"; | |
2164 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
2165 | return 1; |
2166 | } | |
2167 | ||
81683c6a | 2168 | pctx->errcode = ext2fs_write_dir_block4(fs, blk, block, 0, db->ino); |
c4e3d3f3 | 2169 | ext2fs_free_mem(&block); |
1b6bf175 TT |
2170 | if (pctx->errcode) { |
2171 | pctx->str = "ext2fs_write_dir_block"; | |
2172 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
2173 | return 1; |
2174 | } | |
2175 | ||
2176 | /* | |
2177 | * Update the inode block count | |
2178 | */ | |
1ca1059f | 2179 | ext2fs_iblk_add_blocks(fs, &inode, 1); |
62f9bd0e | 2180 | if (EXT2_I_SIZE(&inode) < ((__u64) db->blockcnt+1) * fs->blocksize) { |
97c607b1 DW |
2181 | pctx->errcode = ext2fs_inode_size_set(fs, &inode, |
2182 | (db->blockcnt+1) * fs->blocksize); | |
2183 | if (pctx->errcode) { | |
2184 | pctx->str = "ext2fs_inode_size_set"; | |
2185 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
2186 | return 1; | |
2187 | } | |
2188 | } | |
08b21301 | 2189 | e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block"); |
50e1e10f TT |
2190 | |
2191 | /* | |
2192 | * Finally, update the block pointers for the inode | |
2193 | */ | |
2194 | db->blk = blk; | |
2d07b3ad TT |
2195 | pctx->errcode = ext2fs_bmap2(fs, db->ino, &inode, 0, BMAP_SET, |
2196 | db->blockcnt, 0, &blk); | |
1b6bf175 TT |
2197 | if (pctx->errcode) { |
2198 | pctx->str = "ext2fs_block_iterate"; | |
2199 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
2200 | return 1; |
2201 | } | |
2202 | ||
2203 | return 0; | |
2204 | } |