htree.c revision 5dd77dbe5a0ac6d78c1c6441fae4087be56d9088
1/*
2 * htree.c --- hash tree routines
3 *
4 * Copyright (C) 2002 Theodore Ts'o.  This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
8#include <stdio.h>
9#include <unistd.h>
10#include <stdlib.h>
11#include <ctype.h>
12#include <string.h>
13#include <time.h>
14#ifdef HAVE_ERRNO_H
15#include <errno.h>
16#endif
17#include <sys/types.h>
18#ifdef HAVE_GETOPT_H
19#include <getopt.h>
20#else
21extern int optind;
22extern char *optarg;
23#endif
24
25#include "debugfs.h"
26
27static FILE *pager;
28
29static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
30				 struct ext2_inode *inode,
31				 struct ext2_dx_root_info * rootnode,
32				 blk_t blk, char *buf)
33{
34	errcode_t	errcode;
35	struct ext2_dir_entry *dirent;
36	int		thislen, col = 0;
37	unsigned int	offset = 0;
38	char		name[EXT2_NAME_LEN + 1];
39	char		tmp[EXT2_NAME_LEN + 16];
40	blk_t		pblk;
41	ext2_dirhash_t 	hash, minor_hash;
42	int		rec_len, hash_alg;
43
44	errcode = ext2fs_bmap(fs, ino, inode, buf, 0, blk, &pblk);
45	if (errcode) {
46		com_err("htree_dump_leaf_node", errcode,
47			"while mapping logical block %u\n", blk);
48		return;
49	}
50
51	printf("Reading directory block %lu, phys %lu\n", blk, pblk);
52	errcode = ext2fs_read_dir_block2(current_fs, pblk, buf, 0);
53	if (errcode) {
54		com_err("htree_dump_leaf_node", errcode,
55			"while reading block %lu (%lu)\n", blk, pblk);
56		return;
57	}
58	hash_alg = rootnode->hash_version;
59	if ((hash_alg <= EXT2_HASH_TEA) &&
60	    (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
61		hash_alg += 3;
62
63	while (offset < fs->blocksize) {
64		dirent = (struct ext2_dir_entry *) (buf + offset);
65		rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
66			dirent->rec_len : 65536;
67		if (((offset + rec_len) > fs->blocksize) ||
68		    (rec_len < 8) ||
69		    ((rec_len % 4) != 0) ||
70		    (((dirent->name_len & 0xFF)+8) > rec_len)) {
71			fprintf(pager, "Corrupted directory block (%u)!\n", blk);
72			break;
73		}
74		thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN) ?
75			(dirent->name_len & 0xFF) : EXT2_NAME_LEN;
76		strncpy(name, dirent->name, thislen);
77		name[thislen] = '\0';
78		errcode = ext2fs_dirhash(hash_alg, name,
79					 thislen, fs->super->s_hash_seed,
80					 &hash, &minor_hash);
81		if (errcode)
82			com_err("htree_dump_leaf_node", errcode,
83				"while calculating hash");
84		sprintf(tmp, "%u 0x%08x-%08x (%d) %s   ", dirent->inode,
85			hash, minor_hash, rec_len, name);
86		thislen = strlen(tmp);
87		if (col + thislen > 80) {
88			fprintf(pager, "\n");
89			col = 0;
90		}
91		fprintf(pager, "%s", tmp);
92		col += thislen;
93		offset += rec_len;
94	}
95	fprintf(pager, "\n");
96}
97
98
99static void htree_dump_int_block(ext2_filsys fs, ext2_ino_t ino,
100				 struct ext2_inode *inode,
101				 struct ext2_dx_root_info * rootnode,
102				 blk_t blk, char *buf, int level);
103
104
105static void htree_dump_int_node(ext2_filsys fs, ext2_ino_t ino,
106				struct ext2_inode *inode,
107				struct ext2_dx_root_info * rootnode,
108				struct ext2_dx_entry *ent,
109				char *buf, int level)
110{
111	struct ext2_dx_countlimit	limit;
112	struct ext2_dx_entry		e;
113	int				hash, i;
114
115
116	limit = *((struct ext2_dx_countlimit *) ent);
117	limit.count = ext2fs_le16_to_cpu(limit.count);
118	limit.limit = ext2fs_le16_to_cpu(limit.limit);
119
120	fprintf(pager, "Number of entries (count): %d\n", limit.count);
121	fprintf(pager, "Number of entries (limit): %d\n", limit.limit);
122
123	for (i=0; i < limit.count; i++) {
124		hash = i ? ext2fs_le32_to_cpu(ent[i].hash) : 0;
125		fprintf(pager, "Entry #%d: Hash 0x%08x%s, block %u\n", i,
126			hash, (hash & 1) ? " (**)" : "",
127			ext2fs_le32_to_cpu(ent[i].block));
128		}
129
130	fprintf(pager, "\n");
131
132	for (i=0; i < limit.count; i++) {
133		e.hash = ext2fs_le32_to_cpu(ent[i].hash);
134		e.block = ext2fs_le32_to_cpu(ent[i].block);
135		fprintf(pager, "Entry #%d: Hash 0x%08x, block %u\n", i,
136		       i ? e.hash : 0, e.block);
137		if (level)
138			htree_dump_int_block(fs, ino, inode, rootnode,
139					     e.block, buf, level-1);
140		else
141			htree_dump_leaf_node(fs, ino, inode, rootnode,
142					     e.block, buf);
143	}
144
145	fprintf(pager, "---------------------\n");
146}
147
148static void htree_dump_int_block(ext2_filsys fs, ext2_ino_t ino,
149				 struct ext2_inode *inode,
150				 struct ext2_dx_root_info * rootnode,
151				 blk_t blk, char *buf, int level)
152{
153	char		*cbuf;
154	errcode_t	errcode;
155	blk_t		pblk;
156
157	cbuf = malloc(fs->blocksize);
158	if (!cbuf) {
159		fprintf(pager, "Couldn't allocate child block.\n");
160		return;
161	}
162
163	errcode = ext2fs_bmap(fs, ino, inode, buf, 0, blk, &pblk);
164	if (errcode) {
165		com_err("htree_dump_int_block", errcode,
166			"while mapping logical block %u\n", blk);
167		goto errout;
168	}
169
170	errcode = io_channel_read_blk(current_fs->io, pblk, 1, buf);
171	if (errcode) {
172		com_err("htree_dump_int_block", errcode,
173			"while 	reading block %u\n", blk);
174		goto errout;
175	}
176
177	htree_dump_int_node(fs, ino, inode, rootnode,
178			    (struct ext2_dx_entry *) (buf+8),
179			    cbuf, level);
180errout:
181	free(cbuf);
182}
183
184
185
186void do_htree_dump(int argc, char *argv[])
187{
188	ext2_ino_t	ino;
189	struct ext2_inode inode;
190	int		c;
191	int		long_opt = 0;
192	blk_t		blk;
193	char		*buf = NULL;
194	struct 		ext2_dx_root_info  *rootnode;
195	struct 		ext2_dx_entry *ent;
196	struct		ext2_dx_countlimit *limit;
197	errcode_t	errcode;
198
199	if (check_fs_open(argv[0]))
200		return;
201
202	pager = open_pager();
203
204	reset_getopt();
205	while ((c = getopt (argc, argv, "l")) != EOF) {
206		switch (c) {
207		case 'l':
208			long_opt++;
209			break;
210		default:
211			goto print_usage;
212		}
213	}
214
215	if (argc > optind+1) {
216	print_usage:
217		com_err(0, 0, "Usage: htree_dump [-l] file");
218		goto errout;
219	}
220
221	if (argc == optind)
222		ino = cwd;
223	else
224		ino = string_to_inode(argv[optind]);
225	if (!ino)
226		goto errout;
227
228	if (debugfs_read_inode(ino, &inode, argv[1]))
229		goto errout;
230
231	if (!LINUX_S_ISDIR(inode.i_mode)) {
232		com_err(argv[0], 0, "Not a directory");
233		goto errout;
234	}
235
236	if ((inode.i_flags & EXT2_BTREE_FL) == 0) {
237		com_err(argv[0], 0, "Not a hash-indexed directory");
238		goto errout;
239	}
240
241	buf = malloc(2*current_fs->blocksize);
242	if (!buf) {
243		com_err(argv[0], 0, "Couldn't allocate htree buffer");
244		goto errout;
245	}
246
247	errcode = ext2fs_bmap(current_fs, ino, &inode, buf, 0, 0, &blk);
248	if (errcode) {
249		com_err("do_htree_block", errcode,
250			"while mapping logical block 0\n");
251		goto errout;
252	}
253
254	errcode = io_channel_read_blk(current_fs->io, blk,
255				      1, buf);
256	if (errcode) {
257		com_err(argv[0], errcode, "Error reading root node");
258		goto errout;
259	}
260
261	rootnode = (struct ext2_dx_root_info *) (buf + 24);
262
263	fprintf(pager, "Root node dump:\n");
264	fprintf(pager, "\t Reserved zero: %u\n", rootnode->reserved_zero);
265	fprintf(pager, "\t Hash Version: %d\n", rootnode->hash_version);
266	fprintf(pager, "\t Info length: %d\n", rootnode->info_length);
267	fprintf(pager, "\t Indirect levels: %d\n", rootnode->indirect_levels);
268	fprintf(pager, "\t Flags: %d\n", rootnode->unused_flags);
269
270	ent = (struct ext2_dx_entry *) (buf + 24 + rootnode->info_length);
271	limit = (struct ext2_dx_countlimit *) ent;
272
273	htree_dump_int_node(current_fs, ino, &inode, rootnode, ent,
274			    buf + current_fs->blocksize,
275			    rootnode->indirect_levels);
276
277errout:
278	if (buf)
279		free(buf);
280	close_pager(pager);
281}
282
283/*
284 * This function prints the hash of a given file.
285 */
286void do_dx_hash(int argc, char *argv[])
287{
288	ext2_dirhash_t hash, minor_hash;
289	errcode_t	err;
290	int		c;
291	int		hash_version = 0;
292	__u32		hash_seed[4];
293
294	hash_seed[0] = hash_seed[1] = hash_seed[2] = hash_seed[3] = 0;
295
296	reset_getopt();
297	while ((c = getopt (argc, argv, "h:")) != EOF) {
298		switch (c) {
299		case 'h':
300			hash_version = atoi(optarg);
301			break;
302		default:
303			goto print_usage;
304		}
305	}
306	if (optind != argc-1) {
307	print_usage:
308		com_err(argv[0], 0, "usage: dx_hash filename");
309		return;
310	}
311	err = ext2fs_dirhash(hash_version, argv[optind], strlen(argv[optind]),
312			     hash_seed, &hash, &minor_hash);
313	if (err) {
314		com_err(argv[0], err, "while caclulating hash");
315		return;
316	}
317	printf("Hash of %s is 0x%0x (minor 0x%0x)\n", argv[optind],
318	       hash, minor_hash);
319}
320
321/*
322 * Search for particular directory entry (useful for debugging very
323 * large hash tree directories that have lost some blocks from the
324 * btree index).
325 */
326struct process_block_struct {
327	char	*search_name;
328	char	*buf;
329	int	len;
330};
331
332static int search_dir_block(ext2_filsys fs, blk_t *blocknr,
333			    e2_blkcnt_t blockcnt, blk_t ref_blk,
334			    int ref_offset, void *priv_data);
335
336void do_dirsearch(int argc, char *argv[])
337{
338	ext2_ino_t	inode;
339	struct process_block_struct pb;
340
341	if (check_fs_open(argv[0]))
342		return;
343
344	if (argc != 3) {
345		com_err(0, 0, "Usage: dirsearch dir filename");
346		return;
347	}
348
349	inode = string_to_inode(argv[1]);
350	if (!inode)
351		return;
352
353	pb.buf = malloc(current_fs->blocksize);
354	if (!pb.buf) {
355		com_err("dirsearch", 0, "Couldn't allocate buffer");
356		return;
357	}
358	pb.search_name = argv[2];
359	pb.len = strlen(pb.search_name);
360
361	ext2fs_block_iterate2(current_fs, inode, BLOCK_FLAG_READ_ONLY, 0,
362			      search_dir_block, &pb);
363
364	free(pb.buf);
365}
366
367
368static int search_dir_block(ext2_filsys fs, blk_t *blocknr,
369			    e2_blkcnt_t blockcnt,
370			    blk_t ref_blk EXT2FS_ATTR((unused)),
371			    int ref_offset EXT2FS_ATTR((unused)),
372			    void *priv_data)
373{
374	struct process_block_struct *p;
375	struct ext2_dir_entry *dirent;
376	errcode_t	       	errcode;
377	unsigned int		offset = 0;
378	int			rec_len;
379
380	if (blockcnt < 0)
381		return 0;
382
383	p = (struct process_block_struct *) priv_data;
384
385	errcode = io_channel_read_blk(current_fs->io, *blocknr, 1, p->buf);
386	if (errcode) {
387		com_err("search_dir_block", errcode,
388			"while reading block %lu", (unsigned long) *blocknr);
389		return BLOCK_ABORT;
390	}
391
392	while (offset < fs->blocksize) {
393		dirent = (struct ext2_dir_entry *) (p->buf + offset);
394		rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
395			dirent->rec_len : 65536;
396		if (dirent->inode &&
397		    p->len == (dirent->name_len & 0xFF) &&
398		    strncmp(p->search_name, dirent->name,
399			    p->len) == 0) {
400			printf("Entry found at logical block %lld, "
401			       "phys %u, offset %u\n", (long long)blockcnt,
402			       *blocknr, offset);
403			printf("offset %u\n", offset);
404			return BLOCK_ABORT;
405		}
406		offset += rec_len;
407	}
408	return 0;
409}
410
411