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