Whamcloud - gitweb
e2fsck: don't abort if the journal is corrupted due to checksum errors
[tools/e2fsprogs.git] / e2fsck / dirinfo.c
1 /*
2  * dirinfo.c --- maintains the directory information table for e2fsck.
3  *
4  * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
5  * under the terms of the GNU Public License.
6  */
7
8 #undef DIRINFO_DEBUG
9
10 #include "config.h"
11 #include "e2fsck.h"
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include "uuid/uuid.h"
15
16 #include "ext2fs/ext2fs.h"
17 #include <ext2fs/tdb.h>
18
19 struct dir_info_db {
20         int             count;
21         int             size;
22         struct dir_info *array;
23         struct dir_info *last_lookup;
24         char            *tdb_fn;
25         TDB_CONTEXT     *tdb;
26 };
27
28 struct dir_info_iter {
29         int     i;
30         TDB_DATA        tdb_iter;
31 };
32
33 struct dir_info_ent {
34         ext2_ino_t              dotdot; /* Parent according to '..' */
35         ext2_ino_t              parent; /* Parent according to treewalk */
36 };
37
38
39 static void e2fsck_put_dir_info(e2fsck_t ctx, struct dir_info *dir);
40
41 static void setup_tdb(e2fsck_t ctx, ext2_ino_t num_dirs)
42 {
43         struct dir_info_db      *db = ctx->dir_info;
44         unsigned int            threshold;
45         errcode_t               retval;
46         mode_t                  save_umask;
47         char                    *tdb_dir, uuid[40];
48         int                     fd, enable;
49
50         profile_get_string(ctx->profile, "scratch_files", "directory", 0, 0,
51                            &tdb_dir);
52         profile_get_uint(ctx->profile, "scratch_files",
53                          "numdirs_threshold", 0, 0, &threshold);
54         profile_get_boolean(ctx->profile, "scratch_files",
55                             "dirinfo", 0, 1, &enable);
56
57         if (!enable || !tdb_dir || access(tdb_dir, W_OK) ||
58             (threshold && num_dirs <= threshold))
59                 return;
60
61         retval = ext2fs_get_mem(strlen(tdb_dir) + 64, &db->tdb_fn);
62         if (retval)
63                 return;
64
65         uuid_unparse(ctx->fs->super->s_uuid, uuid);
66         sprintf(db->tdb_fn, "%s/%s-dirinfo-XXXXXX", tdb_dir, uuid);
67         save_umask = umask(077);
68         fd = mkstemp(db->tdb_fn);
69         umask(save_umask);
70         if (fd < 0) {
71                 db->tdb = NULL;
72                 return;
73         }
74
75         if (num_dirs < 99991)
76                 num_dirs = 99991; /* largest 5 digit prime */
77
78         db->tdb = tdb_open(db->tdb_fn, num_dirs, TDB_NOLOCK | TDB_NOSYNC,
79                            O_RDWR | O_CREAT | O_TRUNC, 0600);
80         close(fd);
81 }
82
83 static void setup_db(e2fsck_t ctx)
84 {
85         struct dir_info_db      *db;
86         ext2_ino_t              num_dirs;
87         errcode_t               retval;
88
89         db = (struct dir_info_db *)
90                 e2fsck_allocate_memory(ctx, sizeof(struct dir_info_db),
91                                        "directory map db");
92         db->count = db->size = 0;
93         db->array = 0;
94
95         ctx->dir_info = db;
96
97         retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
98         if (retval)
99                 num_dirs = 1024;        /* Guess */
100
101         setup_tdb(ctx, num_dirs);
102
103         if (db->tdb) {
104 #ifdef DIRINFO_DEBUG
105                 printf("Note: using tdb!\n");
106 #endif
107                 return;
108         }
109
110         db->size = num_dirs + 10;
111         db->array  = (struct dir_info *)
112                 e2fsck_allocate_memory(ctx, db->size
113                                        * sizeof (struct dir_info),
114                                        "directory map");
115 }
116
117 /*
118  * This subroutine is called during pass1 to create a directory info
119  * entry.  During pass1, the passed-in parent is 0; it will get filled
120  * in during pass2.
121  */
122 void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
123 {
124         struct dir_info_db      *db;
125         struct dir_info         *dir, ent, *old_array;
126         int                     i, j;
127         errcode_t               retval;
128         unsigned long           old_size;
129
130 #ifdef DIRINFO_DEBUG
131         printf("add_dir_info for inode (%lu, %lu)...\n", ino, parent);
132 #endif
133         if (!ctx->dir_info)
134                 setup_db(ctx);
135         db = ctx->dir_info;
136
137         if (ctx->dir_info->count >= ctx->dir_info->size) {
138                 old_size = ctx->dir_info->size * sizeof(struct dir_info);
139                 ctx->dir_info->size += 10;
140                 old_array = ctx->dir_info->array;
141                 retval = ext2fs_resize_mem(old_size, ctx->dir_info->size *
142                                            sizeof(struct dir_info),
143                                            &ctx->dir_info->array);
144                 if (retval) {
145                         fprintf(stderr, "Couldn't reallocate dir_info "
146                                 "structure to %d entries\n",
147                                 ctx->dir_info->size);
148                         fatal_error(ctx, 0);
149                         ctx->dir_info->size -= 10;
150                         return;
151                 }
152                 if (old_array != ctx->dir_info->array)
153                         ctx->dir_info->last_lookup = NULL;
154         }
155
156         ent.ino = ino;
157         ent.parent = parent;
158         ent.dotdot = parent;
159
160         if (db->tdb) {
161                 e2fsck_put_dir_info(ctx, &ent);
162                 return;
163         }
164
165         /*
166          * Normally, add_dir_info is called with each inode in
167          * sequential order; but once in a while (like when pass 3
168          * needs to recreate the root directory or lost+found
169          * directory) it is called out of order.  In those cases, we
170          * need to move the dir_info entries down to make room, since
171          * the dir_info array needs to be sorted by inode number for
172          * get_dir_info()'s sake.
173          */
174         if (ctx->dir_info->count &&
175             ctx->dir_info->array[ctx->dir_info->count-1].ino >= ino) {
176                 for (i = ctx->dir_info->count-1; i > 0; i--)
177                         if (ctx->dir_info->array[i-1].ino < ino)
178                                 break;
179                 dir = &ctx->dir_info->array[i];
180                 if (dir->ino != ino)
181                         for (j = ctx->dir_info->count++; j > i; j--)
182                                 ctx->dir_info->array[j] = ctx->dir_info->array[j-1];
183         } else
184                 dir = &ctx->dir_info->array[ctx->dir_info->count++];
185
186         dir->ino = ino;
187         dir->dotdot = parent;
188         dir->parent = parent;
189 }
190
191 /*
192  * get_dir_info() --- given an inode number, try to find the directory
193  * information entry for it.
194  */
195 static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
196 {
197         struct dir_info_db      *db = ctx->dir_info;
198         int                     low, high, mid;
199         struct dir_info_ent     *buf;
200         static struct dir_info  ret_dir_info;
201
202         if (!db)
203                 return 0;
204
205 #ifdef DIRINFO_DEBUG
206         printf("e2fsck_get_dir_info %d...", ino);
207 #endif
208
209         if (db->tdb) {
210                 TDB_DATA key, data;
211
212                 key.dptr = (unsigned char *) &ino;
213                 key.dsize = sizeof(ext2_ino_t);
214
215                 data = tdb_fetch(db->tdb, key);
216                 if (!data.dptr) {
217                         if (tdb_error(db->tdb) != TDB_ERR_NOEXIST)
218                                 printf("fetch failed: %s\n",
219                                        tdb_errorstr(db->tdb));
220                         return 0;
221                 }
222
223                 buf = (struct dir_info_ent *) data.dptr;
224                 ret_dir_info.ino = ino;
225                 ret_dir_info.dotdot = buf->dotdot;
226                 ret_dir_info.parent = buf->parent;
227 #ifdef DIRINFO_DEBUG
228                 printf("(%d,%d,%d)\n", ino, buf->dotdot, buf->parent);
229 #endif
230                 free(data.dptr);
231                 return &ret_dir_info;
232         }
233
234         if (db->last_lookup && db->last_lookup->ino == ino)
235                 return db->last_lookup;
236
237         low = 0;
238         high = ctx->dir_info->count-1;
239         if (ino == ctx->dir_info->array[low].ino) {
240 #ifdef DIRINFO_DEBUG
241                 printf("(%d,%d,%d)\n", ino,
242                        ctx->dir_info->array[low].dotdot,
243                        ctx->dir_info->array[low].parent);
244 #endif
245                 return &ctx->dir_info->array[low];
246         }
247         if (ino == ctx->dir_info->array[high].ino) {
248 #ifdef DIRINFO_DEBUG
249                 printf("(%d,%d,%d)\n", ino,
250                        ctx->dir_info->array[high].dotdot,
251                        ctx->dir_info->array[high].parent);
252 #endif
253                 return &ctx->dir_info->array[high];
254         }
255
256         while (low < high) {
257                 mid = (low+high)/2;
258                 if (mid == low || mid == high)
259                         break;
260                 if (ino == ctx->dir_info->array[mid].ino) {
261 #ifdef DIRINFO_DEBUG
262                         printf("(%d,%d,%d)\n", ino,
263                                ctx->dir_info->array[mid].dotdot,
264                                ctx->dir_info->array[mid].parent);
265 #endif
266                         return &ctx->dir_info->array[mid];
267                 }
268                 if (ino < ctx->dir_info->array[mid].ino)
269                         high = mid;
270                 else
271                         low = mid;
272         }
273         return 0;
274 }
275
276 static void e2fsck_put_dir_info(e2fsck_t ctx, struct dir_info *dir)
277 {
278         struct dir_info_db      *db = ctx->dir_info;
279         struct dir_info_ent     buf;
280         TDB_DATA                key, data;
281
282 #ifdef DIRINFO_DEBUG
283         printf("e2fsck_put_dir_info (%d, %d, %d)...", dir->ino, dir->dotdot,
284                dir->parent);
285 #endif
286
287         if (!db->tdb)
288                 return;
289
290         buf.parent = dir->parent;
291         buf.dotdot = dir->dotdot;
292
293         key.dptr = (unsigned char *) &dir->ino;
294         key.dsize = sizeof(ext2_ino_t);
295         data.dptr = (unsigned char *) &buf;
296         data.dsize = sizeof(buf);
297
298         if (tdb_store(db->tdb, key, data, TDB_REPLACE) == -1) {
299                 printf("store failed: %s\n", tdb_errorstr(db->tdb));
300         }
301         return;
302 }
303
304 /*
305  * Free the dir_info structure when it isn't needed any more.
306  */
307 void e2fsck_free_dir_info(e2fsck_t ctx)
308 {
309         if (ctx->dir_info) {
310                 if (ctx->dir_info->tdb)
311                         tdb_close(ctx->dir_info->tdb);
312                 if (ctx->dir_info->tdb_fn) {
313                         unlink(ctx->dir_info->tdb_fn);
314                         free(ctx->dir_info->tdb_fn);
315                 }
316                 if (ctx->dir_info->array)
317                         ext2fs_free_mem(&ctx->dir_info->array);
318                 ctx->dir_info->array = 0;
319                 ctx->dir_info->size = 0;
320                 ctx->dir_info->count = 0;
321                 ext2fs_free_mem(&ctx->dir_info);
322                 ctx->dir_info = 0;
323         }
324 }
325
326 /*
327  * Return the count of number of directories in the dir_info structure
328  */
329 int e2fsck_get_num_dirinfo(e2fsck_t ctx)
330 {
331         return ctx->dir_info ? ctx->dir_info->count : 0;
332 }
333
334 struct dir_info_iter *e2fsck_dir_info_iter_begin(e2fsck_t ctx)
335 {
336         struct dir_info_iter *iter;
337         struct dir_info_db *db = ctx->dir_info;
338
339         iter = e2fsck_allocate_memory(ctx, sizeof(struct dir_info_iter),
340                                       "dir_info iterator");
341
342         if (db->tdb)
343                 iter->tdb_iter = tdb_firstkey(db->tdb);
344
345         return iter;
346 }
347
348 void e2fsck_dir_info_iter_end(e2fsck_t ctx EXT2FS_ATTR((unused)),
349                               struct dir_info_iter *iter)
350 {
351         free(iter->tdb_iter.dptr);
352         ext2fs_free_mem(&iter);
353 }
354
355 /*
356  * A simple interator function
357  */
358 struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, struct dir_info_iter *iter)
359 {
360         TDB_DATA data, key;
361         struct dir_info_db *db = ctx->dir_info;
362         struct dir_info_ent *buf;
363         static struct dir_info ret_dir_info;
364
365         if (!ctx->dir_info || !iter)
366                 return 0;
367
368         if (db->tdb) {
369                 if (iter->tdb_iter.dptr == 0)
370                         return 0;
371                 key = iter->tdb_iter;
372                 data = tdb_fetch(db->tdb, key);
373                 if (!data.dptr) {
374                         printf("iter fetch failed: %s\n",
375                                tdb_errorstr(db->tdb));
376                         return 0;
377                 }
378                 buf = (struct dir_info_ent *) data.dptr;
379                 ret_dir_info.ino = *((ext2_ino_t *) iter->tdb_iter.dptr);
380                 ret_dir_info.dotdot = buf->dotdot;
381                 ret_dir_info.parent = buf->parent;
382                 iter->tdb_iter = tdb_nextkey(db->tdb, key);
383                 free(key.dptr);
384                 free(data.dptr);
385                 return &ret_dir_info;
386         }
387
388         if (iter->i >= ctx->dir_info->count)
389                 return 0;
390
391 #ifdef DIRINFO_DEBUG
392         printf("iter(%d, %d, %d)...", ctx->dir_info->array[iter->i].ino,
393                ctx->dir_info->array[iter->i].dotdot,
394                ctx->dir_info->array[iter->i].parent);
395 #endif
396         ctx->dir_info->last_lookup = ctx->dir_info->array + iter->i++;
397         return(ctx->dir_info->last_lookup);
398 }
399
400 /*
401  * This function only sets the parent pointer, and requires that
402  * dirinfo structure has already been created.
403  */
404 int e2fsck_dir_info_set_parent(e2fsck_t ctx, ext2_ino_t ino,
405                                ext2_ino_t parent)
406 {
407         struct dir_info *p;
408
409         p = e2fsck_get_dir_info(ctx, ino);
410         if (!p)
411                 return 1;
412         p->parent = parent;
413         e2fsck_put_dir_info(ctx, p);
414         return 0;
415 }
416
417 /*
418  * This function only sets the dot dot pointer, and requires that
419  * dirinfo structure has already been created.
420  */
421 int e2fsck_dir_info_set_dotdot(e2fsck_t ctx, ext2_ino_t ino,
422                                ext2_ino_t dotdot)
423 {
424         struct dir_info *p;
425
426         p = e2fsck_get_dir_info(ctx, ino);
427         if (!p)
428                 return 1;
429         p->dotdot = dotdot;
430         e2fsck_put_dir_info(ctx, p);
431         return 0;
432 }
433
434 /*
435  * This function only sets the parent pointer, and requires that
436  * dirinfo structure has already been created.
437  */
438 int e2fsck_dir_info_get_parent(e2fsck_t ctx, ext2_ino_t ino,
439                                ext2_ino_t *parent)
440 {
441         struct dir_info *p;
442
443         p = e2fsck_get_dir_info(ctx, ino);
444         if (!p)
445                 return 1;
446         *parent = p->parent;
447         return 0;
448 }
449
450 /*
451  * This function only sets the dot dot pointer, and requires that
452  * dirinfo structure has already been created.
453  */
454 int e2fsck_dir_info_get_dotdot(e2fsck_t ctx, ext2_ino_t ino,
455                                ext2_ino_t *dotdot)
456 {
457         struct dir_info *p;
458
459         p = e2fsck_get_dir_info(ctx, ino);
460         if (!p)
461                 return 1;
462         *dotdot = p->dotdot;
463         return 0;
464 }
465