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