]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - debugfs/htree.c
ext2fs: convert unicode normalization from NFKD -> NFD
[thirdparty/e2fsprogs.git] / debugfs / htree.c
CommitLineData
df614db6
TT
1/*
2 * htree.c --- hash tree routines
efc6f628 3 *
df614db6
TT
4 * Copyright (C) 2002 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
d1154eb4 8#include "config.h"
df614db6
TT
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>
efc6f628 21#else
df614db6
TT
22extern int optind;
23extern char *optarg;
24#endif
df614db6
TT
25
26#include "debugfs.h"
42080a86
TT
27#include "uuid/uuid.h"
28#include "e2p/e2p.h"
df614db6 29
ef733f1a
GKB
30#include "ext2fs/nls.h"
31
df614db6
TT
32static FILE *pager;
33
df614db6
TT
34static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
35 struct ext2_inode *inode,
3e699064 36 struct ext2_dx_root_info * rootnode,
048786d7 37 blk64_t blk, char *buf)
df614db6
TT
38{
39 errcode_t errcode;
40 struct ext2_dir_entry *dirent;
54434927
TT
41 int thislen, col = 0;
42 unsigned int offset = 0;
b772900b 43 char name[EXT2_NAME_LEN + 1];
9c23b896 44 char tmp[EXT2_NAME_LEN + 64];
048786d7 45 blk64_t pblk;
226515df 46 ext2_dirhash_t hash, minor_hash;
8a480350
TT
47 unsigned int rec_len;
48 int hash_alg;
ef733f1a 49 int hash_flags = inode->i_flags & EXT4_CASEFOLD_FL;
81683c6a
DW
50 int csum_size = 0;
51
4ee26699 52 if (ext2fs_has_feature_metadata_csum(fs->super))
81683c6a 53 csum_size = sizeof(struct ext2_dir_entry_tail);
efc6f628 54
048786d7 55 errcode = ext2fs_bmap2(fs, ino, inode, buf, 0, blk, 0, &pblk);
df614db6
TT
56 if (errcode) {
57 com_err("htree_dump_leaf_node", errcode,
048786d7 58 "while mapping logical block %llu\n", blk);
df614db6
TT
59 return;
60 }
61
e6575ce3 62 fprintf(pager, "Reading directory block %llu, phys %llu\n", blk, pblk);
81683c6a 63 errcode = ext2fs_read_dir_block4(current_fs, pblk, buf, 0, ino);
df614db6
TT
64 if (errcode) {
65 com_err("htree_dump_leaf_node", errcode,
048786d7
VAH
66 "while reading block %llu (%llu)\n",
67 blk, pblk);
df614db6
TT
68 return;
69 }
f77704e4
TT
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;
df614db6 74
41bf5993 75 while (offset < fs->blocksize) {
df614db6 76 dirent = (struct ext2_dir_entry *) (buf + offset);
8a480350
TT
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 }
70f4632b 84 thislen = ext2fs_dirent_name_len(dirent);
5dd77dbe
TT
85 if (((offset + rec_len) > fs->blocksize) ||
86 (rec_len < 8) ||
87 ((rec_len % 4) != 0) ||
df0b907e 88 ((unsigned) thislen + 8 > rec_len)) {
048786d7
VAH
89 fprintf(pager, "Corrupted directory block (%llu)!\n",
90 blk);
df614db6
TT
91 break;
92 }
df614db6
TT
93 strncpy(name, dirent->name, thislen);
94 name[thislen] = '\0';
ef733f1a
GKB
95 errcode = ext2fs_dirhash2(hash_alg, name, thislen,
96 fs->encoding, hash_flags,
97 fs->super->s_hash_seed,
98 &hash, &minor_hash);
52783e0c
TT
99 if (errcode)
100 com_err("htree_dump_leaf_node", errcode,
101 "while calculating hash");
41bf5993
TT
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 }
df614db6
TT
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;
5dd77dbe 126 offset += rec_len;
df614db6
TT
127 }
128 fprintf(pager, "\n");
129}
130
131
132static void htree_dump_int_block(ext2_filsys fs, ext2_ino_t ino,
133 struct ext2_inode *inode,
3e699064 134 struct ext2_dx_root_info * rootnode,
048786d7 135 blk64_t blk, char *buf, int level);
df614db6
TT
136
137
138static void htree_dump_int_node(ext2_filsys fs, ext2_ino_t ino,
139 struct ext2_inode *inode,
3e699064 140 struct ext2_dx_root_info * rootnode,
efc6f628 141 struct ext2_dx_entry *ent,
df614db6
TT
142 char *buf, int level)
143{
77b493e3 144 struct ext2_dx_countlimit dx_countlimit;
a996466d 145 struct ext2_dx_tail *tail;
42e5b5f9 146 int hash, i;
77b493e3 147 int limit, count;
a996466d 148 int remainder;
df614db6 149
77b493e3
ES
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);
df614db6 153
77b493e3
ES
154 fprintf(pager, "Number of entries (count): %d\n", count);
155 fprintf(pager, "Number of entries (limit): %d\n", limit);
df614db6 156
77b493e3 157 remainder = fs->blocksize - (limit * sizeof(struct ext2_dx_entry));
a996466d
DW
158 if (ent == (struct ext2_dx_entry *)(rootnode + 1))
159 remainder -= sizeof(struct ext2_dx_root_info) + 24;
160 else
161 remainder -= 8;
4ee26699 162 if (ext2fs_has_feature_metadata_csum(fs->super) &&
a996466d 163 remainder == sizeof(struct ext2_dx_tail)) {
77b493e3 164 tail = (struct ext2_dx_tail *)(ent + limit);
a996466d
DW
165 fprintf(pager, "Checksum: 0x%08x\n",
166 ext2fs_le32_to_cpu(tail->dt_checksum));
167 }
168
77b493e3 169 for (i=0; i < count; i++) {
42e5b5f9 170 hash = i ? ext2fs_le32_to_cpu(ent[i].hash) : 0;
d0ff90d5 171 fprintf(pager, "Entry #%d: Hash 0x%08x%s, block %u\n", i,
42e5b5f9 172 hash, (hash & 1) ? " (**)" : "",
621732c9 173 ext2fs_le32_to_cpu(ent[i].block));
42e5b5f9 174 }
df614db6
TT
175
176 fprintf(pager, "\n");
177
77b493e3 178 for (i=0; i < count; i++) {
df0b907e 179 unsigned int hashval, block;
77b493e3 180
df0b907e 181 hashval = ext2fs_le32_to_cpu(ent[i].hash);
77b493e3 182 block = ext2fs_le32_to_cpu(ent[i].block);
8deb80a5 183 fprintf(pager, "Entry #%d: Hash 0x%08x, block %u\n", i,
df0b907e 184 i ? hashval : 0, block);
df614db6 185 if (level)
3e699064 186 htree_dump_int_block(fs, ino, inode, rootnode,
77b493e3 187 block, buf, level-1);
df614db6 188 else
3e699064 189 htree_dump_leaf_node(fs, ino, inode, rootnode,
77b493e3 190 block, buf);
df614db6
TT
191 }
192
193 fprintf(pager, "---------------------\n");
194}
195
196static void htree_dump_int_block(ext2_filsys fs, ext2_ino_t ino,
197 struct ext2_inode *inode,
3e699064 198 struct ext2_dx_root_info * rootnode,
048786d7 199 blk64_t blk, char *buf, int level)
df614db6
TT
200{
201 char *cbuf;
202 errcode_t errcode;
048786d7 203 blk64_t pblk;
df614db6
TT
204
205 cbuf = malloc(fs->blocksize);
206 if (!cbuf) {
207 fprintf(pager, "Couldn't allocate child block.\n");
208 return;
209 }
efc6f628 210
048786d7 211 errcode = ext2fs_bmap2(fs, ino, inode, buf, 0, blk, 0, &pblk);
df614db6
TT
212 if (errcode) {
213 com_err("htree_dump_int_block", errcode,
048786d7 214 "while mapping logical block %llu\n", blk);
89456558 215 goto errout;
df614db6
TT
216 }
217
24a117ab 218 errcode = io_channel_read_blk64(current_fs->io, pblk, 1, buf);
df614db6
TT
219 if (errcode) {
220 com_err("htree_dump_int_block", errcode,
048786d7 221 "while reading block %llu\n", blk);
89456558 222 goto errout;
df614db6
TT
223 }
224
3e699064 225 htree_dump_int_node(fs, ino, inode, rootnode,
503f9e7f 226 (struct ext2_dx_entry *) (buf+8),
df614db6 227 cbuf, level);
89456558 228errout:
df614db6
TT
229 free(cbuf);
230}
231
232
233
2fcbcb1b
TT
234void do_htree_dump(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
235 void *infop EXT2FS_ATTR((unused)))
df614db6
TT
236{
237 ext2_ino_t ino;
238 struct ext2_inode inode;
048786d7 239 blk64_t blk;
3e699064
TT
240 char *buf = NULL;
241 struct ext2_dx_root_info *rootnode;
df614db6 242 struct ext2_dx_entry *ent;
df614db6
TT
243 errcode_t errcode;
244
245 if (check_fs_open(argv[0]))
246 return;
247
248 pager = open_pager();
249
1ec5d104 250 if (common_inode_args_process(argc, argv, &ino, 0))
155f577b 251 goto errout;
df614db6
TT
252
253 if (debugfs_read_inode(ino, &inode, argv[1]))
155f577b 254 goto errout;
df614db6
TT
255
256 if (!LINUX_S_ISDIR(inode.i_mode)) {
257 com_err(argv[0], 0, "Not a directory");
155f577b 258 goto errout;
df614db6 259 }
efc6f628 260
df614db6
TT
261 if ((inode.i_flags & EXT2_BTREE_FL) == 0) {
262 com_err(argv[0], 0, "Not a hash-indexed directory");
155f577b 263 goto errout;
df614db6
TT
264 }
265
266 buf = malloc(2*current_fs->blocksize);
267 if (!buf) {
268 com_err(argv[0], 0, "Couldn't allocate htree buffer");
155f577b 269 goto errout;
df614db6
TT
270 }
271
048786d7 272 errcode = ext2fs_bmap2(current_fs, ino, &inode, buf, 0, 0, 0, &blk);
226515df
TT
273 if (errcode) {
274 com_err("do_htree_block", errcode,
275 "while mapping logical block 0\n");
276 goto errout;
277 }
278
24a117ab
VAH
279 errcode = io_channel_read_blk64(current_fs->io, blk,
280 1, buf);
df614db6
TT
281 if (errcode) {
282 com_err(argv[0], errcode, "Error reading root node");
283 goto errout;
284 }
285
3e699064 286 rootnode = (struct ext2_dx_root_info *) (buf + 24);
df614db6
TT
287
288 fprintf(pager, "Root node dump:\n");
8deb80a5 289 fprintf(pager, "\t Reserved zero: %u\n", rootnode->reserved_zero);
3e699064
TT
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);
df614db6 294
ae9efd05
AB
295 ent = (struct ext2_dx_entry *)
296 ((char *)rootnode + rootnode->info_length);
df614db6 297
3e699064 298 htree_dump_int_node(current_fs, ino, &inode, rootnode, ent,
df614db6 299 buf + current_fs->blocksize,
3e699064 300 rootnode->indirect_levels);
df614db6
TT
301
302errout:
45e338f5 303 free(buf);
df614db6
TT
304 close_pager(pager);
305}
306
307/*
308 * This function prints the hash of a given file.
309 */
2fcbcb1b
TT
310void do_dx_hash(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
311 void *infop EXT2FS_ATTR((unused)))
df614db6 312{
ef733f1a 313 ext2_dirhash_t hash, minor_hash, hash_flags;
52783e0c 314 errcode_t err;
503f9e7f
TT
315 int c;
316 int hash_version = 0;
317 __u32 hash_seed[4];
ef733f1a 318 const struct nls_table *encoding;
efc6f628 319
503f9e7f 320 hash_seed[0] = hash_seed[1] = hash_seed[2] = hash_seed[3] = 0;
88494bb6
TT
321
322 reset_getopt();
f8bd5516 323 while ((c = getopt (argc, argv, "h:s:")) != EOF) {
503f9e7f
TT
324 switch (c) {
325 case 'h':
f8bd5516
TT
326 hash_version = e2p_string2hash(optarg);
327 if (hash_version < 0)
328 hash_version = atoi(optarg);
329 break;
330 case 's':
42080a86 331 if (uuid_parse(optarg, (unsigned char *) hash_seed)) {
f8bd5516
TT
332 fprintf(stderr, "Invalid UUID format: %s\n",
333 optarg);
334 return;
335 }
503f9e7f 336 break;
ef733f1a
GKB
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;
49c6b4e9
TT
346 default:
347 goto print_usage;
503f9e7f
TT
348 }
349 }
350 if (optind != argc-1) {
49c6b4e9 351 print_usage:
f8bd5516 352 com_err(argv[0], 0, "usage: dx_hash [-h hash_alg] "
ef733f1a 353 "[-s hash_seed] [-c] [-e encoding] filename");
df614db6
TT
354 return;
355 }
ef733f1a
GKB
356 err = ext2fs_dirhash2(hash_version, argv[optind],
357 strlen(argv[optind]), encoding, hash_flags,
358 hash_seed, &hash, &minor_hash);
359
52783e0c 360 if (err) {
ce20096f 361 com_err(argv[0], err, "while calculating hash");
52783e0c
TT
362 return;
363 }
503f9e7f
TT
364 printf("Hash of %s is 0x%0x (minor 0x%0x)\n", argv[optind],
365 hash, minor_hash);
df614db6
TT
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 */
373struct process_block_struct {
374 char *search_name;
375 char *buf;
376 int len;
377};
378
048786d7
VAH
379static int search_dir_block(ext2_filsys fs, blk64_t *blocknr,
380 e2_blkcnt_t blockcnt, blk64_t ref_blk,
df614db6
TT
381 int ref_offset, void *priv_data);
382
2fcbcb1b
TT
383void do_dirsearch(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
384 void *infop EXT2FS_ATTR((unused)))
df614db6
TT
385{
386 ext2_ino_t inode;
df614db6 387 struct process_block_struct pb;
efc6f628 388
df614db6
TT
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);
efc6f628 408
048786d7 409 ext2fs_block_iterate3(current_fs, inode, BLOCK_FLAG_READ_ONLY, 0,
43323be9 410 search_dir_block, &pb);
df614db6
TT
411
412 free(pb.buf);
413}
414
415
048786d7 416static int search_dir_block(ext2_filsys fs, blk64_t *blocknr,
efc6f628 417 e2_blkcnt_t blockcnt,
048786d7 418 blk64_t ref_blk EXT2FS_ATTR((unused)),
54434927
TT
419 int ref_offset EXT2FS_ATTR((unused)),
420 void *priv_data)
df614db6
TT
421{
422 struct process_block_struct *p;
423 struct ext2_dir_entry *dirent;
424 errcode_t errcode;
54434927 425 unsigned int offset = 0;
8a480350 426 unsigned int rec_len;
df614db6
TT
427
428 if (blockcnt < 0)
429 return 0;
430
431 p = (struct process_block_struct *) priv_data;
432
24a117ab 433 errcode = io_channel_read_blk64(current_fs->io, *blocknr, 1, p->buf);
df614db6
TT
434 if (errcode) {
435 com_err("search_dir_block", errcode,
54434927 436 "while reading block %lu", (unsigned long) *blocknr);
df614db6
TT
437 return BLOCK_ABORT;
438 }
439
440 while (offset < fs->blocksize) {
441 dirent = (struct ext2_dir_entry *) (p->buf + offset);
8a480350
TT
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);
42080a86 447 return BLOCK_ABORT;
8a480350 448 }
df614db6 449 if (dirent->inode &&
70f4632b 450 p->len == ext2fs_dirent_name_len(dirent) &&
df614db6
TT
451 strncmp(p->search_name, dirent->name,
452 p->len) == 0) {
453 printf("Entry found at logical block %lld, "
048786d7 454 "phys %llu, offset %u\n", (long long)blockcnt,
df614db6 455 *blocknr, offset);
8deb80a5 456 printf("offset %u\n", offset);
df614db6
TT
457 return BLOCK_ABORT;
458 }
5dd77dbe 459 offset += rec_len;
df614db6
TT
460 }
461 return 0;
462}
463