| /* |
| * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "libxfs.h" |
| #include "libxlog.h" |
| #include "command.h" |
| #include "type.h" |
| #include "faddr.h" |
| #include "fprint.h" |
| #include "field.h" |
| #include "io.h" |
| #include "sb.h" |
| #include "bit.h" |
| #include "output.h" |
| #include "init.h" |
| |
| #define uuid_equal(s,d) (platform_uuid_compare((s),(d)) == 0) |
| |
| static int sb_f(int argc, char **argv); |
| static void sb_help(void); |
| static int uuid_f(int argc, char **argv); |
| static void uuid_help(void); |
| static int label_f(int argc, char **argv); |
| static void label_help(void); |
| static int version_f(int argc, char **argv); |
| static void version_help(void); |
| |
| static const cmdinfo_t sb_cmd = |
| { "sb", NULL, sb_f, 0, 1, 1, N_("[agno]"), |
| N_("set current address to sb header"), sb_help }; |
| static const cmdinfo_t uuid_cmd = |
| { "uuid", NULL, uuid_f, 0, 1, 1, N_("[uuid]"), |
| N_("write/print FS uuid"), uuid_help }; |
| static const cmdinfo_t label_cmd = |
| { "label", NULL, label_f, 0, 1, 1, N_("[label]"), |
| N_("write/print FS label"), label_help }; |
| static const cmdinfo_t version_cmd = |
| { "version", NULL, version_f, 0, -1, 1, N_("[feature | [vnum fnum]]"), |
| N_("set feature bit(s) in the sb version field"), version_help }; |
| |
| void |
| sb_init(void) |
| { |
| add_command(&sb_cmd); |
| add_command(&uuid_cmd); |
| add_command(&label_cmd); |
| add_command(&version_cmd); |
| } |
| |
| #define OFF(f) bitize(offsetof(xfs_sb_t, sb_ ## f)) |
| #define SZC(f) szcount(xfs_sb_t, sb_ ## f) |
| const field_t sb_flds[] = { |
| { "magicnum", FLDT_UINT32X, OI(OFF(magicnum)), C1, 0, TYP_NONE }, |
| { "blocksize", FLDT_UINT32D, OI(OFF(blocksize)), C1, 0, TYP_NONE }, |
| { "dblocks", FLDT_DRFSBNO, OI(OFF(dblocks)), C1, 0, TYP_NONE }, |
| { "rblocks", FLDT_DRFSBNO, OI(OFF(rblocks)), C1, 0, TYP_NONE }, |
| { "rextents", FLDT_DRTBNO, OI(OFF(rextents)), C1, 0, TYP_NONE }, |
| { "uuid", FLDT_UUID, OI(OFF(uuid)), C1, 0, TYP_NONE }, |
| { "logstart", FLDT_DFSBNO, OI(OFF(logstart)), C1, 0, TYP_LOG }, |
| { "rootino", FLDT_INO, OI(OFF(rootino)), C1, 0, TYP_INODE }, |
| { "rbmino", FLDT_INO, OI(OFF(rbmino)), C1, 0, TYP_INODE }, |
| { "rsumino", FLDT_INO, OI(OFF(rsumino)), C1, 0, TYP_INODE }, |
| { "rextsize", FLDT_AGBLOCK, OI(OFF(rextsize)), C1, 0, TYP_NONE }, |
| { "agblocks", FLDT_AGBLOCK, OI(OFF(agblocks)), C1, 0, TYP_NONE }, |
| { "agcount", FLDT_AGNUMBER, OI(OFF(agcount)), C1, 0, TYP_NONE }, |
| { "rbmblocks", FLDT_EXTLEN, OI(OFF(rbmblocks)), C1, 0, TYP_NONE }, |
| { "logblocks", FLDT_EXTLEN, OI(OFF(logblocks)), C1, 0, TYP_NONE }, |
| { "versionnum", FLDT_UINT16X, OI(OFF(versionnum)), C1, 0, TYP_NONE }, |
| { "sectsize", FLDT_UINT16D, OI(OFF(sectsize)), C1, 0, TYP_NONE }, |
| { "inodesize", FLDT_UINT16D, OI(OFF(inodesize)), C1, 0, TYP_NONE }, |
| { "inopblock", FLDT_UINT16D, OI(OFF(inopblock)), C1, 0, TYP_NONE }, |
| { "fname", FLDT_CHARNS, OI(OFF(fname)), CI(SZC(fname)), 0, TYP_NONE }, |
| { "blocklog", FLDT_UINT8D, OI(OFF(blocklog)), C1, 0, TYP_NONE }, |
| { "sectlog", FLDT_UINT8D, OI(OFF(sectlog)), C1, 0, TYP_NONE }, |
| { "inodelog", FLDT_UINT8D, OI(OFF(inodelog)), C1, 0, TYP_NONE }, |
| { "inopblog", FLDT_UINT8D, OI(OFF(inopblog)), C1, 0, TYP_NONE }, |
| { "agblklog", FLDT_UINT8D, OI(OFF(agblklog)), C1, 0, TYP_NONE }, |
| { "rextslog", FLDT_UINT8D, OI(OFF(rextslog)), C1, 0, TYP_NONE }, |
| { "inprogress", FLDT_UINT8D, OI(OFF(inprogress)), C1, 0, TYP_NONE }, |
| { "imax_pct", FLDT_UINT8D, OI(OFF(imax_pct)), C1, 0, TYP_NONE }, |
| { "icount", FLDT_UINT64D, OI(OFF(icount)), C1, 0, TYP_NONE }, |
| { "ifree", FLDT_UINT64D, OI(OFF(ifree)), C1, 0, TYP_NONE }, |
| { "fdblocks", FLDT_UINT64D, OI(OFF(fdblocks)), C1, 0, TYP_NONE }, |
| { "frextents", FLDT_UINT64D, OI(OFF(frextents)), C1, 0, TYP_NONE }, |
| { "uquotino", FLDT_INO, OI(OFF(uquotino)), C1, 0, TYP_INODE }, |
| { "gquotino", FLDT_INO, OI(OFF(gquotino)), C1, 0, TYP_INODE }, |
| { "qflags", FLDT_UINT16X, OI(OFF(qflags)), C1, 0, TYP_NONE }, |
| { "flags", FLDT_UINT8X, OI(OFF(flags)), C1, 0, TYP_NONE }, |
| { "shared_vn", FLDT_UINT8D, OI(OFF(shared_vn)), C1, 0, TYP_NONE }, |
| { "inoalignmt", FLDT_EXTLEN, OI(OFF(inoalignmt)), C1, 0, TYP_NONE }, |
| { "unit", FLDT_UINT32D, OI(OFF(unit)), C1, 0, TYP_NONE }, |
| { "width", FLDT_UINT32D, OI(OFF(width)), C1, 0, TYP_NONE }, |
| { "dirblklog", FLDT_UINT8D, OI(OFF(dirblklog)), C1, 0, TYP_NONE }, |
| { "logsectlog", FLDT_UINT8D, OI(OFF(logsectlog)), C1, 0, TYP_NONE }, |
| { "logsectsize", FLDT_UINT16D, OI(OFF(logsectsize)), C1, 0, TYP_NONE }, |
| { "logsunit", FLDT_UINT32D, OI(OFF(logsunit)), C1, 0, TYP_NONE }, |
| { "features2", FLDT_UINT32X, OI(OFF(features2)), C1, 0, TYP_NONE }, |
| { "bad_features2", FLDT_UINT32X, OI(OFF(bad_features2)), |
| C1, 0, TYP_NONE }, |
| { "features_compat", FLDT_UINT32X, OI(OFF(features_compat)), |
| C1, 0, TYP_NONE }, |
| { "features_ro_compat", FLDT_UINT32X, OI(OFF(features_ro_compat)), |
| C1, 0, TYP_NONE }, |
| { "features_incompat", FLDT_UINT32X, OI(OFF(features_incompat)), |
| C1, 0, TYP_NONE }, |
| { "features_log_incompat", FLDT_UINT32X, OI(OFF(features_log_incompat)), |
| C1, 0, TYP_NONE }, |
| { "crc", FLDT_CRC, OI(OFF(crc)), C1, 0, TYP_NONE }, |
| { "spino_align", FLDT_EXTLEN, OI(OFF(spino_align)), C1, 0, TYP_NONE }, |
| { "pquotino", FLDT_INO, OI(OFF(pquotino)), C1, 0, TYP_INODE }, |
| { "lsn", FLDT_UINT64X, OI(OFF(lsn)), C1, 0, TYP_NONE }, |
| { "meta_uuid", FLDT_UUID, OI(OFF(meta_uuid)), C1, 0, TYP_NONE }, |
| { NULL } |
| }; |
| |
| const field_t sb_hfld[] = { |
| { "", FLDT_SB, OI(0), C1, 0, TYP_NONE }, |
| { NULL } |
| }; |
| |
| static void |
| sb_help(void) |
| { |
| dbprintf(_( |
| "\n" |
| " set allocation group superblock\n" |
| "\n" |
| " Example:\n" |
| "\n" |
| " 'sb 7' - set location to 7th allocation group superblock, set type to 'sb'\n" |
| "\n" |
| " Located in the first sector of each allocation group, the superblock\n" |
| " contains the base information for the filesystem.\n" |
| " The superblock in allocation group 0 is the primary. The copies in the\n" |
| " remaining allocation groups only serve as backup for filesystem recovery.\n" |
| " The icount/ifree/fdblocks/frextents are only updated in superblock 0.\n" |
| "\n" |
| )); |
| } |
| |
| static int |
| sb_f( |
| int argc, |
| char **argv) |
| { |
| xfs_agnumber_t agno; |
| char *p; |
| |
| if (argc > 1) { |
| agno = (xfs_agnumber_t)strtoul(argv[1], &p, 0); |
| if (*p != '\0' || agno >= mp->m_sb.sb_agcount) { |
| dbprintf(_("bad allocation group number %s\n"), argv[1]); |
| return 0; |
| } |
| cur_agno = agno; |
| } else if (cur_agno == NULLAGNUMBER) |
| cur_agno = 0; |
| ASSERT(typtab[TYP_SB].typnm == TYP_SB); |
| set_cur(&typtab[TYP_SB], |
| XFS_AG_DADDR(mp, cur_agno, XFS_SB_DADDR), |
| XFS_FSS_TO_BB(mp, 1), DB_RING_ADD, NULL); |
| return 0; |
| } |
| |
| /*ARGSUSED*/ |
| int |
| sb_size( |
| void *obj, |
| int startoff, |
| int idx) |
| { |
| return bitize(mp->m_sb.sb_sectsize); |
| } |
| |
| static int |
| get_sb(xfs_agnumber_t agno, xfs_sb_t *sb) |
| { |
| push_cur(); |
| set_cur(&typtab[TYP_SB], |
| XFS_AG_DADDR(mp, agno, XFS_SB_DADDR), |
| XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL); |
| |
| if (!iocur_top->data) { |
| dbprintf(_("can't read superblock for AG %u\n"), agno); |
| pop_cur(); |
| return 0; |
| } |
| |
| libxfs_sb_from_disk(sb, iocur_top->data); |
| |
| if (sb->sb_magicnum != XFS_SB_MAGIC) { |
| dbprintf(_("bad sb magic # %#x in AG %u\n"), |
| sb->sb_magicnum, agno); |
| return 0; |
| } |
| if (!xfs_sb_good_version(sb)) { |
| dbprintf(_("bad sb version # %#x in AG %u\n"), |
| sb->sb_versionnum, agno); |
| return 0; |
| } |
| if (agno == 0 && sb->sb_inprogress != 0) { |
| dbprintf(_("mkfs not completed successfully\n")); |
| return 0; |
| } |
| return 1; |
| } |
| |
| /* workaround craziness in the xlog routines */ |
| int xlog_recover_do_trans(struct xlog *log, xlog_recover_t *t, int p) |
| { |
| return 0; |
| } |
| |
| int |
| sb_logcheck(void) |
| { |
| int dirty; |
| |
| if (mp->m_sb.sb_logstart) { |
| if (x.logdev && x.logdev != x.ddev) { |
| dbprintf(_("aborting - external log specified for FS " |
| "with an internal log\n")); |
| return 0; |
| } |
| } else { |
| if (!x.logdev || (x.logdev == x.ddev)) { |
| dbprintf(_("aborting - no external log specified for FS " |
| "with an external log\n")); |
| return 0; |
| } |
| } |
| |
| libxfs_buftarg_init(mp, x.ddev, x.logdev, x.rtdev); |
| |
| dirty = xlog_is_dirty(mp, mp->m_log, &x, 0); |
| if (dirty == -1) { |
| dbprintf(_("ERROR: cannot find log head/tail, run xfs_repair\n")); |
| return 0; |
| } else if (dirty == 1) { |
| dbprintf(_( |
| "ERROR: The filesystem has valuable metadata changes in a log which needs to\n" |
| "be replayed. Mount the filesystem to replay the log, and unmount it before\n" |
| "re-running %s. If you are unable to mount the filesystem, then use\n" |
| "the xfs_repair -L option to destroy the log and attempt a repair.\n" |
| "Note that destroying the log may cause corruption -- please attempt a mount\n" |
| "of the filesystem before doing this.\n"), progname); |
| return 0; |
| } |
| /* Log is clean */ |
| return 1; |
| } |
| |
| static int |
| sb_logzero(uuid_t *uuidp) |
| { |
| int cycle = XLOG_INIT_CYCLE; |
| int error; |
| |
| if (!sb_logcheck()) |
| return 0; |
| |
| /* |
| * The log must always move forward on v5 superblocks. Bump it to the |
| * next cycle. |
| */ |
| if (xfs_sb_version_hascrc(&mp->m_sb)) |
| cycle = mp->m_log->l_curr_cycle + 1; |
| |
| dbprintf(_("Clearing log and setting UUID\n")); |
| |
| error = libxfs_log_clear(mp->m_logdev_targp, NULL, |
| XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart), |
| (xfs_extlen_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks), |
| uuidp, |
| xfs_sb_version_haslogv2(&mp->m_sb) ? 2 : 1, |
| mp->m_sb.sb_logsunit, XLOG_FMT, cycle, true); |
| if (error) { |
| dbprintf(_("ERROR: cannot clear the log\n")); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| |
| static void |
| uuid_help(void) |
| { |
| dbprintf(_( |
| "\n" |
| " write/print FS uuid\n" |
| "\n" |
| " Example:\n" |
| "\n" |
| " 'uuid' - print UUID\n" |
| " 'uuid 01234567-0123-0123-0123-0123456789ab' - write UUID\n" |
| " 'uuid generate' - generate and write\n" |
| " 'uuid rewrite' - copy UUID from SB 0\n" |
| "\n" |
| "The print function checks the UUID in each SB and will warn if the UUIDs\n" |
| "differ between AGs (the log is not checked). The write commands will\n" |
| "set the uuid in all AGs to either a specified value, a newly generated\n" |
| "value or the value found in the first superblock (SB 0) respectively.\n" |
| "As a side effect of writing the UUID, the log is cleared (which is fine\n" |
| "on a CLEANLY unmounted FS).\n" |
| "\n" |
| )); |
| } |
| |
| static uuid_t * |
| do_uuid(xfs_agnumber_t agno, uuid_t *uuid) |
| { |
| xfs_sb_t tsb; |
| static uuid_t uu; |
| |
| if (!get_sb(agno, &tsb)) |
| return NULL; |
| |
| if (!uuid) { /* get uuid */ |
| memcpy(&uu, &tsb.sb_uuid, sizeof(uuid_t)); |
| pop_cur(); |
| return &uu; |
| } |
| /* set uuid */ |
| if (!xfs_sb_version_hascrc(&tsb)) |
| goto write; |
| /* |
| * If we have CRCs, and this UUID differs from that stamped in the |
| * metadata, set the incompat flag and copy the old one to the |
| * metadata-specific location. |
| * |
| * If we are setting the user-visible UUID back to match the metadata |
| * UUID, clear the metadata-specific location and the incompat flag. |
| */ |
| if (!xfs_sb_version_hasmetauuid(&tsb) && |
| !uuid_equal(uuid, &mp->m_sb.sb_meta_uuid)) { |
| mp->m_sb.sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_META_UUID; |
| tsb.sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_META_UUID; |
| memcpy(&tsb.sb_meta_uuid, &tsb.sb_uuid, sizeof(uuid_t)); |
| } else if (xfs_sb_version_hasmetauuid(&tsb) && |
| uuid_equal(uuid, &mp->m_sb.sb_meta_uuid)) { |
| memset(&tsb.sb_meta_uuid, 0, sizeof(uuid_t)); |
| /* Write those zeros now; it's ignored once we clear the flag */ |
| libxfs_sb_to_disk(iocur_top->data, &tsb); |
| mp->m_sb.sb_features_incompat &= |
| ~XFS_SB_FEAT_INCOMPAT_META_UUID; |
| tsb.sb_features_incompat &= ~XFS_SB_FEAT_INCOMPAT_META_UUID; |
| } |
| |
| write: |
| memcpy(&tsb.sb_uuid, uuid, sizeof(uuid_t)); |
| libxfs_sb_to_disk(iocur_top->data, &tsb); |
| write_cur(); |
| return uuid; |
| } |
| |
| static int |
| uuid_f( |
| int argc, |
| char **argv) |
| { |
| char bp[40]; |
| xfs_agnumber_t agno; |
| uuid_t uu; |
| uuid_t *uup = NULL; |
| |
| if (argc != 1 && argc != 2) { |
| dbprintf(_("invalid parameters\n")); |
| return 0; |
| } |
| |
| if (argc == 2) { /* WRITE UUID */ |
| |
| if ((x.isreadonly & LIBXFS_ISREADONLY) || !expert_mode) { |
| dbprintf(_("%s: not in expert mode, writing disabled\n"), |
| progname); |
| return 0; |
| } |
| |
| if (!strcasecmp(argv[1], "generate")) { |
| platform_uuid_generate(&uu); |
| } else if (!strcasecmp(argv[1], "nil")) { |
| platform_uuid_clear(&uu); |
| } else if (!strcasecmp(argv[1], "rewrite")) { |
| uup = do_uuid(0, NULL); |
| if (!uup) { |
| dbprintf(_("failed to read UUID from AG 0\n")); |
| return 0; |
| } |
| memcpy(&uu, uup, sizeof(uuid_t)); |
| platform_uuid_unparse(&uu, bp); |
| dbprintf(_("old UUID = %s\n"), bp); |
| } else if (!strcasecmp(argv[1], "restore")) { |
| xfs_sb_t tsb; |
| |
| if (!get_sb(0, &tsb)) |
| return 0; |
| |
| /* Not set; nothing to do. Success! */ |
| if (!xfs_sb_version_hasmetauuid(&tsb)) |
| return 0; |
| |
| memcpy(&uu, mp->m_sb.sb_meta_uuid, sizeof(uuid_t)); |
| } else { |
| if (platform_uuid_parse(argv[1], &uu)) { |
| dbprintf(_("invalid UUID\n")); |
| return 0; |
| } |
| } |
| |
| /* clear the log (setting uuid) if it's not dirty */ |
| if (!sb_logzero(&uu)) |
| return 0; |
| |
| dbprintf(_("writing all SBs\n")); |
| for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) |
| if (!do_uuid(agno, &uu)) { |
| dbprintf(_("failed to set UUID in AG %d\n"), agno); |
| break; |
| } |
| |
| platform_uuid_unparse(&uu, bp); |
| dbprintf(_("new UUID = %s\n"), bp); |
| return 0; |
| |
| } else { /* READ+CHECK UUID */ |
| |
| for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { |
| uup = do_uuid(agno, NULL); |
| if (!uup) { |
| dbprintf(_("failed to read UUID from AG %d\n"), |
| agno); |
| return 0; |
| } |
| if (agno) { |
| if (memcmp(&uu, uup, sizeof(uuid_t))) { |
| dbprintf(_("warning: UUID in AG %d " |
| "differs to the primary SB\n"), |
| agno); |
| break; |
| } |
| } else { |
| memcpy(&uu, uup, sizeof(uuid_t)); |
| } |
| } |
| if (mp->m_sb.sb_logstart) { |
| if (x.logdev && x.logdev != x.ddev) |
| dbprintf(_("warning - external log specified " |
| "for FS with an internal log\n")); |
| } else if (!x.logdev || (x.logdev == x.ddev)) { |
| dbprintf(_("warning - no external log specified " |
| "for FS with an external log\n")); |
| } |
| |
| platform_uuid_unparse(&uu, bp); |
| dbprintf(_("UUID = %s\n"), bp); |
| } |
| |
| return 0; |
| } |
| |
| |
| static void |
| label_help(void) |
| { |
| dbprintf(_( |
| "\n" |
| " write/print FS label\n" |
| "\n" |
| " Example:\n" |
| "\n" |
| " 'label' - print label\n" |
| " 'label 123456789012' - write label\n" |
| " 'label --' - write an empty label\n" |
| "\n" |
| "The print function checks the label in each SB and will warn if the labels\n" |
| "differ between AGs. The write commands will set the label in all AGs to the\n" |
| "specified value. The maximum length of a label is 12 characters - use of a\n" |
| "longer label will result in truncation and a warning will be issued.\n" |
| "\n" |
| )); |
| } |
| |
| static char * |
| do_label(xfs_agnumber_t agno, char *label) |
| { |
| size_t len; |
| xfs_sb_t tsb; |
| static char lbl[sizeof(tsb.sb_fname) + 1]; |
| |
| if (!get_sb(agno, &tsb)) |
| return NULL; |
| |
| memset(&lbl[0], 0, sizeof(lbl)); |
| |
| if (!label) { /* get label */ |
| pop_cur(); |
| memcpy(&lbl[0], &tsb.sb_fname, sizeof(tsb.sb_fname)); |
| return &lbl[0]; |
| } |
| /* set label */ |
| if ((len = strlen(label)) > sizeof(tsb.sb_fname)) { |
| if (agno == 0) |
| dbprintf(_("%s: truncating label length from %d to %d\n"), |
| progname, (int)len, (int)sizeof(tsb.sb_fname)); |
| len = sizeof(tsb.sb_fname); |
| } |
| if ( len == 2 && |
| (strcmp(label, "\"\"") == 0 || |
| strcmp(label, "''") == 0 || |
| strcmp(label, "--") == 0) ) |
| label[0] = label[1] = '\0'; |
| memset(&tsb.sb_fname, 0, sizeof(tsb.sb_fname)); |
| memcpy(&tsb.sb_fname, label, len); |
| memcpy(&lbl[0], &tsb.sb_fname, sizeof(tsb.sb_fname)); |
| libxfs_sb_to_disk(iocur_top->data, &tsb); |
| write_cur(); |
| return &lbl[0]; |
| } |
| |
| static int |
| label_f( |
| int argc, |
| char **argv) |
| { |
| char *p = NULL; |
| xfs_sb_t sb; |
| xfs_agnumber_t ag; |
| |
| if (argc != 1 && argc != 2) { |
| dbprintf(_("invalid parameters\n")); |
| return 0; |
| } |
| |
| if (argc == 2) { /* WRITE LABEL */ |
| |
| if ((x.isreadonly & LIBXFS_ISREADONLY) || !expert_mode) { |
| dbprintf(_("%s: not in expert mode, writing disabled\n"), |
| progname); |
| return 0; |
| } |
| |
| dbprintf(_("writing all SBs\n")); |
| for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) |
| if ((p = do_label(ag, argv[1])) == NULL) { |
| dbprintf(_("failed to set label in AG %d\n"), ag); |
| break; |
| } |
| dbprintf(_("new label = \"%s\"\n"), p); |
| |
| } else { /* READ LABEL */ |
| |
| for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { |
| p = do_label(ag, NULL); |
| if (!p) { |
| dbprintf(_("failed to read label in AG %d\n"), ag); |
| return 0; |
| } |
| if (!ag) |
| memcpy(&sb.sb_fname, p, sizeof(sb.sb_fname)); |
| else if (memcmp(&sb.sb_fname, p, sizeof(sb.sb_fname))) |
| dbprintf(_("warning: AG %d label differs\n"), ag); |
| } |
| dbprintf(_("label = \"%s\"\n"), p); |
| } |
| return 0; |
| } |
| |
| |
| static void |
| version_help(void) |
| { |
| dbprintf(_( |
| "\n" |
| " set/print feature bits in sb version\n" |
| "\n" |
| " Example:\n" |
| "\n" |
| " 'version' - print current feature bits\n" |
| " 'version extflg' - enable unwritten extents\n" |
| " 'version attr1' - enable v1 inline extended attributes\n" |
| " 'version attr2' - enable v2 inline extended attributes\n" |
| " 'version log2' - enable v2 log format\n" |
| "\n" |
| "The version function prints currently enabled features for a filesystem\n" |
| "according to the version field of its primary superblock.\n" |
| "It can also be used to enable selected features, such as support for\n" |
| "unwritten extents. The updated version is written into all AGs.\n" |
| "\n" |
| )); |
| } |
| |
| static int |
| do_version(xfs_agnumber_t agno, __uint16_t version, __uint32_t features) |
| { |
| xfs_sb_t tsb; |
| |
| if (!get_sb(agno, &tsb)) |
| return 0; |
| |
| if (xfs_sb_has_mismatched_features2(&tsb)) { |
| dbprintf(_("Superblock has mismatched features2 fields, " |
| "skipping modification\n")); |
| return 0; |
| } |
| |
| if ((version & XFS_SB_VERSION_LOGV2BIT) && |
| !xfs_sb_version_haslogv2(&tsb)) { |
| tsb.sb_logsunit = 1; |
| } |
| |
| tsb.sb_versionnum = version; |
| tsb.sb_features2 = features; |
| tsb.sb_bad_features2 = features; |
| libxfs_sb_to_disk(iocur_top->data, &tsb); |
| write_cur(); |
| return 1; |
| } |
| |
| static char * |
| version_string( |
| xfs_sb_t *sbp) |
| { |
| static char s[1024]; |
| |
| if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_1) |
| strcpy(s, "V1"); |
| else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_2) |
| strcpy(s, "V2"); |
| else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_3) |
| strcpy(s, "V3"); |
| else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) |
| strcpy(s, "V4"); |
| else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) |
| strcpy(s, "V5"); |
| |
| /* |
| * We assume the state of these features now, so macros don't exist for |
| * them any more. |
| */ |
| if (sbp->sb_versionnum & XFS_SB_VERSION_NLINKBIT) |
| strcat(s, ",NLINK"); |
| if (sbp->sb_versionnum & XFS_SB_VERSION_SHAREDBIT) |
| strcat(s, ",SHARED"); |
| if (sbp->sb_versionnum & XFS_SB_VERSION_DIRV2BIT) |
| strcat(s, ",DIRV2"); |
| |
| if (xfs_sb_version_hasattr(sbp)) |
| strcat(s, ",ATTR"); |
| if (xfs_sb_version_hasquota(sbp)) |
| strcat(s, ",QUOTA"); |
| if (xfs_sb_version_hasalign(sbp)) |
| strcat(s, ",ALIGN"); |
| if (xfs_sb_version_hasdalign(sbp)) |
| strcat(s, ",DALIGN"); |
| if (xfs_sb_version_haslogv2(sbp)) |
| strcat(s, ",LOGV2"); |
| if (xfs_sb_version_hasextflgbit(sbp)) |
| strcat(s, ",EXTFLG"); |
| if (xfs_sb_version_hassector(sbp)) |
| strcat(s, ",SECTOR"); |
| if (xfs_sb_version_hasasciici(sbp)) |
| strcat(s, ",ASCII_CI"); |
| if (xfs_sb_version_hasmorebits(sbp)) |
| strcat(s, ",MOREBITS"); |
| if (xfs_sb_version_hasattr2(sbp)) |
| strcat(s, ",ATTR2"); |
| if (xfs_sb_version_haslazysbcount(sbp)) |
| strcat(s, ",LAZYSBCOUNT"); |
| if (xfs_sb_version_hasprojid32bit(sbp)) |
| strcat(s, ",PROJID32BIT"); |
| if (xfs_sb_version_hascrc(sbp)) |
| strcat(s, ",CRC"); |
| if (xfs_sb_version_hasftype(sbp)) |
| strcat(s, ",FTYPE"); |
| if (xfs_sb_version_hasfinobt(sbp)) |
| strcat(s, ",FINOBT"); |
| if (xfs_sb_version_hassparseinodes(sbp)) |
| strcat(s, ",SPARSE_INODES"); |
| if (xfs_sb_version_hasmetauuid(sbp)) |
| strcat(s, ",META_UUID"); |
| if (xfs_sb_version_hasreflink(sbp)) |
| strcat(s, ",REFLINK"); |
| return s; |
| } |
| |
| /* |
| * XXX: this only supports reading and writing to version 4 superblock fields. |
| * V5 superblocks always define certain V4 feature bits - they are blocked from |
| * being changed if a V5 sb is detected, but otherwise v5 superblock features |
| * are not handled here. |
| */ |
| static int |
| version_f( |
| int argc, |
| char **argv) |
| { |
| __uint16_t version = 0; |
| __uint32_t features = 0; |
| xfs_agnumber_t ag; |
| |
| if (argc == 2) { /* WRITE VERSION */ |
| |
| if ((x.isreadonly & LIBXFS_ISREADONLY) || !expert_mode) { |
| dbprintf(_("%s: not in expert mode, writing disabled\n"), |
| progname); |
| return 0; |
| } |
| |
| /* Logic here derived from the IRIX xfs_chver(1M) script. */ |
| if (!strcasecmp(argv[1], "extflg")) { |
| switch (XFS_SB_VERSION_NUM(&mp->m_sb)) { |
| case XFS_SB_VERSION_1: |
| version = 0x0004 | XFS_SB_VERSION_EXTFLGBIT; |
| break; |
| case XFS_SB_VERSION_2: |
| version = 0x0014 | XFS_SB_VERSION_EXTFLGBIT; |
| break; |
| case XFS_SB_VERSION_3: |
| version = 0x0034 | XFS_SB_VERSION_EXTFLGBIT; |
| break; |
| case XFS_SB_VERSION_4: |
| if (xfs_sb_version_hasextflgbit(&mp->m_sb)) |
| dbprintf( |
| _("unwritten extents flag is already enabled\n")); |
| else |
| version = mp->m_sb.sb_versionnum | |
| XFS_SB_VERSION_EXTFLGBIT; |
| break; |
| case XFS_SB_VERSION_5: |
| dbprintf( |
| _("unwritten extents always enabled for v5 superblocks.\n")); |
| break; |
| } |
| } else if (!strcasecmp(argv[1], "log2")) { |
| switch (XFS_SB_VERSION_NUM(&mp->m_sb)) { |
| case XFS_SB_VERSION_1: |
| version = 0x0004 | XFS_SB_VERSION_LOGV2BIT; |
| break; |
| case XFS_SB_VERSION_2: |
| version = 0x0014 | XFS_SB_VERSION_LOGV2BIT; |
| break; |
| case XFS_SB_VERSION_3: |
| version = 0x0034 | XFS_SB_VERSION_LOGV2BIT; |
| break; |
| case XFS_SB_VERSION_4: |
| if (xfs_sb_version_haslogv2(&mp->m_sb)) |
| dbprintf( |
| _("version 2 log format is already in use\n")); |
| else |
| version = mp->m_sb.sb_versionnum | |
| XFS_SB_VERSION_LOGV2BIT; |
| break; |
| case XFS_SB_VERSION_5: |
| dbprintf( |
| _("Version 2 logs always enabled for v5 superblocks.\n")); |
| break; |
| } |
| } else if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5) { |
| dbprintf( |
| _("%s: Cannot change %s on v5 superblocks.\n"), |
| progname, argv[1]); |
| return 0; |
| } else if (!strcasecmp(argv[1], "attr1")) { |
| |
| if (xfs_sb_version_hasattr2(&mp->m_sb)) { |
| if (!(mp->m_sb.sb_features2 &= |
| ~XFS_SB_VERSION2_ATTR2BIT)) |
| mp->m_sb.sb_versionnum &= |
| ~XFS_SB_VERSION_MOREBITSBIT; |
| } |
| xfs_sb_version_addattr(&mp->m_sb); |
| version = mp->m_sb.sb_versionnum; |
| features = mp->m_sb.sb_features2; |
| } else if (!strcasecmp(argv[1], "attr2")) { |
| xfs_sb_version_addattr(&mp->m_sb); |
| xfs_sb_version_addattr2(&mp->m_sb); |
| version = mp->m_sb.sb_versionnum; |
| features = mp->m_sb.sb_features2; |
| } else if (!strcasecmp(argv[1], "projid32bit")) { |
| xfs_sb_version_addprojid32bit(&mp->m_sb); |
| version = mp->m_sb.sb_versionnum; |
| features = mp->m_sb.sb_features2; |
| } else { |
| dbprintf(_("%s: invalid version change command \"%s\"\n"), |
| progname, argv[1]); |
| return 0; |
| } |
| |
| if (version) { |
| dbprintf(_("writing all SBs\n")); |
| for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) |
| if (!do_version(ag, version, features)) { |
| dbprintf(_("failed to set versionnum " |
| "in AG %d\n"), ag); |
| break; |
| } |
| mp->m_sb.sb_versionnum = version; |
| mp->m_sb.sb_features2 = features; |
| } |
| } |
| |
| if (argc == 3) { /* VERSIONNUM + FEATURES2 */ |
| char *sp; |
| |
| version = mp->m_sb.sb_versionnum; |
| features = mp->m_sb.sb_features2; |
| mp->m_sb.sb_versionnum = strtoul(argv[1], &sp, 0); |
| mp->m_sb.sb_features2 = strtoul(argv[2], &sp, 0); |
| } |
| |
| dbprintf(_("versionnum [0x%x+0x%x] = %s\n"), mp->m_sb.sb_versionnum, |
| mp->m_sb.sb_features2, version_string(&mp->m_sb)); |
| |
| if (argc == 3) { /* now reset... */ |
| mp->m_sb.sb_versionnum = version; |
| mp->m_sb.sb_features2 = features; |
| return 0; |
| } |
| |
| return 0; |
| } |