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