]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - db/namei.c
xfsprogs: Release v6.10.1
[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;
f8146197 293 struct xfs_dir2_sf_hdr *sfp = dp->i_df.if_data;
08f24589
DW
294 xfs_ino_t ino;
295 xfs_dir2_dataptr_t off;
296 unsigned int i;
297 uint8_t filetype;
298
08f24589
DW
299 /* . and .. entries */
300 off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
301 geo->data_entry_offset);
302 dir_emit(args->dp->i_mount, off, ".", -1, dp->i_ino, XFS_DIR3_FT_DIR);
303
304 ino = libxfs_dir2_sf_get_parent_ino(sfp);
305 off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
306 geo->data_entry_offset +
307 libxfs_dir2_data_entsize(mp, sizeof(".") - 1));
308 dir_emit(args->dp->i_mount, off, "..", -1, ino, XFS_DIR3_FT_DIR);
309
310 /* Walk everything else. */
311 sfep = xfs_dir2_sf_firstentry(sfp);
312 for (i = 0; i < sfp->count; i++) {
313 ino = libxfs_dir2_sf_get_ino(mp, sfp, sfep);
314 filetype = libxfs_dir2_sf_get_ftype(mp, sfep);
315 off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
316 xfs_dir2_sf_get_offset(sfep));
317
318 dir_emit(args->dp->i_mount, off, (char *)sfep->name,
319 sfep->namelen, ino, filetype);
320 sfep = libxfs_dir2_sf_nextentry(mp, sfp, sfep);
321 }
322
323 return 0;
324}
325
326/* List entries in block format directory. */
327static int
328list_blockdir(
329 struct xfs_da_args *args)
330{
331 struct xfs_inode *dp = args->dp;
332 struct xfs_mount *mp = dp->i_mount;
333 struct xfs_buf *bp;
334 struct xfs_da_geometry *geo = mp->m_dir_geo;
335 xfs_dir2_dataptr_t diroff;
336 unsigned int offset;
337 unsigned int end;
338 int error;
339
482abce5 340 error = xfs_dir3_block_read(NULL, dp, args->owner, &bp);
08f24589
DW
341 if (error)
342 return error;
343
344 end = xfs_dir3_data_end_offset(geo, bp->b_addr);
345 for (offset = geo->data_entry_offset; offset < end;) {
346 struct xfs_dir2_data_unused *dup = bp->b_addr + offset;
347 struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
348 uint8_t filetype;
349
350 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
351 /* Unused entry */
352 offset += be16_to_cpu(dup->length);
353 continue;
354 }
355
356 /* Real entry */
357 diroff = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, offset);
358 offset += libxfs_dir2_data_entsize(mp, dep->namelen);
359 filetype = libxfs_dir2_data_get_ftype(dp->i_mount, dep);
360 dir_emit(mp, diroff, (char *)dep->name, dep->namelen,
361 be64_to_cpu(dep->inumber), filetype);
362 }
363
364 libxfs_trans_brelse(args->trans, bp);
365 return error;
366}
367
368/* List entries in leaf format directory. */
369static int
370list_leafdir(
371 struct xfs_da_args *args)
372{
373 struct xfs_bmbt_irec map;
374 struct xfs_iext_cursor icur;
375 struct xfs_inode *dp = args->dp;
376 struct xfs_mount *mp = dp->i_mount;
377 struct xfs_buf *bp = NULL;
722e81c1 378 struct xfs_ifork *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
08f24589
DW
379 struct xfs_da_geometry *geo = mp->m_dir_geo;
380 xfs_dir2_off_t dirboff;
381 xfs_dablk_t dabno = 0;
382 int error = 0;
383
384 /* Read extent map. */
30de9f1c
CH
385 error = -libxfs_iread_extents(NULL, dp, XFS_DATA_FORK);
386 if (error)
387 return error;
08f24589
DW
388
389 while (dabno < geo->leafblk) {
390 unsigned int offset;
391 unsigned int length;
392
393 /* Find mapping for leaf block. */
394 if (!xfs_iext_lookup_extent(dp, ifp, dabno, &icur, &map))
395 break;
396 if (map.br_startoff >= geo->leafblk)
397 break;
398 libxfs_trim_extent(&map, dabno, geo->leafblk - dabno);
399
400 /* Read the directory block of that first mapping. */
72386ff0
DW
401 error = xfs_dir3_data_read(NULL, dp, args->owner,
402 map.br_startoff, 0, &bp);
08f24589
DW
403 if (error)
404 break;
405
406 dirboff = xfs_dir2_da_to_byte(geo, map.br_startoff);
407 for (offset = geo->data_entry_offset; offset < geo->blksize;) {
408 struct xfs_dir2_data_entry *dep;
409 struct xfs_dir2_data_unused *dup;
410 uint8_t filetype;
411
412 dup = bp->b_addr + offset;
413 dep = bp->b_addr + offset;
414
415 if (be16_to_cpu(dup->freetag) ==
416 XFS_DIR2_DATA_FREE_TAG) {
417 /* Skip unused entry */
418 length = be16_to_cpu(dup->length);
419 offset += length;
420 continue;
421 }
422
423 offset += libxfs_dir2_data_entsize(mp, dep->namelen);
424 filetype = libxfs_dir2_data_get_ftype(mp, dep);
425
426 dir_emit(mp, xfs_dir2_byte_to_dataptr(dirboff + offset),
427 (char *)dep->name, dep->namelen,
428 be64_to_cpu(dep->inumber), filetype);
429 }
430
431 dabno += XFS_DADDR_TO_FSB(mp, bp->b_length);
432 libxfs_buf_relse(bp);
433 bp = NULL;
434 }
435
436 if (bp)
437 libxfs_buf_relse(bp);
438
439 return error;
440}
441
442/* Read the directory, display contents. */
b6fef47a 443static int
08f24589
DW
444listdir(
445 struct xfs_inode *dp)
446{
447 struct xfs_da_args args = {
448 .dp = dp,
449 .geo = dp->i_mount->m_dir_geo,
7e74984e 450 .owner = dp->i_ino,
08f24589
DW
451 };
452 int error;
08f24589 453
3cedb37a
CH
454 switch (libxfs_dir2_format(&args, &error)) {
455 case XFS_DIR2_FMT_SF:
08f24589 456 return list_sfdir(&args);
3cedb37a 457 case XFS_DIR2_FMT_BLOCK:
08f24589 458 return list_blockdir(&args);
3cedb37a
CH
459 case XFS_DIR2_FMT_LEAF:
460 case XFS_DIR2_FMT_NODE:
461 return list_leafdir(&args);
462 default:
463 return error;
464 }
08f24589
DW
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
34d3b107
DW
599static void
600pptr_emit(
601 struct xfs_inode *ip,
602 unsigned int attr_flags,
603 const uint8_t *name,
604 unsigned int namelen,
605 const void *value,
606 unsigned int valuelen)
607{
608 struct xfs_mount *mp = ip->i_mount;
609 xfs_ino_t parent_ino;
610 uint32_t parent_gen;
611 int error;
612
613 if (!(attr_flags & XFS_ATTR_PARENT))
614 return;
615
616 error = -libxfs_parent_from_attr(mp, attr_flags, name, namelen, value,
617 valuelen, &parent_ino, &parent_gen);
618 if (error)
619 return;
620
621 dbprintf("%18llu:0x%08x %3d %.*s\n", parent_ino, parent_gen, namelen,
622 namelen, name);
623}
624
625static int
626list_sf_pptrs(
627 struct xfs_inode *ip)
628{
629 struct xfs_attr_sf_hdr *hdr = ip->i_af.if_data;
630 struct xfs_attr_sf_entry *sfe;
631 unsigned int i;
632
633 sfe = libxfs_attr_sf_firstentry(hdr);
634 for (i = 0; i < hdr->count; i++) {
635 pptr_emit(ip, sfe->flags, sfe->nameval, sfe->namelen,
636 sfe->nameval + sfe->valuelen, sfe->valuelen);
637
638 sfe = xfs_attr_sf_nextentry(sfe);
639 }
640
641 return 0;
642}
643
644static void
645list_leaf_pptr_entries(
646 struct xfs_inode *ip,
647 struct xfs_buf *bp)
648{
649 struct xfs_attr3_icleaf_hdr ichdr;
650 struct xfs_mount *mp = ip->i_mount;
651 struct xfs_attr_leafblock *leaf = bp->b_addr;
652 struct xfs_attr_leaf_entry *entry;
653 unsigned int i;
654
655 libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);
656 entry = xfs_attr3_leaf_entryp(leaf);
657
658 for (i = 0; i < ichdr.count; entry++, i++) {
659 struct xfs_attr_leaf_name_local *name_loc;
660
661 /*
662 * Parent pointers cannot be remote values; don't bother
663 * decoding this xattr name.
664 */
665 if (!(entry->flags & XFS_ATTR_LOCAL))
666 continue;
667
668 name_loc = xfs_attr3_leaf_name_local(leaf, i);
669 pptr_emit(ip, entry->flags, name_loc->nameval,
670 name_loc->namelen,
671 name_loc->nameval + name_loc->namelen,
672 be16_to_cpu(name_loc->valuelen));
673 }
674}
675
676static int
677list_leaf_pptrs(
678 struct xfs_inode *ip)
679{
680 struct xfs_buf *leaf_bp;
681 int error;
682
683 error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino, 0, &leaf_bp);
684 if (error)
685 return error;
686
687 list_leaf_pptr_entries(ip, leaf_bp);
688 libxfs_trans_brelse(NULL, leaf_bp);
689 return 0;
690}
691
692static int
693find_leftmost_attr_leaf(
694 struct xfs_inode *ip,
695 struct xfs_buf **leaf_bpp)
696{
697 struct xfs_da3_icnode_hdr nodehdr;
698 struct xfs_mount *mp = ip->i_mount;
699 struct xfs_da_intnode *node;
700 struct xfs_da_node_entry *btree;
701 struct xfs_buf *bp;
702 xfs_dablk_t blkno = 0;
703 unsigned int expected_level = 0;
704 int error;
705
706 for (;;) {
707 uint16_t magic;
708
709 error = -libxfs_da3_node_read(NULL, ip, blkno, &bp,
710 XFS_ATTR_FORK);
711 if (error)
712 return error;
713
714 node = bp->b_addr;
715 magic = be16_to_cpu(node->hdr.info.magic);
716 if (magic == XFS_ATTR_LEAF_MAGIC ||
717 magic == XFS_ATTR3_LEAF_MAGIC)
718 break;
719
720 error = EFSCORRUPTED;
721 if (magic != XFS_DA_NODE_MAGIC &&
722 magic != XFS_DA3_NODE_MAGIC)
723 goto out_buf;
724
725 libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node);
726
727 if (nodehdr.count == 0 || nodehdr.level >= XFS_DA_NODE_MAXDEPTH)
728 goto out_buf;
729
730 /* Check the level from the root node. */
731 if (blkno == 0)
732 expected_level = nodehdr.level - 1;
733 else if (expected_level != nodehdr.level)
734 goto out_buf;
735 else
736 expected_level--;
737
738 /* Find the next level towards the leaves of the dabtree. */
739 btree = nodehdr.btree;
740 blkno = be32_to_cpu(btree->before);
741 libxfs_trans_brelse(NULL, bp);
742 }
743
744 error = EFSCORRUPTED;
745 if (expected_level != 0)
746 goto out_buf;
747
748 *leaf_bpp = bp;
749 return 0;
750
751out_buf:
752 libxfs_trans_brelse(NULL, bp);
753 return error;
754}
755
756static int
757list_node_pptrs(
758 struct xfs_inode *ip)
759{
760 struct xfs_attr3_icleaf_hdr leafhdr;
761 struct xfs_mount *mp = ip->i_mount;
762 struct xfs_attr_leafblock *leaf;
763 struct xfs_buf *leaf_bp;
764 int error;
765
766 error = find_leftmost_attr_leaf(ip, &leaf_bp);
767 if (error)
768 return error;
769
770 for (;;) {
771 list_leaf_pptr_entries(ip, leaf_bp);
772
773 /* Find the right sibling of this leaf block. */
774 leaf = leaf_bp->b_addr;
775 libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
776 if (leafhdr.forw == 0)
777 goto out_leaf;
778
779 libxfs_trans_brelse(NULL, leaf_bp);
780
781 error = -libxfs_attr3_leaf_read(NULL, ip, ip->i_ino,
782 leafhdr.forw, &leaf_bp);
783 if (error)
784 return error;
785 }
786
787out_leaf:
788 libxfs_trans_brelse(NULL, leaf_bp);
789 return error;
790}
791
792static int
793list_pptrs(
794 struct xfs_inode *ip)
795{
796 int error;
797
798 if (!libxfs_inode_hasattr(ip))
799 return 0;
800
801 if (ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)
802 return list_sf_pptrs(ip);
803
804 /* attr functions require that the attr fork is loaded */
805 error = -libxfs_iread_extents(NULL, ip, XFS_ATTR_FORK);
806 if (error)
807 return error;
808
809 if (libxfs_attr_is_leaf(ip))
810 return list_leaf_pptrs(ip);
811
812 return list_node_pptrs(ip);
813}
814
815/* If the io cursor points to a file, list its parents. */
816static int
817parent_cur(
818 char *tag)
819{
820 struct xfs_inode *ip;
821 int error = 0;
822
823 if (!xfs_has_parent(mp))
824 return 0;
825
826 if (iocur_top->typ != &typtab[TYP_INODE])
827 return ENOTDIR;
828
829 error = -libxfs_iget(mp, NULL, iocur_top->ino, 0, &ip);
830 if (error)
831 return error;
832
833 /* List the parents of a file. */
834 if (tag)
835 dbprintf(_("%s:\n"), tag);
836
837 error = list_pptrs(ip);
838 if (error)
839 goto rele;
840
841rele:
842 libxfs_irele(ip);
843 return error;
844}
845
846static void
847parent_help(void)
848{
849 dbprintf(_(
850"\n"
851" List the parents of the currently selected file.\n"
852"\n"
853" Parent pointers will be listed in the format:\n"
854" inode_number:inode_gen ondisk_namehash:namehash name_length name\n"
855 ));
856}
857
858static int
859parent_f(
860 int argc,
861 char **argv)
862{
863 int c;
864 int error = 0;
865
866 while ((c = getopt(argc, argv, "")) != -1) {
867 switch (c) {
868 default:
869 ls_help();
870 return 0;
871 }
872 }
873
874 if (optind == argc) {
875 error = parent_cur(NULL);
876 if (error) {
877 dbprintf("%s\n", strerror(error));
878 exitcode = 1;
879 }
880
881 return 0;
882 }
883
884 for (c = optind; c < argc; c++) {
885 push_cur();
886
887 error = path_walk(argv[c]);
888 if (error)
889 goto err_cur;
890
891 error = parent_cur(argv[c]);
892 if (error)
893 goto err_cur;
894
895 pop_cur();
896 }
897
898 return 0;
899err_cur:
900 pop_cur();
901 if (error) {
902 dbprintf("%s: %s\n", argv[c], strerror(error));
903 exitcode = 1;
904 }
905 return 0;
906}
907
908static struct cmdinfo parent_cmd = {
909 .name = "parent",
910 .altname = "pptr",
911 .cfunc = parent_f,
912 .argmin = 0,
913 .argmax = -1,
914 .canpush = 0,
915 .args = "[paths...]",
916 .help = parent_help,
917};
918
7b3f2025
DW
919static void
920link_help(void)
921{
922 dbprintf(_(
923"\n"
924" Create a directory entry in the current directory that points to the\n"
925" specified file.\n"
926"\n"
927" Options:\n"
928" -i -- Point to this specific inode number.\n"
929" -p -- Point to the inode given by this path.\n"
930" -t -- Set the file type to this value.\n"
931" name -- Create this directory entry with this name.\n"
932 ));
933}
934
935static int
936create_child(
937 struct xfs_mount *mp,
938 xfs_ino_t parent_ino,
939 const char *name,
940 unsigned int ftype,
941 xfs_ino_t child_ino)
942{
943 struct xfs_name xname = {
944 .name = (const unsigned char *)name,
945 .len = strlen(name),
946 .type = ftype,
947 };
948 struct xfs_parent_args *ppargs = NULL;
949 struct xfs_trans *tp;
950 struct xfs_inode *dp, *ip;
951 unsigned int resblks;
952 bool isdir;
953 int error;
954
955 error = -libxfs_iget(mp, NULL, parent_ino, 0, &dp);
956 if (error)
957 return error;
958
959 if (!S_ISDIR(VFS_I(dp)->i_mode)) {
960 error = -ENOTDIR;
961 goto out_dp;
962 }
963
964 error = -libxfs_iget(mp, NULL, child_ino, 0, &ip);
965 if (error)
966 goto out_dp;
967 isdir = S_ISDIR(VFS_I(ip)->i_mode);
968
969 if (xname.type == XFS_DIR3_FT_UNKNOWN)
970 xname.type = libxfs_mode_to_ftype(VFS_I(ip)->i_mode);
971
972 error = -libxfs_parent_start(mp, &ppargs);
973 if (error)
974 goto out_ip;
975
976 resblks = libxfs_link_space_res(mp, MAXNAMELEN);
977 error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_link, resblks, 0, 0,
978 &tp);
979 if (error)
980 goto out_parent;
981
982 libxfs_trans_ijoin(tp, dp, 0);
983 libxfs_trans_ijoin(tp, ip, 0);
984
985 error = -libxfs_dir_createname(tp, dp, &xname, ip->i_ino, resblks);
986 if (error)
987 goto out_trans;
988
989 /* bump dp's link to ip */
990 libxfs_bumplink(tp, ip);
991
992 /* bump ip's dotdot link to dp */
993 if (isdir)
994 libxfs_bumplink(tp, dp);
995
996 /* Replace the dotdot entry in the child directory. */
997 if (isdir) {
998 error = -libxfs_dir_replace(tp, ip, &xfs_name_dotdot,
999 dp->i_ino, resblks);
1000 if (error)
1001 goto out_trans;
1002 }
1003
1004 if (ppargs) {
1005 error = -libxfs_parent_addname(tp, ppargs, dp, &xname, ip);
1006 if (error)
1007 goto out_trans;
1008 }
1009
1010 error = -libxfs_trans_commit(tp);
1011 goto out_parent;
1012
1013out_trans:
1014 libxfs_trans_cancel(tp);
1015out_parent:
1016 libxfs_parent_finish(mp, ppargs);
1017out_ip:
1018 libxfs_irele(ip);
1019out_dp:
1020 libxfs_irele(dp);
1021 return error;
1022}
1023
1024static const char *ftype_map[] = {
1025 [XFS_DIR3_FT_REG_FILE] = "reg",
1026 [XFS_DIR3_FT_DIR] = "dir",
1027 [XFS_DIR3_FT_CHRDEV] = "cdev",
1028 [XFS_DIR3_FT_BLKDEV] = "bdev",
1029 [XFS_DIR3_FT_FIFO] = "fifo",
1030 [XFS_DIR3_FT_SOCK] = "sock",
1031 [XFS_DIR3_FT_SYMLINK] = "symlink",
1032 [XFS_DIR3_FT_WHT] = "whiteout",
1033};
1034
1035static int
1036link_f(
1037 int argc,
1038 char **argv)
1039{
1040 xfs_ino_t child_ino = NULLFSINO;
1041 int ftype = XFS_DIR3_FT_UNKNOWN;
1042 unsigned int i;
1043 int c;
1044 int error = 0;
1045
1046 while ((c = getopt(argc, argv, "i:p:t:")) != -1) {
1047 switch (c) {
1048 case 'i':
1049 errno = 0;
1050 child_ino = strtoull(optarg, NULL, 0);
1051 if (errno == ERANGE) {
1052 printf("%s: unknown inode number\n", optarg);
1053 exitcode = 1;
1054 return 0;
1055 }
1056 break;
1057 case 'p':
1058 push_cur();
1059 error = path_walk(optarg);
1060 if (error) {
1061 printf("%s: %s\n", optarg, strerror(error));
1062 exitcode = 1;
1063 return 0;
1064 } else if (iocur_top->typ != &typtab[TYP_INODE]) {
1065 printf("%s: does not point to an inode\n",
1066 optarg);
1067 exitcode = 1;
1068 return 0;
1069 } else {
1070 child_ino = iocur_top->ino;
1071 }
1072 pop_cur();
1073 break;
1074 case 't':
1075 for (i = 0; i < ARRAY_SIZE(ftype_map); i++) {
1076 if (ftype_map[i] &&
1077 !strcmp(ftype_map[i], optarg)) {
1078 ftype = i;
1079 break;
1080 }
1081 }
1082 if (i == ARRAY_SIZE(ftype_map)) {
1083 printf("%s: unknown file type\n", optarg);
1084 exitcode = 1;
1085 return 0;
1086 }
1087 break;
1088 default:
1089 link_help();
1090 return 0;
1091 }
1092 }
1093
1094 if (child_ino == NULLFSINO) {
1095 printf("link: need to specify child via -i or -p\n");
1096 exitcode = 1;
1097 return 0;
1098 }
1099
1100 if (iocur_top->typ != &typtab[TYP_INODE]) {
1101 printf("io cursor does not point to an inode.\n");
1102 exitcode = 1;
1103 return 0;
1104 }
1105
1106 if (optind + 1 != argc) {
1107 printf("link: need directory entry name");
1108 exitcode = 1;
1109 return 0;
1110 }
1111
1112 error = create_child(mp, iocur_top->ino, argv[optind], ftype,
1113 child_ino);
1114 if (error) {
1115 printf("link failed: %s\n", strerror(error));
1116 exitcode = 1;
1117 return 0;
1118 }
1119
1120 return 0;
1121}
1122
1123static struct cmdinfo link_cmd = {
1124 .name = "link",
1125 .cfunc = link_f,
1126 .argmin = 0,
1127 .argmax = -1,
1128 .canpush = 0,
1129 .args = "[-i ino] [-p path] [-t ftype] name",
1130 .help = link_help,
1131};
1132
1133static void
1134unlink_help(void)
1135{
1136 dbprintf(_(
1137"\n"
1138" Remove a directory entry from the current directory.\n"
1139"\n"
1140" Options:\n"
1141" name -- Remove the directory entry with this name.\n"
1142 ));
1143}
1144
1145static void
1146droplink(
1147 struct xfs_trans *tp,
1148 struct xfs_inode *ip)
1149{
1150 struct inode *inode = VFS_I(ip);
1151
1152 libxfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
1153
1154 if (inode->i_nlink != XFS_NLINK_PINNED)
1155 drop_nlink(VFS_I(ip));
1156
1157 libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1158}
1159
1160static int
1161remove_child(
1162 struct xfs_mount *mp,
1163 xfs_ino_t parent_ino,
1164 const char *name)
1165{
1166 struct xfs_name xname = {
1167 .name = (const unsigned char *)name,
1168 .len = strlen(name),
1169 };
1170 struct xfs_parent_args *ppargs;
1171 struct xfs_trans *tp;
1172 struct xfs_inode *dp, *ip;
1173 xfs_ino_t child_ino;
1174 unsigned int resblks;
1175 int error;
1176
1177 error = -libxfs_iget(mp, NULL, parent_ino, 0, &dp);
1178 if (error)
1179 return error;
1180
1181 if (!S_ISDIR(VFS_I(dp)->i_mode)) {
1182 error = -ENOTDIR;
1183 goto out_dp;
1184 }
1185
1186 error = -libxfs_dir_lookup(NULL, dp, &xname, &child_ino, NULL);
1187 if (error)
1188 goto out_dp;
1189
1190 error = -libxfs_iget(mp, NULL, child_ino, 0, &ip);
1191 if (error)
1192 goto out_dp;
1193
1194 error = -libxfs_parent_start(mp, &ppargs);
1195 if (error)
1196 goto out_ip;
1197
1198 resblks = libxfs_remove_space_res(mp, MAXNAMELEN);
1199 error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, resblks, 0, 0,
1200 &tp);
1201 if (error)
1202 goto out_parent;
1203
1204 libxfs_trans_ijoin(tp, dp, 0);
1205 libxfs_trans_ijoin(tp, ip, 0);
1206
1207 if (S_ISDIR(VFS_I(ip)->i_mode)) {
1208 /* drop ip's dotdot link to dp */
1209 droplink(tp, dp);
1210 } else {
1211 libxfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
1212 }
1213
1214 /* drop dp's link to ip */
1215 droplink(tp, ip);
1216
1217 error = -libxfs_dir_removename(tp, dp, &xname, ip->i_ino, resblks);
1218 if (error)
1219 goto out_trans;
1220
1221 if (ppargs) {
1222 error = -libxfs_parent_removename(tp, ppargs, dp, &xname, ip);
1223 if (error)
1224 goto out_trans;
1225 }
1226
1227 error = -libxfs_trans_commit(tp);
1228 goto out_parent;
1229
1230out_trans:
1231 libxfs_trans_cancel(tp);
1232out_parent:
1233 libxfs_parent_finish(mp, ppargs);
1234out_ip:
1235 libxfs_irele(ip);
1236out_dp:
1237 libxfs_irele(dp);
1238 return error;
1239}
1240
1241static int
1242unlink_f(
1243 int argc,
1244 char **argv)
1245{
1246 int c;
1247 int error = 0;
1248
1249 while ((c = getopt(argc, argv, "")) != -1) {
1250 switch (c) {
1251 default:
1252 unlink_help();
1253 return 0;
1254 }
1255 }
1256
1257 if (iocur_top->typ != &typtab[TYP_INODE]) {
1258 printf("io cursor does not point to an inode.\n");
1259 exitcode = 1;
1260 return 0;
1261 }
1262
1263 if (optind + 1 != argc) {
1264 printf("unlink: need directory entry name");
1265 exitcode = 1;
1266 return 0;
1267 }
1268
1269 error = remove_child(mp, iocur_top->ino, argv[optind]);
1270 if (error) {
1271 printf("unlink failed: %s\n", strerror(error));
1272 exitcode = 1;
1273 return 0;
1274 }
1275
1276 return 0;
1277}
1278
1279static struct cmdinfo unlink_cmd = {
1280 .name = "unlink",
1281 .cfunc = unlink_f,
1282 .argmin = 0,
1283 .argmax = -1,
1284 .canpush = 0,
1285 .args = "name",
1286 .help = unlink_help,
1287};
1288
d29084ce
DW
1289void
1290namei_init(void)
1291{
1292 path_cmd.oneline = _("navigate to an inode by path");
1293 add_command(&path_cmd);
08f24589
DW
1294
1295 ls_cmd.oneline = _("list directory contents");
1296 add_command(&ls_cmd);
34d3b107
DW
1297
1298 parent_cmd.oneline = _("list parent pointers");
1299 add_command(&parent_cmd);
7b3f2025
DW
1300
1301 if (expert_mode) {
1302 link_cmd.oneline = _("create directory link");
1303 add_command(&link_cmd);
1304
1305 unlink_cmd.oneline = _("remove directory link");
1306 add_command(&unlink_cmd);
1307 }
d29084ce 1308}