]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - db/namei.c
xfs: cache last bitmap block in realtime allocator
[thirdparty/xfsprogs-dev.git] / db / namei.c
CommitLineData
d29084ce
DW
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2021 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <djwong@kernel.org>
5 */
6#include "libxfs.h"
7#include "command.h"
8#include "output.h"
9#include "init.h"
10#include "io.h"
11#include "type.h"
12#include "input.h"
13#include "faddr.h"
14#include "fprint.h"
15#include "field.h"
16#include "inode.h"
17
18/* Path lookup */
19
20/* Key for looking up metadata inodes. */
21struct dirpath {
22 /* Array of string pointers. */
23 char **path;
24
25 /* Number of strings in path. */
26 unsigned int depth;
27};
28
29static void
30path_free(
31 struct dirpath *dirpath)
32{
33 unsigned int i;
34
35 for (i = 0; i < dirpath->depth; i++)
36 free(dirpath->path[i]);
37 free(dirpath->path);
38 free(dirpath);
39}
40
41/* Chop a freeform string path into a structured path. */
42static struct dirpath *
43path_parse(
44 const char *path)
45{
46 struct dirpath *dirpath;
47 const char *p = path;
48 const char *endp = path + strlen(path);
49
50 dirpath = calloc(sizeof(*dirpath), 1);
51 if (!dirpath)
52 return NULL;
53
54 while (p < endp) {
55 char **new_path;
56 const char *next_slash;
57
58 next_slash = strchr(p, '/');
59 if (next_slash == p) {
60 p++;
61 continue;
62 }
63 if (!next_slash)
64 next_slash = endp;
65
66 new_path = realloc(dirpath->path,
67 (dirpath->depth + 1) * sizeof(char *));
68 if (!new_path) {
69 path_free(dirpath);
70 return NULL;
71 }
72
73 dirpath->path = new_path;
74 dirpath->path[dirpath->depth] = strndup(p, next_slash - p);
75 dirpath->depth++;
76
77 p = next_slash + 1;
78 }
79
80 return dirpath;
81}
82
83/* Given a directory and a structured path, walk the path and set the cursor. */
84static int
85path_navigate(
86 struct xfs_mount *mp,
87 xfs_ino_t rootino,
88 struct dirpath *dirpath)
89{
90 struct xfs_inode *dp;
91 xfs_ino_t ino = rootino;
92 unsigned int i;
93 int error;
94
95 error = -libxfs_iget(mp, NULL, ino, 0, &dp);
96 if (error)
97 return error;
98
99 for (i = 0; i < dirpath->depth; i++) {
100 struct xfs_name xname = {
4f82f921 101 .name = (unsigned char *)dirpath->path[i],
d29084ce
DW
102 .len = strlen(dirpath->path[i]),
103 };
104
105 if (!S_ISDIR(VFS_I(dp)->i_mode)) {
106 error = ENOTDIR;
107 goto rele;
108 }
109
110 error = -libxfs_dir_lookup(NULL, dp, &xname, &ino, NULL);
111 if (error)
112 goto rele;
113 if (!xfs_verify_ino(mp, ino)) {
114 error = EFSCORRUPTED;
115 goto rele;
116 }
117
118 libxfs_irele(dp);
119 dp = NULL;
120
121 error = -libxfs_iget(mp, NULL, ino, 0, &dp);
122 switch (error) {
123 case EFSCORRUPTED:
124 case EFSBADCRC:
125 case 0:
126 break;
127 default:
128 return error;
129 }
130 }
131
132 set_cur_inode(ino);
133rele:
134 if (dp)
135 libxfs_irele(dp);
136 return error;
137}
138
139/* Walk a directory path to an inode and set the io cursor to that inode. */
140static int
141path_walk(
142 char *path)
143{
144 struct dirpath *dirpath;
145 char *p = path;
146 xfs_ino_t rootino = mp->m_sb.sb_rootino;
147 int error = 0;
148
149 if (*p == '/') {
150 /* Absolute path, start from the root inode. */
151 p++;
152 } else {
153 /* Relative path, start from current dir. */
154 if (iocur_top->typ != &typtab[TYP_INODE] ||
155 !S_ISDIR(iocur_top->mode))
156 return ENOTDIR;
157
158 rootino = iocur_top->ino;
159 }
160
161 dirpath = path_parse(p);
162 if (!dirpath)
163 return ENOMEM;
164
165 error = path_navigate(mp, rootino, dirpath);
166 path_free(dirpath);
167 return error;
168}
169
170static void
171path_help(void)
172{
173 dbprintf(_(
174"\n"
175" Navigate to an inode via directory path.\n"
176 ));
177}
178
179static int
180path_f(
181 int argc,
182 char **argv)
183{
184 int c;
185 int error;
186
187 while ((c = getopt(argc, argv, "")) != -1) {
188 switch (c) {
189 default:
190 path_help();
191 return 0;
192 }
193 }
194
195 error = path_walk(argv[optind]);
196 if (error) {
197 dbprintf("%s: %s\n", argv[optind], strerror(error));
198 exitcode = 1;
199 }
200
201 return 0;
202}
203
204static struct cmdinfo path_cmd = {
205 .name = "path",
206 .altname = NULL,
207 .cfunc = path_f,
208 .argmin = 1,
209 .argmax = 1,
210 .canpush = 0,
211 .args = "",
212 .help = path_help,
213};
214
08f24589
DW
215/* List a directory's entries. */
216
217static const char *filetype_strings[XFS_DIR3_FT_MAX] = {
218 [XFS_DIR3_FT_UNKNOWN] = "unknown",
219 [XFS_DIR3_FT_REG_FILE] = "regular",
220 [XFS_DIR3_FT_DIR] = "directory",
221 [XFS_DIR3_FT_CHRDEV] = "chardev",
222 [XFS_DIR3_FT_BLKDEV] = "blkdev",
223 [XFS_DIR3_FT_FIFO] = "fifo",
224 [XFS_DIR3_FT_SOCK] = "socket",
225 [XFS_DIR3_FT_SYMLINK] = "symlink",
226 [XFS_DIR3_FT_WHT] = "whiteout",
227};
228
229static const char *
230get_dstr(
231 struct xfs_mount *mp,
232 uint8_t filetype)
233{
2660e653 234 if (!xfs_has_ftype(mp))
08f24589
DW
235 return filetype_strings[XFS_DIR3_FT_UNKNOWN];
236
237 if (filetype >= XFS_DIR3_FT_MAX)
238 return filetype_strings[XFS_DIR3_FT_UNKNOWN];
239
240 return filetype_strings[filetype];
241}
242
243static void
244dir_emit(
245 struct xfs_mount *mp,
246 xfs_dir2_dataptr_t off,
247 char *name,
248 ssize_t namelen,
249 xfs_ino_t ino,
250 uint8_t dtype)
251{
252 char *display_name;
4f82f921 253 struct xfs_name xname = { .name = (unsigned char *)name };
08f24589
DW
254 const char *dstr = get_dstr(mp, dtype);
255 xfs_dahash_t hash;
256 bool good;
257
258 if (namelen < 0) {
259 /* Negative length means that name is null-terminated. */
260 display_name = name;
261 xname.len = strlen(name);
262 good = true;
263 } else {
264 /*
265 * Otherwise, name came from a directory entry, so we have to
266 * copy the string to a buffer so that we can add the null
267 * terminator.
268 */
269 display_name = malloc(namelen + 1);
270 memcpy(display_name, name, namelen);
271 display_name[namelen] = 0;
272 xname.len = namelen;
273 good = libxfs_dir2_namecheck(name, namelen);
274 }
275 hash = libxfs_dir2_hashname(mp, &xname);
276
277 dbprintf("%-10u %-18llu %-14s 0x%08llx %3d %s %s\n", off & 0xFFFFFFFF,
278 ino, dstr, hash, xname.len,
279 display_name, good ? _("(good)") : _("(corrupt)"));
280
281 if (display_name != name)
282 free(display_name);
283}
284
285static int
286list_sfdir(
287 struct xfs_da_args *args)
288{
289 struct xfs_inode *dp = args->dp;
290 struct xfs_mount *mp = dp->i_mount;
291 struct xfs_da_geometry *geo = args->geo;
292 struct xfs_dir2_sf_entry *sfep;
293 struct xfs_dir2_sf_hdr *sfp;
294 xfs_ino_t ino;
295 xfs_dir2_dataptr_t off;
296 unsigned int i;
297 uint8_t filetype;
298
299 sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
300
301 /* . and .. entries */
302 off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
303 geo->data_entry_offset);
304 dir_emit(args->dp->i_mount, off, ".", -1, dp->i_ino, XFS_DIR3_FT_DIR);
305
306 ino = libxfs_dir2_sf_get_parent_ino(sfp);
307 off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
308 geo->data_entry_offset +
309 libxfs_dir2_data_entsize(mp, sizeof(".") - 1));
310 dir_emit(args->dp->i_mount, off, "..", -1, ino, XFS_DIR3_FT_DIR);
311
312 /* Walk everything else. */
313 sfep = xfs_dir2_sf_firstentry(sfp);
314 for (i = 0; i < sfp->count; i++) {
315 ino = libxfs_dir2_sf_get_ino(mp, sfp, sfep);
316 filetype = libxfs_dir2_sf_get_ftype(mp, sfep);
317 off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
318 xfs_dir2_sf_get_offset(sfep));
319
320 dir_emit(args->dp->i_mount, off, (char *)sfep->name,
321 sfep->namelen, ino, filetype);
322 sfep = libxfs_dir2_sf_nextentry(mp, sfp, sfep);
323 }
324
325 return 0;
326}
327
328/* List entries in block format directory. */
329static int
330list_blockdir(
331 struct xfs_da_args *args)
332{
333 struct xfs_inode *dp = args->dp;
334 struct xfs_mount *mp = dp->i_mount;
335 struct xfs_buf *bp;
336 struct xfs_da_geometry *geo = mp->m_dir_geo;
337 xfs_dir2_dataptr_t diroff;
338 unsigned int offset;
339 unsigned int end;
340 int error;
341
342 error = xfs_dir3_block_read(NULL, dp, &bp);
343 if (error)
344 return error;
345
346 end = xfs_dir3_data_end_offset(geo, bp->b_addr);
347 for (offset = geo->data_entry_offset; offset < end;) {
348 struct xfs_dir2_data_unused *dup = bp->b_addr + offset;
349 struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
350 uint8_t filetype;
351
352 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
353 /* Unused entry */
354 offset += be16_to_cpu(dup->length);
355 continue;
356 }
357
358 /* Real entry */
359 diroff = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, offset);
360 offset += libxfs_dir2_data_entsize(mp, dep->namelen);
361 filetype = libxfs_dir2_data_get_ftype(dp->i_mount, dep);
362 dir_emit(mp, diroff, (char *)dep->name, dep->namelen,
363 be64_to_cpu(dep->inumber), filetype);
364 }
365
366 libxfs_trans_brelse(args->trans, bp);
367 return error;
368}
369
370/* List entries in leaf format directory. */
371static int
372list_leafdir(
373 struct xfs_da_args *args)
374{
375 struct xfs_bmbt_irec map;
376 struct xfs_iext_cursor icur;
377 struct xfs_inode *dp = args->dp;
378 struct xfs_mount *mp = dp->i_mount;
379 struct xfs_buf *bp = NULL;
722e81c1 380 struct xfs_ifork *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
08f24589
DW
381 struct xfs_da_geometry *geo = mp->m_dir_geo;
382 xfs_dir2_off_t dirboff;
383 xfs_dablk_t dabno = 0;
384 int error = 0;
385
386 /* Read extent map. */
30de9f1c
CH
387 error = -libxfs_iread_extents(NULL, dp, XFS_DATA_FORK);
388 if (error)
389 return error;
08f24589
DW
390
391 while (dabno < geo->leafblk) {
392 unsigned int offset;
393 unsigned int length;
394
395 /* Find mapping for leaf block. */
396 if (!xfs_iext_lookup_extent(dp, ifp, dabno, &icur, &map))
397 break;
398 if (map.br_startoff >= geo->leafblk)
399 break;
400 libxfs_trim_extent(&map, dabno, geo->leafblk - dabno);
401
402 /* Read the directory block of that first mapping. */
403 error = xfs_dir3_data_read(NULL, dp, map.br_startoff, 0, &bp);
404 if (error)
405 break;
406
407 dirboff = xfs_dir2_da_to_byte(geo, map.br_startoff);
408 for (offset = geo->data_entry_offset; offset < geo->blksize;) {
409 struct xfs_dir2_data_entry *dep;
410 struct xfs_dir2_data_unused *dup;
411 uint8_t filetype;
412
413 dup = bp->b_addr + offset;
414 dep = bp->b_addr + offset;
415
416 if (be16_to_cpu(dup->freetag) ==
417 XFS_DIR2_DATA_FREE_TAG) {
418 /* Skip unused entry */
419 length = be16_to_cpu(dup->length);
420 offset += length;
421 continue;
422 }
423
424 offset += libxfs_dir2_data_entsize(mp, dep->namelen);
425 filetype = libxfs_dir2_data_get_ftype(mp, dep);
426
427 dir_emit(mp, xfs_dir2_byte_to_dataptr(dirboff + offset),
428 (char *)dep->name, dep->namelen,
429 be64_to_cpu(dep->inumber), filetype);
430 }
431
432 dabno += XFS_DADDR_TO_FSB(mp, bp->b_length);
433 libxfs_buf_relse(bp);
434 bp = NULL;
435 }
436
437 if (bp)
438 libxfs_buf_relse(bp);
439
440 return error;
441}
442
443/* Read the directory, display contents. */
b6fef47a 444static int
08f24589
DW
445listdir(
446 struct xfs_inode *dp)
447{
448 struct xfs_da_args args = {
449 .dp = dp,
450 .geo = dp->i_mount->m_dir_geo,
451 };
452 int error;
1a3bfffe 453 bool isblock;
08f24589
DW
454
455 if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
456 return list_sfdir(&args);
457
458 error = -libxfs_dir2_isblock(&args, &isblock);
459 if (error)
460 return error;
461
462 if (isblock)
463 return list_blockdir(&args);
464 return list_leafdir(&args);
465}
466
467/* List the inode number of the currently selected inode. */
468static int
469inum_cur(void)
470{
471 if (iocur_top->typ != &typtab[TYP_INODE])
472 return ENOENT;
473
474 dbprintf("%llu\n", iocur_top->ino);
475 return 0;
476}
477
478/* If the io cursor points to a directory, list its contents. */
479static int
480ls_cur(
481 char *tag)
482{
483 struct xfs_inode *dp;
484 int error = 0;
485
486 if (iocur_top->typ != &typtab[TYP_INODE] ||
487 !S_ISDIR(iocur_top->mode))
488 return ENOTDIR;
489
490 error = -libxfs_iget(mp, NULL, iocur_top->ino, 0, &dp);
491 if (error)
492 return error;
493
494 if (!S_ISDIR(VFS_I(dp)->i_mode)) {
495 error = ENOTDIR;
496 goto rele;
497 }
498
499 /* List the contents of a directory. */
500 if (tag)
501 dbprintf(_("%s:\n"), tag);
502
503 error = listdir(dp);
504 if (error)
505 goto rele;
506
507rele:
508 libxfs_irele(dp);
509 return error;
510}
511
512static void
513ls_help(void)
514{
515 dbprintf(_(
516"\n"
517" List the contents of the currently selected directory inode.\n"
518"\n"
519" Options:\n"
520" -i -- Resolve the given paths to their corresponding inode numbers.\n"
521" If no paths are given, display the current inode number.\n"
522"\n"
523" Directory contents will be listed in the format:\n"
524" dir_cookie inode_number type hash name_length name\n"
525 ));
526}
527
528static int
529ls_f(
530 int argc,
531 char **argv)
532{
533 bool inum_only = false;
534 int c;
535 int error = 0;
536
537 while ((c = getopt(argc, argv, "i")) != -1) {
538 switch (c) {
539 case 'i':
540 inum_only = true;
541 break;
542 default:
543 ls_help();
544 return 0;
545 }
546 }
547
548 if (optind == argc) {
549 if (inum_only)
550 error = inum_cur();
551 else
552 error = ls_cur(NULL);
553 if (error) {
554 dbprintf("%s\n", strerror(error));
555 exitcode = 1;
556 }
557
558 return 0;
559 }
560
561 for (c = optind; c < argc; c++) {
562 push_cur();
563
564 error = path_walk(argv[c]);
565 if (error)
566 goto err_cur;
567
568 if (inum_only)
569 error = inum_cur();
570 else
571 error = ls_cur(argv[c]);
572 if (error)
573 goto err_cur;
574
575 pop_cur();
576 }
577
578 return 0;
579err_cur:
580 pop_cur();
581 if (error) {
582 dbprintf("%s: %s\n", argv[c], strerror(error));
583 exitcode = 1;
584 }
585 return 0;
586}
587
588static struct cmdinfo ls_cmd = {
589 .name = "ls",
590 .altname = "l",
591 .cfunc = ls_f,
592 .argmin = 0,
593 .argmax = -1,
594 .canpush = 0,
595 .args = "[-i] [paths...]",
596 .help = ls_help,
597};
598
d29084ce
DW
599void
600namei_init(void)
601{
602 path_cmd.oneline = _("navigate to an inode by path");
603 add_command(&path_cmd);
08f24589
DW
604
605 ls_cmd.oneline = _("list directory contents");
606 add_command(&ls_cmd);
d29084ce 607}