| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| */ |
| |
| #include "libxfs.h" |
| #include "libxlog.h" |
| #include <signal.h> |
| #include "command.h" |
| #include "init.h" |
| #include "input.h" |
| #include "io.h" |
| #include "init.h" |
| #include "sig.h" |
| #include "output.h" |
| #include "malloc.h" |
| #include "type.h" |
| |
| static char **cmdline; |
| static int ncmdline; |
| char *fsdevice; |
| int blkbb; |
| int exitcode; |
| int expert_mode; |
| static int force; |
| static struct xfs_mount xmount; |
| struct xfs_mount *mp; |
| static struct xlog xlog; |
| xfs_agnumber_t cur_agno = NULLAGNUMBER; |
| |
| static void |
| usage(void) |
| { |
| fprintf(stderr, _( |
| "Usage: %s [-ifFrxV] [-p prog] [-l logdev] [-c cmd]... device\n" |
| ), progname); |
| exit(1); |
| } |
| |
| static void |
| init( |
| int argc, |
| char **argv) |
| { |
| struct xfs_sb *sbp; |
| struct xfs_buf *bp; |
| unsigned int agcount; |
| int c; |
| int error; |
| |
| setlocale(LC_ALL, ""); |
| bindtextdomain(PACKAGE, LOCALEDIR); |
| textdomain(PACKAGE); |
| |
| progname = basename(argv[0]); |
| while ((c = getopt(argc, argv, "c:fFip:rxVl:")) != EOF) { |
| switch (c) { |
| case 'c': |
| cmdline = xrealloc(cmdline, (ncmdline+1)*sizeof(char*)); |
| cmdline[ncmdline++] = optarg; |
| break; |
| case 'f': |
| x.disfile = 1; |
| break; |
| case 'F': |
| force = 1; |
| break; |
| case 'i': |
| x.isreadonly = (LIBXFS_ISREADONLY|LIBXFS_ISINACTIVE); |
| break; |
| case 'p': |
| progname = optarg; |
| break; |
| case 'r': |
| x.isreadonly = LIBXFS_ISREADONLY; |
| break; |
| case 'l': |
| x.logname = optarg; |
| break; |
| case 'x': |
| expert_mode = 1; |
| break; |
| case 'V': |
| printf(_("%s version %s\n"), progname, VERSION); |
| exit(0); |
| default: |
| usage(); |
| } |
| } |
| if (optind + 1 != argc) |
| usage(); |
| |
| fsdevice = argv[optind]; |
| if (!x.disfile) |
| x.volname = fsdevice; |
| else |
| x.dname = fsdevice; |
| |
| x.bcache_flags = CACHE_MISCOMPARE_PURGE; |
| if (!libxfs_init(&x)) { |
| fputs(_("\nfatal error -- couldn't initialize XFS library\n"), |
| stderr); |
| exit(1); |
| } |
| |
| /* |
| * Read the superblock, but don't validate it - we are a diagnostic |
| * tool and so need to be able to mount busted filesystems. |
| */ |
| memset(&xmount, 0, sizeof(struct xfs_mount)); |
| libxfs_buftarg_init(&xmount, x.ddev, x.logdev, x.rtdev); |
| error = -libxfs_buf_read_uncached(xmount.m_ddev_targp, XFS_SB_DADDR, |
| 1 << (XFS_MAX_SECTORSIZE_LOG - BBSHIFT), 0, &bp, NULL); |
| if (error) { |
| fprintf(stderr, _("%s: %s is invalid (cannot read first 512 " |
| "bytes)\n"), progname, fsdevice); |
| exit(1); |
| } |
| |
| /* copy SB from buffer to in-core, converting architecture as we go */ |
| libxfs_sb_from_disk(&xmount.m_sb, XFS_BUF_TO_SBP(bp)); |
| libxfs_buf_relse(bp); |
| |
| sbp = &xmount.m_sb; |
| if (sbp->sb_magicnum != XFS_SB_MAGIC) { |
| fprintf(stderr, _("%s: %s is not a valid XFS filesystem (unexpected SB magic number 0x%08x)\n"), |
| progname, fsdevice, sbp->sb_magicnum); |
| if (!force) { |
| fprintf(stderr, _("Use -F to force a read attempt.\n")); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| agcount = sbp->sb_agcount; |
| mp = libxfs_mount(&xmount, sbp, x.ddev, x.logdev, x.rtdev, |
| LIBXFS_MOUNT_DEBUGGER); |
| if (!mp) { |
| fprintf(stderr, |
| _("%s: device %s unusable (not an XFS filesystem?)\n"), |
| progname, fsdevice); |
| exit(1); |
| } |
| mp->m_log = &xlog; |
| blkbb = 1 << mp->m_blkbb_log; |
| |
| /* Did we limit a broken agcount in libxfs_mount? */ |
| if (sbp->sb_agcount != agcount) |
| exitcode = 1; |
| |
| /* |
| * xfs_check needs corrected incore superblock values |
| */ |
| if (sbp->sb_rootino != NULLFSINO && |
| xfs_sb_version_haslazysbcount(&mp->m_sb)) { |
| int error = -libxfs_initialize_perag_data(mp, sbp->sb_agcount); |
| if (error) { |
| fprintf(stderr, |
| _("%s: cannot init perag data (%d). Continuing anyway.\n"), |
| progname, error); |
| } |
| } |
| |
| if (xfs_sb_version_hassparseinodes(&mp->m_sb)) |
| type_set_tab_spcrc(); |
| else if (xfs_sb_version_hascrc(&mp->m_sb)) |
| type_set_tab_crc(); |
| |
| push_cur(); |
| init_commands(); |
| init_sig(); |
| } |
| |
| int |
| main( |
| int argc, |
| char **argv) |
| { |
| int c, i, done = 0; |
| char *input; |
| char **v; |
| int start_iocur_sp; |
| |
| init(argc, argv); |
| start_iocur_sp = iocur_sp; |
| |
| for (i = 0; !done && i < ncmdline; i++) { |
| v = breakline(cmdline[i], &c); |
| if (c) |
| done = command(c, v); |
| xfree(v); |
| } |
| if (cmdline) { |
| xfree(cmdline); |
| goto close_devices; |
| } |
| |
| pushfile(stdin); |
| while (!done) { |
| if ((input = fetchline()) == NULL) |
| break; |
| v = breakline(input, &c); |
| if (c) |
| done = command(c, v); |
| doneline(input, v); |
| } |
| |
| close_devices: |
| /* |
| * Make sure that we pop the all the buffer contexts we hold so that |
| * they are released before we purge the caches during unmount. |
| */ |
| while (iocur_sp > start_iocur_sp) |
| pop_cur(); |
| libxfs_umount(mp); |
| libxfs_destroy(&x); |
| |
| return exitcode; |
| } |