]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/sb.c
Merge branch 'progs-header-cleanup' into for-next
[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 "libxfs.h"
20 #include "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 { "spino_align", FLDT_EXTLEN, OI(OFF(spino_align)), C1, 0, TYP_NONE },
123 { "pquotino", FLDT_INO, OI(OFF(pquotino)), C1, 0, TYP_INODE },
124 { "lsn", FLDT_UINT64X, OI(OFF(lsn)), C1, 0, TYP_NONE },
125 { NULL }
126 };
127
128 const field_t sb_hfld[] = {
129 { "", FLDT_SB, OI(0), C1, 0, TYP_NONE },
130 { NULL }
131 };
132
133 static void
134 sb_help(void)
135 {
136 dbprintf(_(
137 "\n"
138 " set allocation group superblock\n"
139 "\n"
140 " Example:\n"
141 "\n"
142 " 'sb 7' - set location to 7th allocation group superblock, set type to 'sb'\n"
143 "\n"
144 " Located in the first sector of each allocation group, the superblock\n"
145 " contains the base information for the filesystem.\n"
146 " The superblock in allocation group 0 is the primary. The copies in the\n"
147 " remaining allocation groups only serve as backup for filesystem recovery.\n"
148 " The icount/ifree/fdblocks/frextents are only updated in superblock 0.\n"
149 "\n"
150 ));
151 }
152
153 static int
154 sb_f(
155 int argc,
156 char **argv)
157 {
158 xfs_agnumber_t agno;
159 char *p;
160
161 if (argc > 1) {
162 agno = (xfs_agnumber_t)strtoul(argv[1], &p, 0);
163 if (*p != '\0' || agno >= mp->m_sb.sb_agcount) {
164 dbprintf(_("bad allocation group number %s\n"), argv[1]);
165 return 0;
166 }
167 cur_agno = agno;
168 } else if (cur_agno == NULLAGNUMBER)
169 cur_agno = 0;
170 ASSERT(typtab[TYP_SB].typnm == TYP_SB);
171 set_cur(&typtab[TYP_SB],
172 XFS_AG_DADDR(mp, cur_agno, XFS_SB_DADDR),
173 XFS_FSS_TO_BB(mp, 1), DB_RING_ADD, NULL);
174 return 0;
175 }
176
177 /*ARGSUSED*/
178 int
179 sb_size(
180 void *obj,
181 int startoff,
182 int idx)
183 {
184 return bitize(mp->m_sb.sb_sectsize);
185 }
186
187 static int
188 get_sb(xfs_agnumber_t agno, xfs_sb_t *sb)
189 {
190 push_cur();
191 set_cur(&typtab[TYP_SB],
192 XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
193 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
194
195 if (!iocur_top->data) {
196 dbprintf(_("can't read superblock for AG %u\n"), agno);
197 pop_cur();
198 return 0;
199 }
200
201 libxfs_sb_from_disk(sb, iocur_top->data);
202
203 if (sb->sb_magicnum != XFS_SB_MAGIC) {
204 dbprintf(_("bad sb magic # %#x in AG %u\n"),
205 sb->sb_magicnum, agno);
206 return 0;
207 }
208 if (!xfs_sb_good_version(sb)) {
209 dbprintf(_("bad sb version # %#x in AG %u\n"),
210 sb->sb_versionnum, agno);
211 return 0;
212 }
213 if (agno == 0 && sb->sb_inprogress != 0) {
214 dbprintf(_("mkfs not completed successfully\n"));
215 return 0;
216 }
217 return 1;
218 }
219
220 /* workaround craziness in the xlog routines */
221 int xlog_recover_do_trans(struct xlog *log, xlog_recover_t *t, int p)
222 {
223 return 0;
224 }
225
226 int
227 sb_logcheck(void)
228 {
229 int dirty;
230
231 if (mp->m_sb.sb_logstart) {
232 if (x.logdev && x.logdev != x.ddev) {
233 dbprintf(_("aborting - external log specified for FS "
234 "with an internal log\n"));
235 return 0;
236 }
237 } else {
238 if (!x.logdev || (x.logdev == x.ddev)) {
239 dbprintf(_("aborting - no external log specified for FS "
240 "with an external log\n"));
241 return 0;
242 }
243 }
244
245 libxfs_buftarg_init(mp, x.ddev, x.logdev, x.rtdev);
246
247 dirty = xlog_is_dirty(mp, &x, 0);
248 if (dirty == -1) {
249 dbprintf(_("ERROR: cannot find log head/tail, run xfs_repair\n"));
250 return 0;
251 } else if (dirty == 1) {
252 dbprintf(_(
253 "ERROR: The filesystem has valuable metadata changes in a log which needs to\n"
254 "be replayed. Mount the filesystem to replay the log, and unmount it before\n"
255 "re-running %s. If you are unable to mount the filesystem, then use\n"
256 "the xfs_repair -L option to destroy the log and attempt a repair.\n"
257 "Note that destroying the log may cause corruption -- please attempt a mount\n"
258 "of the filesystem before doing this.\n"), progname);
259 return 0;
260 }
261 /* Log is clean */
262 return 1;
263 }
264
265 static int
266 sb_logzero(uuid_t *uuidp)
267 {
268 if (!sb_logcheck())
269 return 0;
270
271 dbprintf(_("Clearing log and setting UUID\n"));
272
273 if (libxfs_log_clear(mp->m_logdev_targp,
274 XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart),
275 (xfs_extlen_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks),
276 uuidp,
277 xfs_sb_version_haslogv2(&mp->m_sb) ? 2 : 1,
278 mp->m_sb.sb_logsunit, XLOG_FMT)) {
279 dbprintf(_("ERROR: cannot clear the log\n"));
280 return 0;
281 }
282 return 1;
283 }
284
285
286 static void
287 uuid_help(void)
288 {
289 dbprintf(_(
290 "\n"
291 " write/print FS uuid\n"
292 "\n"
293 " Example:\n"
294 "\n"
295 " 'uuid' - print UUID\n"
296 " 'uuid 01234567-0123-0123-0123-0123456789ab' - write UUID\n"
297 " 'uuid generate' - generate and write\n"
298 " 'uuid rewrite' - copy UUID from SB 0\n"
299 "\n"
300 "The print function checks the UUID in each SB and will warn if the UUIDs\n"
301 "differ between AGs (the log is not checked). The write commands will\n"
302 "set the uuid in all AGs to either a specified value, a newly generated\n"
303 "value or the value found in the first superblock (SB 0) respectively.\n"
304 "As a side effect of writing the UUID, the log is cleared (which is fine\n"
305 "on a CLEANLY unmounted FS).\n"
306 "\n"
307 ));
308 }
309
310 static uuid_t *
311 do_uuid(xfs_agnumber_t agno, uuid_t *uuid)
312 {
313 xfs_sb_t tsb;
314 static uuid_t uu;
315
316 if (!get_sb(agno, &tsb))
317 return NULL;
318
319 if (!uuid) { /* get uuid */
320 memcpy(&uu, &tsb.sb_uuid, sizeof(uuid_t));
321 pop_cur();
322 return &uu;
323 }
324 /* set uuid */
325 memcpy(&tsb.sb_uuid, uuid, sizeof(uuid_t));
326 libxfs_sb_to_disk(iocur_top->data, &tsb);
327 write_cur();
328 return uuid;
329 }
330
331 static int
332 uuid_f(
333 int argc,
334 char **argv)
335 {
336 char bp[40];
337 xfs_agnumber_t agno;
338 uuid_t uu;
339 uuid_t *uup = NULL;
340
341 if (argc != 1 && argc != 2) {
342 dbprintf(_("invalid parameters\n"));
343 return 0;
344 }
345
346 if (argc == 2) { /* WRITE UUID */
347
348 if ((x.isreadonly & LIBXFS_ISREADONLY) || !expert_mode) {
349 dbprintf(_("%s: not in expert mode, writing disabled\n"),
350 progname);
351 return 0;
352 }
353
354 /*
355 * For now, changing the UUID of V5 superblock filesystems is
356 * not supported; we do not have the infrastructure to fix all
357 * other metadata when a new superblock UUID is generated.
358 */
359 if (xfs_sb_version_hascrc(&mp->m_sb) &&
360 strcasecmp(argv[1], "rewrite")) {
361 dbprintf(_("%s: only 'rewrite' supported on V5 fs\n"),
362 progname);
363 return 0;
364 }
365
366 if (!strcasecmp(argv[1], "generate")) {
367 platform_uuid_generate(&uu);
368 } else if (!strcasecmp(argv[1], "nil")) {
369 platform_uuid_clear(&uu);
370 } else if (!strcasecmp(argv[1], "rewrite")) {
371 uup = do_uuid(0, NULL);
372 if (!uup) {
373 dbprintf(_("failed to read UUID from AG 0\n"));
374 return 0;
375 }
376 memcpy(&uu, uup, sizeof(uuid_t));
377 platform_uuid_unparse(&uu, bp);
378 dbprintf(_("old UUID = %s\n"), bp);
379 } else {
380 if (platform_uuid_parse(argv[1], &uu)) {
381 dbprintf(_("invalid UUID\n"));
382 return 0;
383 }
384 }
385
386 /* clear the log (setting uuid) if it's not dirty */
387 if (!sb_logzero(&uu))
388 return 0;
389
390 dbprintf(_("writing all SBs\n"));
391 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++)
392 if (!do_uuid(agno, &uu)) {
393 dbprintf(_("failed to set UUID in AG %d\n"), agno);
394 break;
395 }
396
397 platform_uuid_unparse(&uu, bp);
398 dbprintf(_("new UUID = %s\n"), bp);
399 return 0;
400
401 } else { /* READ+CHECK UUID */
402
403 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
404 uup = do_uuid(agno, NULL);
405 if (!uup) {
406 dbprintf(_("failed to read UUID from AG %d\n"),
407 agno);
408 return 0;
409 }
410 if (agno) {
411 if (memcmp(&uu, uup, sizeof(uuid_t))) {
412 dbprintf(_("warning: UUID in AG %d "
413 "differs to the primary SB\n"),
414 agno);
415 break;
416 }
417 } else {
418 memcpy(&uu, uup, sizeof(uuid_t));
419 }
420 }
421 if (mp->m_sb.sb_logstart) {
422 if (x.logdev && x.logdev != x.ddev)
423 dbprintf(_("warning - external log specified "
424 "for FS with an internal log\n"));
425 } else if (!x.logdev || (x.logdev == x.ddev)) {
426 dbprintf(_("warning - no external log specified "
427 "for FS with an external log\n"));
428 }
429
430 platform_uuid_unparse(&uu, bp);
431 dbprintf(_("UUID = %s\n"), bp);
432 }
433
434 return 0;
435 }
436
437
438 static void
439 label_help(void)
440 {
441 dbprintf(_(
442 "\n"
443 " write/print FS label\n"
444 "\n"
445 " Example:\n"
446 "\n"
447 " 'label' - print label\n"
448 " 'label 123456789012' - write label\n"
449 " 'label --' - write an empty label\n"
450 "\n"
451 "The print function checks the label in each SB and will warn if the labels\n"
452 "differ between AGs. The write commands will set the label in all AGs to the\n"
453 "specified value. The maximum length of a label is 12 characters - use of a\n"
454 "longer label will result in truncation and a warning will be issued.\n"
455 "\n"
456 ));
457 }
458
459 static char *
460 do_label(xfs_agnumber_t agno, char *label)
461 {
462 size_t len;
463 xfs_sb_t tsb;
464 static char lbl[sizeof(tsb.sb_fname) + 1];
465
466 if (!get_sb(agno, &tsb))
467 return NULL;
468
469 memset(&lbl[0], 0, sizeof(lbl));
470
471 if (!label) { /* get label */
472 pop_cur();
473 memcpy(&lbl[0], &tsb.sb_fname, sizeof(tsb.sb_fname));
474 return &lbl[0];
475 }
476 /* set label */
477 if ((len = strlen(label)) > sizeof(tsb.sb_fname)) {
478 if (agno == 0)
479 dbprintf(_("%s: truncating label length from %d to %d\n"),
480 progname, (int)len, (int)sizeof(tsb.sb_fname));
481 len = sizeof(tsb.sb_fname);
482 }
483 if ( len == 2 &&
484 (strcmp(label, "\"\"") == 0 ||
485 strcmp(label, "''") == 0 ||
486 strcmp(label, "--") == 0) )
487 label[0] = label[1] = '\0';
488 memset(&tsb.sb_fname, 0, sizeof(tsb.sb_fname));
489 memcpy(&tsb.sb_fname, label, len);
490 memcpy(&lbl[0], &tsb.sb_fname, sizeof(tsb.sb_fname));
491 libxfs_sb_to_disk(iocur_top->data, &tsb);
492 write_cur();
493 return &lbl[0];
494 }
495
496 static int
497 label_f(
498 int argc,
499 char **argv)
500 {
501 char *p = NULL;
502 xfs_sb_t sb;
503 xfs_agnumber_t ag;
504
505 if (argc != 1 && argc != 2) {
506 dbprintf(_("invalid parameters\n"));
507 return 0;
508 }
509
510 if (argc == 2) { /* WRITE LABEL */
511
512 if ((x.isreadonly & LIBXFS_ISREADONLY) || !expert_mode) {
513 dbprintf(_("%s: not in expert mode, writing disabled\n"),
514 progname);
515 return 0;
516 }
517
518 dbprintf(_("writing all SBs\n"));
519 for (ag = 0; ag < mp->m_sb.sb_agcount; ag++)
520 if ((p = do_label(ag, argv[1])) == NULL) {
521 dbprintf(_("failed to set label in AG %d\n"), ag);
522 break;
523 }
524 dbprintf(_("new label = \"%s\"\n"), p);
525
526 } else { /* READ LABEL */
527
528 for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) {
529 p = do_label(ag, NULL);
530 if (!p) {
531 dbprintf(_("failed to read label in AG %d\n"), ag);
532 return 0;
533 }
534 if (!ag)
535 memcpy(&sb.sb_fname, p, sizeof(sb.sb_fname));
536 else if (memcmp(&sb.sb_fname, p, sizeof(sb.sb_fname)))
537 dbprintf(_("warning: AG %d label differs\n"), ag);
538 }
539 dbprintf(_("label = \"%s\"\n"), p);
540 }
541 return 0;
542 }
543
544
545 static void
546 version_help(void)
547 {
548 dbprintf(_(
549 "\n"
550 " set/print feature bits in sb version\n"
551 "\n"
552 " Example:\n"
553 "\n"
554 " 'version' - print current feature bits\n"
555 " 'version extflg' - enable unwritten extents\n"
556 " 'version attr1' - enable v1 inline extended attributes\n"
557 " 'version attr2' - enable v2 inline extended attributes\n"
558 " 'version log2' - enable v2 log format\n"
559 "\n"
560 "The version function prints currently enabled features for a filesystem\n"
561 "according to the version field of its primary superblock.\n"
562 "It can also be used to enable selected features, such as support for\n"
563 "unwritten extents. The updated version is written into all AGs.\n"
564 "\n"
565 ));
566 }
567
568 static int
569 do_version(xfs_agnumber_t agno, __uint16_t version, __uint32_t features)
570 {
571 xfs_sb_t tsb;
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 }
586
587 tsb.sb_versionnum = version;
588 tsb.sb_features2 = features;
589 tsb.sb_bad_features2 = features;
590 libxfs_sb_to_disk(iocur_top->data, &tsb);
591 write_cur();
592 return 1;
593 }
594
595 static char *
596 version_string(
597 xfs_sb_t *sbp)
598 {
599 static char s[1024];
600
601 if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_1)
602 strcpy(s, "V1");
603 else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_2)
604 strcpy(s, "V2");
605 else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_3)
606 strcpy(s, "V3");
607 else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4)
608 strcpy(s, "V4");
609 else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5)
610 strcpy(s, "V5");
611
612 /*
613 * We assume the state of these features now, so macros don't exist for
614 * them any more.
615 */
616 if (sbp->sb_versionnum & XFS_SB_VERSION_NLINKBIT)
617 strcat(s, ",NLINK");
618 if (sbp->sb_versionnum & XFS_SB_VERSION_SHAREDBIT)
619 strcat(s, ",SHARED");
620 if (sbp->sb_versionnum & XFS_SB_VERSION_DIRV2BIT)
621 strcat(s, ",DIRV2");
622
623 if (xfs_sb_version_hasattr(sbp))
624 strcat(s, ",ATTR");
625 if (xfs_sb_version_hasquota(sbp))
626 strcat(s, ",QUOTA");
627 if (xfs_sb_version_hasalign(sbp))
628 strcat(s, ",ALIGN");
629 if (xfs_sb_version_hasdalign(sbp))
630 strcat(s, ",DALIGN");
631 if (xfs_sb_version_haslogv2(sbp))
632 strcat(s, ",LOGV2");
633 if (xfs_sb_version_hasextflgbit(sbp))
634 strcat(s, ",EXTFLG");
635 if (xfs_sb_version_hassector(sbp))
636 strcat(s, ",SECTOR");
637 if (xfs_sb_version_hasasciici(sbp))
638 strcat(s, ",ASCII_CI");
639 if (xfs_sb_version_hasmorebits(sbp))
640 strcat(s, ",MOREBITS");
641 if (xfs_sb_version_hasattr2(sbp))
642 strcat(s, ",ATTR2");
643 if (xfs_sb_version_haslazysbcount(sbp))
644 strcat(s, ",LAZYSBCOUNT");
645 if (xfs_sb_version_hasprojid32bit(sbp))
646 strcat(s, ",PROJID32BIT");
647 if (xfs_sb_version_hascrc(sbp))
648 strcat(s, ",CRC");
649 if (xfs_sb_version_hasftype(sbp))
650 strcat(s, ",FTYPE");
651 if (xfs_sb_version_hasfinobt(sbp))
652 strcat(s, ",FINOBT");
653 if (xfs_sb_version_hassparseinodes(sbp))
654 strcat(s, ",SPARSE_INODES");
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 }