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