Home | History | Annotate | Download | only in e2fsck
      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 
     17 struct 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 
     26 struct dir_info_iter {
     27 	int	i;
     28 	TDB_DATA	tdb_iter;
     29 };
     30 
     31 struct dir_info_ent {
     32 	ext2_ino_t		dotdot;	/* Parent according to '..' */
     33 	ext2_ino_t		parent; /* Parent according to treewalk */
     34 };
     35 
     36 
     37 static void e2fsck_put_dir_info(e2fsck_t ctx, struct dir_info *dir);
     38 
     39 static 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 
     78 static 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  */
    117 void 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  */
    183 static 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 
    264 static 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  */
    295 void 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  */
    317 int e2fsck_get_num_dirinfo(e2fsck_t ctx)
    318 {
    319 	return ctx->dir_info ? ctx->dir_info->count : 0;
    320 }
    321 
    322 struct 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 
    336 void 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  */
    346 struct 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  */
    392 int 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  */
    409 int 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  */
    426 int 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  */
    442 int 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