]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/sb.c
xfsprogs: Release v6.8.0
[thirdparty/xfsprogs-dev.git] / db / sb.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #include "libxfs.h"
8 #include "libxlog.h"
9 #include "command.h"
10 #include "type.h"
11 #include "faddr.h"
12 #include "fprint.h"
13 #include "field.h"
14 #include "io.h"
15 #include "sb.h"
16 #include "bit.h"
17 #include "output.h"
18 #include "init.h"
19
20 #define uuid_equal(s,d) (platform_uuid_compare((s),(d)) == 0)
21
22 static int sb_f(int argc, char **argv);
23 static void sb_help(void);
24 static int uuid_f(int argc, char **argv);
25 static void uuid_help(void);
26 static int label_f(int argc, char **argv);
27 static void label_help(void);
28 static int version_f(int argc, char **argv);
29 static void version_help(void);
30
31 static const cmdinfo_t sb_cmd =
32 { "sb", NULL, sb_f, 0, 1, 1, N_("[agno]"),
33 N_("set current address to sb header"), sb_help };
34 static const cmdinfo_t uuid_cmd =
35 { "uuid", NULL, uuid_f, 0, 1, 1, N_("[uuid]"),
36 N_("write/print FS uuid"), uuid_help };
37 static const cmdinfo_t label_cmd =
38 { "label", NULL, label_f, 0, 1, 1, N_("[label]"),
39 N_("write/print FS label"), label_help };
40 static const cmdinfo_t version_cmd =
41 { "version", NULL, version_f, 0, -1, 1, N_("[feature | [vnum fnum]]"),
42 N_("set feature bit(s) in the sb version field"), version_help };
43
44 void
45 sb_init(void)
46 {
47 add_command(&sb_cmd);
48 add_command(&uuid_cmd);
49 add_command(&label_cmd);
50 add_command(&version_cmd);
51 }
52
53 #define OFF(f) bitize(offsetof(xfs_sb_t, sb_ ## f))
54 #define SZC(f) szcount(xfs_sb_t, sb_ ## f)
55 const field_t sb_flds[] = {
56 { "magicnum", FLDT_UINT32X, OI(OFF(magicnum)), C1, 0, TYP_NONE },
57 { "blocksize", FLDT_UINT32D, OI(OFF(blocksize)), C1, 0, TYP_NONE },
58 { "dblocks", FLDT_DRFSBNO, OI(OFF(dblocks)), C1, 0, TYP_NONE },
59 { "rblocks", FLDT_DRFSBNO, OI(OFF(rblocks)), C1, 0, TYP_NONE },
60 { "rextents", FLDT_DRTBNO, OI(OFF(rextents)), C1, 0, TYP_NONE },
61 { "uuid", FLDT_UUID, OI(OFF(uuid)), C1, 0, TYP_NONE },
62 { "logstart", FLDT_DFSBNO, OI(OFF(logstart)), C1, 0, TYP_LOG },
63 { "rootino", FLDT_INO, OI(OFF(rootino)), C1, 0, TYP_INODE },
64 { "rbmino", FLDT_INO, OI(OFF(rbmino)), C1, 0, TYP_INODE },
65 { "rsumino", FLDT_INO, OI(OFF(rsumino)), C1, 0, TYP_INODE },
66 { "rextsize", FLDT_AGBLOCK, OI(OFF(rextsize)), C1, 0, TYP_NONE },
67 { "agblocks", FLDT_AGBLOCK, OI(OFF(agblocks)), C1, 0, TYP_NONE },
68 { "agcount", FLDT_AGNUMBER, OI(OFF(agcount)), C1, 0, TYP_NONE },
69 { "rbmblocks", FLDT_EXTLEN, OI(OFF(rbmblocks)), C1, 0, TYP_NONE },
70 { "logblocks", FLDT_EXTLEN, OI(OFF(logblocks)), C1, 0, TYP_NONE },
71 { "versionnum", FLDT_UINT16X, OI(OFF(versionnum)), C1, 0, TYP_NONE },
72 { "sectsize", FLDT_UINT16D, OI(OFF(sectsize)), C1, 0, TYP_NONE },
73 { "inodesize", FLDT_UINT16D, OI(OFF(inodesize)), C1, 0, TYP_NONE },
74 { "inopblock", FLDT_UINT16D, OI(OFF(inopblock)), C1, 0, TYP_NONE },
75 { "fname", FLDT_CHARNS, OI(OFF(fname)), CI(SZC(fname)), 0, TYP_NONE },
76 { "blocklog", FLDT_UINT8D, OI(OFF(blocklog)), C1, 0, TYP_NONE },
77 { "sectlog", FLDT_UINT8D, OI(OFF(sectlog)), C1, 0, TYP_NONE },
78 { "inodelog", FLDT_UINT8D, OI(OFF(inodelog)), C1, 0, TYP_NONE },
79 { "inopblog", FLDT_UINT8D, OI(OFF(inopblog)), C1, 0, TYP_NONE },
80 { "agblklog", FLDT_UINT8D, OI(OFF(agblklog)), C1, 0, TYP_NONE },
81 { "rextslog", FLDT_UINT8D, OI(OFF(rextslog)), C1, 0, TYP_NONE },
82 { "inprogress", FLDT_UINT8D, OI(OFF(inprogress)), C1, 0, TYP_NONE },
83 { "imax_pct", FLDT_UINT8D, OI(OFF(imax_pct)), C1, 0, TYP_NONE },
84 { "icount", FLDT_UINT64D, OI(OFF(icount)), C1, 0, TYP_NONE },
85 { "ifree", FLDT_UINT64D, OI(OFF(ifree)), C1, 0, TYP_NONE },
86 { "fdblocks", FLDT_UINT64D, OI(OFF(fdblocks)), C1, 0, TYP_NONE },
87 { "frextents", FLDT_UINT64D, OI(OFF(frextents)), C1, 0, TYP_NONE },
88 { "uquotino", FLDT_INO, OI(OFF(uquotino)), C1, 0, TYP_INODE },
89 { "gquotino", FLDT_INO, OI(OFF(gquotino)), C1, 0, TYP_INODE },
90 { "qflags", FLDT_UINT16X, OI(OFF(qflags)), C1, 0, TYP_NONE },
91 { "flags", FLDT_UINT8X, OI(OFF(flags)), C1, 0, TYP_NONE },
92 { "shared_vn", FLDT_UINT8D, OI(OFF(shared_vn)), C1, 0, TYP_NONE },
93 { "inoalignmt", FLDT_EXTLEN, OI(OFF(inoalignmt)), C1, 0, TYP_NONE },
94 { "unit", FLDT_UINT32D, OI(OFF(unit)), C1, 0, TYP_NONE },
95 { "width", FLDT_UINT32D, OI(OFF(width)), C1, 0, TYP_NONE },
96 { "dirblklog", FLDT_UINT8D, OI(OFF(dirblklog)), C1, 0, TYP_NONE },
97 { "logsectlog", FLDT_UINT8D, OI(OFF(logsectlog)), C1, 0, TYP_NONE },
98 { "logsectsize", FLDT_UINT16D, OI(OFF(logsectsize)), C1, 0, TYP_NONE },
99 { "logsunit", FLDT_UINT32D, OI(OFF(logsunit)), C1, 0, TYP_NONE },
100 { "features2", FLDT_UINT32X, OI(OFF(features2)), C1, 0, TYP_NONE },
101 { "bad_features2", FLDT_UINT32X, OI(OFF(bad_features2)),
102 C1, 0, TYP_NONE },
103 { "features_compat", FLDT_UINT32X, OI(OFF(features_compat)),
104 C1, 0, TYP_NONE },
105 { "features_ro_compat", FLDT_UINT32X, OI(OFF(features_ro_compat)),
106 C1, 0, TYP_NONE },
107 { "features_incompat", FLDT_UINT32X, OI(OFF(features_incompat)),
108 C1, 0, TYP_NONE },
109 { "features_log_incompat", FLDT_UINT32X, OI(OFF(features_log_incompat)),
110 C1, 0, TYP_NONE },
111 { "crc", FLDT_CRC, OI(OFF(crc)), C1, 0, TYP_NONE },
112 { "spino_align", FLDT_EXTLEN, OI(OFF(spino_align)), C1, 0, TYP_NONE },
113 { "pquotino", FLDT_INO, OI(OFF(pquotino)), C1, 0, TYP_INODE },
114 { "lsn", FLDT_UINT64X, OI(OFF(lsn)), C1, 0, TYP_NONE },
115 { "meta_uuid", FLDT_UUID, OI(OFF(meta_uuid)), C1, 0, TYP_NONE },
116 { NULL }
117 };
118
119 const field_t sb_hfld[] = {
120 { "", FLDT_SB, OI(0), C1, 0, TYP_NONE },
121 { NULL }
122 };
123
124 static void
125 sb_help(void)
126 {
127 dbprintf(_(
128 "\n"
129 " set allocation group superblock\n"
130 "\n"
131 " Example:\n"
132 "\n"
133 " 'sb 7' - set location to 7th allocation group superblock, set type to 'sb'\n"
134 "\n"
135 " Located in the first sector of each allocation group, the superblock\n"
136 " contains the base information for the filesystem.\n"
137 " The superblock in allocation group 0 is the primary. The copies in the\n"
138 " remaining allocation groups only serve as backup for filesystem recovery.\n"
139 " The icount/ifree/fdblocks/frextents are only updated in superblock 0.\n"
140 "\n"
141 ));
142 }
143
144 static int
145 sb_f(
146 int argc,
147 char **argv)
148 {
149 xfs_agnumber_t agno;
150 char *p;
151
152 if (argc > 1) {
153 agno = (xfs_agnumber_t)strtoul(argv[1], &p, 0);
154 if (*p != '\0' || agno >= mp->m_sb.sb_agcount) {
155 dbprintf(_("bad allocation group number %s\n"), argv[1]);
156 return 0;
157 }
158 cur_agno = agno;
159 } else if (cur_agno == NULLAGNUMBER)
160 cur_agno = 0;
161 ASSERT(typtab[TYP_SB].typnm == TYP_SB);
162 set_cur(&typtab[TYP_SB],
163 XFS_AG_DADDR(mp, cur_agno, XFS_SB_DADDR),
164 XFS_FSS_TO_BB(mp, 1), DB_RING_ADD, NULL);
165 return 0;
166 }
167
168 /*ARGSUSED*/
169 int
170 sb_size(
171 void *obj,
172 int startoff,
173 int idx)
174 {
175 return bitize(mp->m_sb.sb_sectsize);
176 }
177
178 static int
179 get_sb(xfs_agnumber_t agno, xfs_sb_t *sb)
180 {
181 push_cur();
182 set_cur(&typtab[TYP_SB],
183 XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
184 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
185
186 if (!iocur_top->data) {
187 dbprintf(_("can't read superblock for AG %u\n"), agno);
188 pop_cur();
189 return 0;
190 }
191
192 libxfs_sb_from_disk(sb, iocur_top->data);
193
194 if (sb->sb_magicnum != XFS_SB_MAGIC) {
195 dbprintf(_("bad sb magic # %#x in AG %u\n"),
196 sb->sb_magicnum, agno);
197 return 0;
198 }
199 if (!xfs_sb_good_version(sb)) {
200 dbprintf(_("bad sb version # %#x in AG %u\n"),
201 sb->sb_versionnum, agno);
202 return 0;
203 }
204 if (agno == 0 && sb->sb_inprogress != 0) {
205 dbprintf(_("mkfs not completed successfully\n"));
206 return 0;
207 }
208 return 1;
209 }
210
211 /* workaround craziness in the xlog routines */
212 int xlog_recover_do_trans(struct xlog *log, struct xlog_recover *t, int p)
213 {
214 return 0;
215 }
216
217 int
218 sb_logcheck(void)
219 {
220 int dirty;
221
222 if (mp->m_sb.sb_logstart) {
223 if (x.log.dev && x.log.dev != x.data.dev) {
224 dbprintf(_("aborting - external log specified for FS "
225 "with an internal log\n"));
226 return 0;
227 }
228 } else {
229 if (!x.log.dev || (x.log.dev == x.data.dev)) {
230 dbprintf(_("aborting - no external log specified for FS "
231 "with an external log\n"));
232 return 0;
233 }
234 }
235
236 libxfs_buftarg_init(mp, &x);
237
238 dirty = xlog_is_dirty(mp, mp->m_log);
239 if (dirty == -1) {
240 dbprintf(_("ERROR: cannot find log head/tail, run xfs_repair\n"));
241 return 0;
242 } else if (dirty == 1) {
243 dbprintf(_(
244 "ERROR: The filesystem has valuable metadata changes in a log which needs to\n"
245 "be replayed. Mount the filesystem to replay the log, and unmount it before\n"
246 "re-running %s. If you are unable to mount the filesystem, then use\n"
247 "the xfs_repair -L option to destroy the log and attempt a repair.\n"
248 "Note that destroying the log may cause corruption -- please attempt a mount\n"
249 "of the filesystem before doing this.\n"), progname);
250 return 0;
251 }
252 /* Log is clean */
253 return 1;
254 }
255
256 static int
257 sb_logzero(uuid_t *uuidp)
258 {
259 int cycle = XLOG_INIT_CYCLE;
260 int error;
261
262 if (!sb_logcheck())
263 return 0;
264
265 /*
266 * The log must always move forward on v5 superblocks. Bump it to the
267 * next cycle.
268 */
269 if (xfs_has_crc(mp))
270 cycle = mp->m_log->l_curr_cycle + 1;
271
272 dbprintf(_("Clearing log and setting UUID\n"));
273
274 error = libxfs_log_clear(mp->m_logdev_targp, NULL,
275 XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart),
276 (xfs_extlen_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks),
277 uuidp,
278 xfs_has_logv2(mp) ? 2 : 1,
279 mp->m_sb.sb_logsunit, XLOG_FMT, cycle, true);
280 if (error) {
281 dbprintf(_("ERROR: cannot clear the log\n"));
282 return 0;
283 }
284
285 return 1;
286 }
287
288
289 static void
290 uuid_help(void)
291 {
292 dbprintf(_(
293 "\n"
294 " write/print FS uuid\n"
295 "\n"
296 " Example:\n"
297 "\n"
298 " 'uuid' - print UUID\n"
299 " 'uuid 01234567-0123-0123-0123-0123456789ab' - write UUID\n"
300 " 'uuid generate' - generate and write\n"
301 " 'uuid rewrite' - copy UUID from SB 0\n"
302 "\n"
303 "The print function checks the UUID in each SB and will warn if the UUIDs\n"
304 "differ between AGs (the log is not checked). The write commands will\n"
305 "set the uuid in all AGs to either a specified value, a newly generated\n"
306 "value or the value found in the first superblock (SB 0) respectively.\n"
307 "As a side effect of writing the UUID, the log is cleared (which is fine\n"
308 "on a CLEANLY unmounted FS).\n"
309 "\n"
310 ));
311 }
312
313 static uuid_t *
314 do_uuid(xfs_agnumber_t agno, uuid_t *uuid)
315 {
316 xfs_sb_t tsb;
317 static uuid_t uu;
318
319 if (!get_sb(agno, &tsb))
320 return NULL;
321
322 if (!uuid) { /* get uuid */
323 memcpy(&uu, &tsb.sb_uuid, sizeof(uuid_t));
324 pop_cur();
325 return &uu;
326 }
327 /* set uuid */
328 if (!xfs_sb_version_hascrc(&tsb))
329 goto write;
330 /*
331 * If we have CRCs, and this UUID differs from that stamped in the
332 * metadata, set the incompat flag and copy the old one to the
333 * metadata-specific location.
334 *
335 * If we are setting the user-visible UUID back to match the metadata
336 * UUID, clear the metadata-specific location and the incompat flag.
337 */
338 if (!xfs_sb_version_hasmetauuid(&tsb) &&
339 !uuid_equal(uuid, &mp->m_sb.sb_meta_uuid)) {
340 mp->m_sb.sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_META_UUID;
341 tsb.sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_META_UUID;
342 memcpy(&tsb.sb_meta_uuid, &tsb.sb_uuid, sizeof(uuid_t));
343 } else if (xfs_sb_version_hasmetauuid(&tsb) &&
344 uuid_equal(uuid, &mp->m_sb.sb_meta_uuid)) {
345 memset(&tsb.sb_meta_uuid, 0, sizeof(uuid_t));
346 /* Write those zeros now; it's ignored once we clear the flag */
347 libxfs_sb_to_disk(iocur_top->data, &tsb);
348 mp->m_sb.sb_features_incompat &=
349 ~XFS_SB_FEAT_INCOMPAT_META_UUID;
350 tsb.sb_features_incompat &= ~XFS_SB_FEAT_INCOMPAT_META_UUID;
351 }
352
353 write:
354 memcpy(&tsb.sb_uuid, uuid, sizeof(uuid_t));
355 libxfs_sb_to_disk(iocur_top->data, &tsb);
356 write_cur();
357 return uuid;
358 }
359
360 static int
361 uuid_f(
362 int argc,
363 char **argv)
364 {
365 char bp[40];
366 xfs_agnumber_t agno;
367 uuid_t uu;
368 uuid_t *uup = NULL;
369
370 if (argc != 1 && argc != 2) {
371 dbprintf(_("invalid parameters\n"));
372 return 0;
373 }
374
375 if (argc == 2) { /* WRITE UUID */
376
377 if ((x.flags & LIBXFS_ISREADONLY) || !expert_mode) {
378 dbprintf(_("%s: not in expert mode, writing disabled\n"),
379 progname);
380 return 0;
381 }
382 if (xfs_sb_version_needsrepair(&mp->m_sb)) {
383 dbprintf(_("%s: filesystem needs xfs_repair\n"),
384 progname);
385 return 0;
386 }
387
388 if (!strcasecmp(argv[1], "generate")) {
389 platform_uuid_generate(&uu);
390 } else if (!strcasecmp(argv[1], "nil")) {
391 platform_uuid_clear(&uu);
392 } else if (!strcasecmp(argv[1], "rewrite")) {
393 uup = do_uuid(0, NULL);
394 if (!uup) {
395 dbprintf(_("failed to read UUID from AG 0\n"));
396 return 0;
397 }
398 memcpy(&uu, uup, sizeof(uuid_t));
399 platform_uuid_unparse(&uu, bp);
400 dbprintf(_("old UUID = %s\n"), bp);
401 } else if (!strcasecmp(argv[1], "restore")) {
402 xfs_sb_t tsb;
403
404 if (!get_sb(0, &tsb))
405 return 0;
406
407 /* Not set; nothing to do. Success! */
408 if (!xfs_sb_version_hasmetauuid(&tsb))
409 return 0;
410
411 memcpy(&uu, mp->m_sb.sb_meta_uuid, sizeof(uuid_t));
412 } else {
413 if (platform_uuid_parse(argv[1], &uu)) {
414 dbprintf(_("invalid UUID\n"));
415 return 0;
416 }
417 }
418
419 /* clear the log (setting uuid) if it's not dirty */
420 if (!sb_logzero(&uu))
421 return 0;
422
423 dbprintf(_("writing all SBs\n"));
424 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++)
425 if (!do_uuid(agno, &uu)) {
426 dbprintf(_("failed to set UUID in AG %d\n"), agno);
427 break;
428 }
429
430 platform_uuid_unparse(&uu, bp);
431 dbprintf(_("new UUID = %s\n"), bp);
432 return 0;
433
434 } else { /* READ+CHECK UUID */
435
436 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
437 uup = do_uuid(agno, NULL);
438 if (!uup) {
439 dbprintf(_("failed to read UUID from AG %d\n"),
440 agno);
441 return 0;
442 }
443 if (agno) {
444 if (memcmp(&uu, uup, sizeof(uuid_t))) {
445 dbprintf(_("warning: UUID in AG %d "
446 "differs to the primary SB\n"),
447 agno);
448 break;
449 }
450 } else {
451 memcpy(&uu, uup, sizeof(uuid_t));
452 }
453 }
454 if (mp->m_sb.sb_logstart) {
455 if (x.log.dev && x.log.dev != x.data.dev)
456 dbprintf(_("warning - external log specified "
457 "for FS with an internal log\n"));
458 } else if (!x.log.dev || (x.log.dev == x.data.dev)) {
459 dbprintf(_("warning - no external log specified "
460 "for FS with an external log\n"));
461 }
462
463 platform_uuid_unparse(&uu, bp);
464 dbprintf(_("UUID = %s\n"), bp);
465 }
466
467 return 0;
468 }
469
470
471 static void
472 label_help(void)
473 {
474 dbprintf(_(
475 "\n"
476 " write/print FS label\n"
477 "\n"
478 " Example:\n"
479 "\n"
480 " 'label' - print label\n"
481 " 'label 123456789012' - write label\n"
482 " 'label --' - write an empty label\n"
483 "\n"
484 "The print function checks the label in each SB and will warn if the labels\n"
485 "differ between AGs. The write commands will set the label in all AGs to the\n"
486 "specified value. The maximum length of a label is 12 characters - use of a\n"
487 "longer label will result in truncation and a warning will be issued.\n"
488 "\n"
489 ));
490 }
491
492 static char *
493 do_label(xfs_agnumber_t agno, char *label)
494 {
495 size_t len;
496 xfs_sb_t tsb;
497 static char lbl[sizeof(tsb.sb_fname) + 1];
498
499 if (!get_sb(agno, &tsb))
500 return NULL;
501
502 memset(&lbl[0], 0, sizeof(lbl));
503
504 if (!label) { /* get label */
505 pop_cur();
506 memcpy(&lbl[0], &tsb.sb_fname, sizeof(tsb.sb_fname));
507 return &lbl[0];
508 }
509 /* set label */
510 if ((len = strlen(label)) > sizeof(tsb.sb_fname)) {
511 if (agno == 0)
512 dbprintf(_("%s: truncating label length from %d to %d\n"),
513 progname, (int)len, (int)sizeof(tsb.sb_fname));
514 len = sizeof(tsb.sb_fname);
515 }
516 if ( len == 2 &&
517 (strcmp(label, "\"\"") == 0 ||
518 strcmp(label, "''") == 0 ||
519 strcmp(label, "--") == 0) )
520 label[0] = label[1] = '\0';
521 memset(&tsb.sb_fname, 0, sizeof(tsb.sb_fname));
522 memcpy(&tsb.sb_fname, label, len);
523 memcpy(&lbl[0], &tsb.sb_fname, sizeof(tsb.sb_fname));
524 libxfs_sb_to_disk(iocur_top->data, &tsb);
525 write_cur();
526 return &lbl[0];
527 }
528
529 static int
530 label_f(
531 int argc,
532 char **argv)
533 {
534 char *p = NULL;
535 xfs_sb_t sb;
536 xfs_agnumber_t ag;
537
538 if (argc != 1 && argc != 2) {
539 dbprintf(_("invalid parameters\n"));
540 return 0;
541 }
542
543 if (argc == 2) { /* WRITE LABEL */
544
545 if ((x.flags & LIBXFS_ISREADONLY) || !expert_mode) {
546 dbprintf(_("%s: not in expert mode, writing disabled\n"),
547 progname);
548 return 0;
549 }
550
551 if (xfs_sb_version_needsrepair(&mp->m_sb)) {
552 dbprintf(_("%s: filesystem needs xfs_repair\n"),
553 progname);
554 return 0;
555 }
556
557 dbprintf(_("writing all SBs\n"));
558 for (ag = 0; ag < mp->m_sb.sb_agcount; ag++)
559 if ((p = do_label(ag, argv[1])) == NULL) {
560 dbprintf(_("failed to set label in AG %d\n"), ag);
561 break;
562 }
563 dbprintf(_("new label = \"%s\"\n"), p);
564
565 } else { /* READ LABEL */
566
567 for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) {
568 p = do_label(ag, NULL);
569 if (!p) {
570 dbprintf(_("failed to read label in AG %d\n"), ag);
571 return 0;
572 }
573 if (!ag)
574 memcpy(&sb.sb_fname, p, sizeof(sb.sb_fname));
575 else if (memcmp(&sb.sb_fname, p, sizeof(sb.sb_fname)))
576 dbprintf(_("warning: AG %d label differs\n"), ag);
577 }
578 dbprintf(_("label = \"%s\"\n"), p);
579 }
580 return 0;
581 }
582
583
584 static void
585 version_help(void)
586 {
587 dbprintf(_(
588 "\n"
589 " set/print feature bits in sb version\n"
590 "\n"
591 " Example:\n"
592 "\n"
593 " 'version' - print current feature bits\n"
594 " 'version extflg' - enable unwritten extents\n"
595 " 'version attr1' - enable v1 inline extended attributes\n"
596 " 'version attr2' - enable v2 inline extended attributes\n"
597 " 'version log2' - enable v2 log format\n"
598 "\n"
599 "The version function prints currently enabled features for a filesystem\n"
600 "according to the version field of its primary superblock.\n"
601 "It can also be used to enable selected features, such as support for\n"
602 "unwritten extents. The updated version is written into all AGs.\n"
603 "\n"
604 ));
605 }
606
607 static int
608 do_version(xfs_agnumber_t agno, uint16_t version, uint32_t features)
609 {
610 xfs_sb_t tsb;
611
612 if (!get_sb(agno, &tsb))
613 return 0;
614
615 if (xfs_sb_has_mismatched_features2(&tsb)) {
616 dbprintf(_("Superblock has mismatched features2 fields, "
617 "skipping modification\n"));
618 return 0;
619 }
620
621 if ((version & XFS_SB_VERSION_LOGV2BIT) &&
622 !xfs_sb_version_haslogv2(&tsb)) {
623 tsb.sb_logsunit = 1;
624 }
625
626 tsb.sb_versionnum = version;
627 tsb.sb_features2 = features;
628 tsb.sb_bad_features2 = features;
629 libxfs_sb_to_disk(iocur_top->data, &tsb);
630 write_cur();
631 return 1;
632 }
633
634 static char *
635 version_string(
636 struct xfs_mount *mp)
637 {
638 static char s[1024];
639
640 if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_1)
641 strcpy(s, "V1");
642 else if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_2)
643 strcpy(s, "V2");
644 else if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_3)
645 strcpy(s, "V3");
646 else if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_4)
647 strcpy(s, "V4");
648 else if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5)
649 strcpy(s, "V5");
650
651 /*
652 * We assume the state of these features now, so macros don't exist for
653 * them any more.
654 */
655 if (xfs_has_nlink(mp))
656 strcat(s, ",NLINK");
657 if (mp->m_sb.sb_versionnum & XFS_SB_VERSION_SHAREDBIT)
658 strcat(s, ",SHARED");
659 if (mp->m_sb.sb_versionnum & XFS_SB_VERSION_DIRV2BIT)
660 strcat(s, ",DIRV2");
661
662 if (xfs_has_attr(mp))
663 strcat(s, ",ATTR");
664 if (xfs_has_quota(mp))
665 strcat(s, ",QUOTA");
666 if (xfs_has_align(mp))
667 strcat(s, ",ALIGN");
668 if (xfs_has_dalign(mp))
669 strcat(s, ",DALIGN");
670 if (xfs_has_logv2(mp))
671 strcat(s, ",LOGV2");
672 /* This feature is required now as well */
673 if (xfs_has_extflg(mp))
674 strcat(s, ",EXTFLG");
675 if (xfs_has_sector(mp))
676 strcat(s, ",SECTOR");
677 if (xfs_has_asciici(mp))
678 strcat(s, ",ASCII_CI");
679 if (mp->m_sb.sb_versionnum & XFS_SB_VERSION_MOREBITSBIT)
680 strcat(s, ",MOREBITS");
681 if (xfs_has_attr2(mp))
682 strcat(s, ",ATTR2");
683 if (xfs_has_lazysbcount(mp))
684 strcat(s, ",LAZYSBCOUNT");
685 if (xfs_has_projid32(mp))
686 strcat(s, ",PROJID32BIT");
687 if (xfs_has_crc(mp))
688 strcat(s, ",CRC");
689 if (xfs_has_ftype(mp))
690 strcat(s, ",FTYPE");
691 if (xfs_has_finobt(mp))
692 strcat(s, ",FINOBT");
693 if (xfs_has_sparseinodes(mp))
694 strcat(s, ",SPARSE_INODES");
695 if (xfs_has_metauuid(mp))
696 strcat(s, ",META_UUID");
697 if (xfs_has_rmapbt(mp))
698 strcat(s, ",RMAPBT");
699 if (xfs_has_reflink(mp))
700 strcat(s, ",REFLINK");
701 if (xfs_has_inobtcounts(mp))
702 strcat(s, ",INOBTCNT");
703 if (xfs_has_bigtime(mp))
704 strcat(s, ",BIGTIME");
705 if (xfs_has_needsrepair(mp))
706 strcat(s, ",NEEDSREPAIR");
707 if (xfs_has_large_extent_counts(mp))
708 strcat(s, ",NREXT64");
709 return s;
710 }
711
712 /*
713 * XXX: this only supports reading and writing to version 4 superblock fields.
714 * V5 superblocks always define certain V4 feature bits - they are blocked from
715 * being changed if a V5 sb is detected, but otherwise v5 superblock features
716 * are not handled here.
717 */
718 static int
719 version_f(
720 int argc,
721 char **argv)
722 {
723 uint16_t version = 0;
724 uint32_t features = 0;
725 unsigned long old_mfeatures = 0;
726 xfs_agnumber_t ag;
727
728 if (argc == 2) { /* WRITE VERSION */
729
730 if ((x.flags & LIBXFS_ISREADONLY) || !expert_mode) {
731 dbprintf(_("%s: not in expert mode, writing disabled\n"),
732 progname);
733 return 0;
734 }
735
736 /* Logic here derived from the IRIX xfs_chver(1M) script. */
737 if (!strcasecmp(argv[1], "extflg")) {
738 switch (XFS_SB_VERSION_NUM(&mp->m_sb)) {
739 case XFS_SB_VERSION_1:
740 version = 0x0004 | XFS_SB_VERSION_EXTFLGBIT;
741 break;
742 case XFS_SB_VERSION_2:
743 version = 0x0014 | XFS_SB_VERSION_EXTFLGBIT;
744 break;
745 case XFS_SB_VERSION_3:
746 version = 0x0034 | XFS_SB_VERSION_EXTFLGBIT;
747 break;
748 case XFS_SB_VERSION_4:
749 if (mp->m_sb.sb_versionnum &
750 XFS_SB_VERSION_EXTFLGBIT)
751 dbprintf(
752 _("unwritten extents flag is already enabled\n"));
753 else
754 version = mp->m_sb.sb_versionnum |
755 XFS_SB_VERSION_EXTFLGBIT;
756 break;
757 case XFS_SB_VERSION_5:
758 dbprintf(
759 _("unwritten extents always enabled for v5 superblocks.\n"));
760 break;
761 }
762 } else if (!strcasecmp(argv[1], "log2")) {
763 switch (XFS_SB_VERSION_NUM(&mp->m_sb)) {
764 case XFS_SB_VERSION_1:
765 version = 0x0004 | XFS_SB_VERSION_LOGV2BIT;
766 break;
767 case XFS_SB_VERSION_2:
768 version = 0x0014 | XFS_SB_VERSION_LOGV2BIT;
769 break;
770 case XFS_SB_VERSION_3:
771 version = 0x0034 | XFS_SB_VERSION_LOGV2BIT;
772 break;
773 case XFS_SB_VERSION_4:
774 if (xfs_has_logv2(mp))
775 dbprintf(
776 _("version 2 log format is already in use\n"));
777 else
778 version = mp->m_sb.sb_versionnum |
779 XFS_SB_VERSION_LOGV2BIT;
780 break;
781 case XFS_SB_VERSION_5:
782 dbprintf(
783 _("Version 2 logs always enabled for v5 superblocks.\n"));
784 break;
785 }
786 } else if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5) {
787 dbprintf(
788 _("%s: Cannot change %s on v5 superblocks.\n"),
789 progname, argv[1]);
790 return 0;
791 } else if (!strcasecmp(argv[1], "attr1")) {
792
793 if (xfs_has_attr2(mp)) {
794 if (!(mp->m_sb.sb_features2 &=
795 ~XFS_SB_VERSION2_ATTR2BIT))
796 mp->m_sb.sb_versionnum &=
797 ~XFS_SB_VERSION_MOREBITSBIT;
798 }
799 xfs_sb_version_addattr(&mp->m_sb);
800 version = mp->m_sb.sb_versionnum;
801 features = mp->m_sb.sb_features2;
802 } else if (!strcasecmp(argv[1], "attr2")) {
803 xfs_sb_version_addattr(&mp->m_sb);
804 xfs_sb_version_addattr2(&mp->m_sb);
805 version = mp->m_sb.sb_versionnum;
806 features = mp->m_sb.sb_features2;
807 } else if (!strcasecmp(argv[1], "projid32bit")) {
808 xfs_sb_version_addprojid32(&mp->m_sb);
809 version = mp->m_sb.sb_versionnum;
810 features = mp->m_sb.sb_features2;
811 } else {
812 dbprintf(_("%s: invalid version change command \"%s\"\n"),
813 progname, argv[1]);
814 return 0;
815 }
816
817 if (version) {
818 dbprintf(_("writing all SBs\n"));
819 for (ag = 0; ag < mp->m_sb.sb_agcount; ag++)
820 if (!do_version(ag, version, features)) {
821 dbprintf(_("failed to set versionnum "
822 "in AG %d\n"), ag);
823 break;
824 }
825 mp->m_sb.sb_versionnum = version;
826 mp->m_sb.sb_features2 = features;
827 mp->m_features &= ~XFS_FEAT_ATTR2;
828 mp->m_features |= libxfs_sb_version_to_features(&mp->m_sb);
829 }
830 }
831
832 if (argc == 3) { /* VERSIONNUM + FEATURES2 */
833 char *sp;
834
835 version = mp->m_sb.sb_versionnum;
836 features = mp->m_sb.sb_features2;
837 mp->m_sb.sb_versionnum = strtoul(argv[1], &sp, 0);
838 mp->m_sb.sb_features2 = strtoul(argv[2], &sp, 0);
839 old_mfeatures = mp->m_features;
840 mp->m_features = libxfs_sb_version_to_features(&mp->m_sb);
841 }
842
843 dbprintf(_("versionnum [0x%x+0x%x] = %s\n"), mp->m_sb.sb_versionnum,
844 mp->m_sb.sb_features2, version_string(mp));
845
846 if (argc == 3) { /* now reset... */
847 mp->m_sb.sb_versionnum = version;
848 mp->m_sb.sb_features2 = features;
849 mp->m_features = old_mfeatures;
850 return 0;
851 }
852
853 return 0;
854 }