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