]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/sb.c
xfs: zero length symlinks are not valid
[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, xlog_recover_t *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.logdev && x.logdev != x.ddev) {
224 dbprintf(_("aborting - external log specified for FS "
225 "with an internal log\n"));
226 return 0;
227 }
228 } else {
229 if (!x.logdev || (x.logdev == x.ddev)) {
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.ddev, x.logdev, x.rtdev);
237
238 dirty = xlog_is_dirty(mp, mp->m_log, &x, 0);
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_sb_version_hascrc(&mp->m_sb))
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_sb_version_haslogv2(&mp->m_sb) ? 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.isreadonly & LIBXFS_ISREADONLY) || !expert_mode) {
378 dbprintf(_("%s: not in expert mode, writing disabled\n"),
379 progname);
380 return 0;
381 }
382
383 if (!strcasecmp(argv[1], "generate")) {
384 platform_uuid_generate(&uu);
385 } else if (!strcasecmp(argv[1], "nil")) {
386 platform_uuid_clear(&uu);
387 } else if (!strcasecmp(argv[1], "rewrite")) {
388 uup = do_uuid(0, NULL);
389 if (!uup) {
390 dbprintf(_("failed to read UUID from AG 0\n"));
391 return 0;
392 }
393 memcpy(&uu, uup, sizeof(uuid_t));
394 platform_uuid_unparse(&uu, bp);
395 dbprintf(_("old UUID = %s\n"), bp);
396 } else if (!strcasecmp(argv[1], "restore")) {
397 xfs_sb_t tsb;
398
399 if (!get_sb(0, &tsb))
400 return 0;
401
402 /* Not set; nothing to do. Success! */
403 if (!xfs_sb_version_hasmetauuid(&tsb))
404 return 0;
405
406 memcpy(&uu, mp->m_sb.sb_meta_uuid, sizeof(uuid_t));
407 } else {
408 if (platform_uuid_parse(argv[1], &uu)) {
409 dbprintf(_("invalid UUID\n"));
410 return 0;
411 }
412 }
413
414 /* clear the log (setting uuid) if it's not dirty */
415 if (!sb_logzero(&uu))
416 return 0;
417
418 dbprintf(_("writing all SBs\n"));
419 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++)
420 if (!do_uuid(agno, &uu)) {
421 dbprintf(_("failed to set UUID in AG %d\n"), agno);
422 break;
423 }
424
425 platform_uuid_unparse(&uu, bp);
426 dbprintf(_("new UUID = %s\n"), bp);
427 return 0;
428
429 } else { /* READ+CHECK UUID */
430
431 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
432 uup = do_uuid(agno, NULL);
433 if (!uup) {
434 dbprintf(_("failed to read UUID from AG %d\n"),
435 agno);
436 return 0;
437 }
438 if (agno) {
439 if (memcmp(&uu, uup, sizeof(uuid_t))) {
440 dbprintf(_("warning: UUID in AG %d "
441 "differs to the primary SB\n"),
442 agno);
443 break;
444 }
445 } else {
446 memcpy(&uu, uup, sizeof(uuid_t));
447 }
448 }
449 if (mp->m_sb.sb_logstart) {
450 if (x.logdev && x.logdev != x.ddev)
451 dbprintf(_("warning - external log specified "
452 "for FS with an internal log\n"));
453 } else if (!x.logdev || (x.logdev == x.ddev)) {
454 dbprintf(_("warning - no external log specified "
455 "for FS with an external log\n"));
456 }
457
458 platform_uuid_unparse(&uu, bp);
459 dbprintf(_("UUID = %s\n"), bp);
460 }
461
462 return 0;
463 }
464
465
466 static void
467 label_help(void)
468 {
469 dbprintf(_(
470 "\n"
471 " write/print FS label\n"
472 "\n"
473 " Example:\n"
474 "\n"
475 " 'label' - print label\n"
476 " 'label 123456789012' - write label\n"
477 " 'label --' - write an empty label\n"
478 "\n"
479 "The print function checks the label in each SB and will warn if the labels\n"
480 "differ between AGs. The write commands will set the label in all AGs to the\n"
481 "specified value. The maximum length of a label is 12 characters - use of a\n"
482 "longer label will result in truncation and a warning will be issued.\n"
483 "\n"
484 ));
485 }
486
487 static char *
488 do_label(xfs_agnumber_t agno, char *label)
489 {
490 size_t len;
491 xfs_sb_t tsb;
492 static char lbl[sizeof(tsb.sb_fname) + 1];
493
494 if (!get_sb(agno, &tsb))
495 return NULL;
496
497 memset(&lbl[0], 0, sizeof(lbl));
498
499 if (!label) { /* get label */
500 pop_cur();
501 memcpy(&lbl[0], &tsb.sb_fname, sizeof(tsb.sb_fname));
502 return &lbl[0];
503 }
504 /* set label */
505 if ((len = strlen(label)) > sizeof(tsb.sb_fname)) {
506 if (agno == 0)
507 dbprintf(_("%s: truncating label length from %d to %d\n"),
508 progname, (int)len, (int)sizeof(tsb.sb_fname));
509 len = sizeof(tsb.sb_fname);
510 }
511 if ( len == 2 &&
512 (strcmp(label, "\"\"") == 0 ||
513 strcmp(label, "''") == 0 ||
514 strcmp(label, "--") == 0) )
515 label[0] = label[1] = '\0';
516 memset(&tsb.sb_fname, 0, sizeof(tsb.sb_fname));
517 memcpy(&tsb.sb_fname, label, len);
518 memcpy(&lbl[0], &tsb.sb_fname, sizeof(tsb.sb_fname));
519 libxfs_sb_to_disk(iocur_top->data, &tsb);
520 write_cur();
521 return &lbl[0];
522 }
523
524 static int
525 label_f(
526 int argc,
527 char **argv)
528 {
529 char *p = NULL;
530 xfs_sb_t sb;
531 xfs_agnumber_t ag;
532
533 if (argc != 1 && argc != 2) {
534 dbprintf(_("invalid parameters\n"));
535 return 0;
536 }
537
538 if (argc == 2) { /* WRITE LABEL */
539
540 if ((x.isreadonly & LIBXFS_ISREADONLY) || !expert_mode) {
541 dbprintf(_("%s: not in expert mode, writing disabled\n"),
542 progname);
543 return 0;
544 }
545
546 dbprintf(_("writing all SBs\n"));
547 for (ag = 0; ag < mp->m_sb.sb_agcount; ag++)
548 if ((p = do_label(ag, argv[1])) == NULL) {
549 dbprintf(_("failed to set label in AG %d\n"), ag);
550 break;
551 }
552 dbprintf(_("new label = \"%s\"\n"), p);
553
554 } else { /* READ LABEL */
555
556 for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) {
557 p = do_label(ag, NULL);
558 if (!p) {
559 dbprintf(_("failed to read label in AG %d\n"), ag);
560 return 0;
561 }
562 if (!ag)
563 memcpy(&sb.sb_fname, p, sizeof(sb.sb_fname));
564 else if (memcmp(&sb.sb_fname, p, sizeof(sb.sb_fname)))
565 dbprintf(_("warning: AG %d label differs\n"), ag);
566 }
567 dbprintf(_("label = \"%s\"\n"), p);
568 }
569 return 0;
570 }
571
572
573 static void
574 version_help(void)
575 {
576 dbprintf(_(
577 "\n"
578 " set/print feature bits in sb version\n"
579 "\n"
580 " Example:\n"
581 "\n"
582 " 'version' - print current feature bits\n"
583 " 'version extflg' - enable unwritten extents\n"
584 " 'version attr1' - enable v1 inline extended attributes\n"
585 " 'version attr2' - enable v2 inline extended attributes\n"
586 " 'version log2' - enable v2 log format\n"
587 "\n"
588 "The version function prints currently enabled features for a filesystem\n"
589 "according to the version field of its primary superblock.\n"
590 "It can also be used to enable selected features, such as support for\n"
591 "unwritten extents. The updated version is written into all AGs.\n"
592 "\n"
593 ));
594 }
595
596 static int
597 do_version(xfs_agnumber_t agno, uint16_t version, uint32_t features)
598 {
599 xfs_sb_t tsb;
600
601 if (!get_sb(agno, &tsb))
602 return 0;
603
604 if (xfs_sb_has_mismatched_features2(&tsb)) {
605 dbprintf(_("Superblock has mismatched features2 fields, "
606 "skipping modification\n"));
607 return 0;
608 }
609
610 if ((version & XFS_SB_VERSION_LOGV2BIT) &&
611 !xfs_sb_version_haslogv2(&tsb)) {
612 tsb.sb_logsunit = 1;
613 }
614
615 tsb.sb_versionnum = version;
616 tsb.sb_features2 = features;
617 tsb.sb_bad_features2 = features;
618 libxfs_sb_to_disk(iocur_top->data, &tsb);
619 write_cur();
620 return 1;
621 }
622
623 static char *
624 version_string(
625 xfs_sb_t *sbp)
626 {
627 static char s[1024];
628
629 if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_1)
630 strcpy(s, "V1");
631 else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_2)
632 strcpy(s, "V2");
633 else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_3)
634 strcpy(s, "V3");
635 else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4)
636 strcpy(s, "V4");
637 else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5)
638 strcpy(s, "V5");
639
640 /*
641 * We assume the state of these features now, so macros don't exist for
642 * them any more.
643 */
644 if (sbp->sb_versionnum & XFS_SB_VERSION_NLINKBIT)
645 strcat(s, ",NLINK");
646 if (sbp->sb_versionnum & XFS_SB_VERSION_SHAREDBIT)
647 strcat(s, ",SHARED");
648 if (sbp->sb_versionnum & XFS_SB_VERSION_DIRV2BIT)
649 strcat(s, ",DIRV2");
650
651 if (xfs_sb_version_hasattr(sbp))
652 strcat(s, ",ATTR");
653 if (xfs_sb_version_hasquota(sbp))
654 strcat(s, ",QUOTA");
655 if (xfs_sb_version_hasalign(sbp))
656 strcat(s, ",ALIGN");
657 if (xfs_sb_version_hasdalign(sbp))
658 strcat(s, ",DALIGN");
659 if (xfs_sb_version_haslogv2(sbp))
660 strcat(s, ",LOGV2");
661 /* This feature is required now as well */
662 if (sbp->sb_versionnum & XFS_SB_VERSION_EXTFLGBIT)
663 strcat(s, ",EXTFLG");
664 if (xfs_sb_version_hassector(sbp))
665 strcat(s, ",SECTOR");
666 if (xfs_sb_version_hasasciici(sbp))
667 strcat(s, ",ASCII_CI");
668 if (xfs_sb_version_hasmorebits(sbp))
669 strcat(s, ",MOREBITS");
670 if (xfs_sb_version_hasattr2(sbp))
671 strcat(s, ",ATTR2");
672 if (xfs_sb_version_haslazysbcount(sbp))
673 strcat(s, ",LAZYSBCOUNT");
674 if (xfs_sb_version_hasprojid32bit(sbp))
675 strcat(s, ",PROJID32BIT");
676 if (xfs_sb_version_hascrc(sbp))
677 strcat(s, ",CRC");
678 if (xfs_sb_version_hasftype(sbp))
679 strcat(s, ",FTYPE");
680 if (xfs_sb_version_hasfinobt(sbp))
681 strcat(s, ",FINOBT");
682 if (xfs_sb_version_hassparseinodes(sbp))
683 strcat(s, ",SPARSE_INODES");
684 if (xfs_sb_version_hasmetauuid(sbp))
685 strcat(s, ",META_UUID");
686 if (xfs_sb_version_hasrmapbt(sbp))
687 strcat(s, ",RMAPBT");
688 if (xfs_sb_version_hasreflink(sbp))
689 strcat(s, ",REFLINK");
690 return s;
691 }
692
693 /*
694 * XXX: this only supports reading and writing to version 4 superblock fields.
695 * V5 superblocks always define certain V4 feature bits - they are blocked from
696 * being changed if a V5 sb is detected, but otherwise v5 superblock features
697 * are not handled here.
698 */
699 static int
700 version_f(
701 int argc,
702 char **argv)
703 {
704 uint16_t version = 0;
705 uint32_t features = 0;
706 xfs_agnumber_t ag;
707
708 if (argc == 2) { /* WRITE VERSION */
709
710 if ((x.isreadonly & LIBXFS_ISREADONLY) || !expert_mode) {
711 dbprintf(_("%s: not in expert mode, writing disabled\n"),
712 progname);
713 return 0;
714 }
715
716 /* Logic here derived from the IRIX xfs_chver(1M) script. */
717 if (!strcasecmp(argv[1], "extflg")) {
718 switch (XFS_SB_VERSION_NUM(&mp->m_sb)) {
719 case XFS_SB_VERSION_1:
720 version = 0x0004 | XFS_SB_VERSION_EXTFLGBIT;
721 break;
722 case XFS_SB_VERSION_2:
723 version = 0x0014 | XFS_SB_VERSION_EXTFLGBIT;
724 break;
725 case XFS_SB_VERSION_3:
726 version = 0x0034 | XFS_SB_VERSION_EXTFLGBIT;
727 break;
728 case XFS_SB_VERSION_4:
729 if (mp->m_sb.sb_versionnum &
730 XFS_SB_VERSION_EXTFLGBIT)
731 dbprintf(
732 _("unwritten extents flag is already enabled\n"));
733 else
734 version = mp->m_sb.sb_versionnum |
735 XFS_SB_VERSION_EXTFLGBIT;
736 break;
737 case XFS_SB_VERSION_5:
738 dbprintf(
739 _("unwritten extents always enabled for v5 superblocks.\n"));
740 break;
741 }
742 } else if (!strcasecmp(argv[1], "log2")) {
743 switch (XFS_SB_VERSION_NUM(&mp->m_sb)) {
744 case XFS_SB_VERSION_1:
745 version = 0x0004 | XFS_SB_VERSION_LOGV2BIT;
746 break;
747 case XFS_SB_VERSION_2:
748 version = 0x0014 | XFS_SB_VERSION_LOGV2BIT;
749 break;
750 case XFS_SB_VERSION_3:
751 version = 0x0034 | XFS_SB_VERSION_LOGV2BIT;
752 break;
753 case XFS_SB_VERSION_4:
754 if (xfs_sb_version_haslogv2(&mp->m_sb))
755 dbprintf(
756 _("version 2 log format is already in use\n"));
757 else
758 version = mp->m_sb.sb_versionnum |
759 XFS_SB_VERSION_LOGV2BIT;
760 break;
761 case XFS_SB_VERSION_5:
762 dbprintf(
763 _("Version 2 logs always enabled for v5 superblocks.\n"));
764 break;
765 }
766 } else if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5) {
767 dbprintf(
768 _("%s: Cannot change %s on v5 superblocks.\n"),
769 progname, argv[1]);
770 return 0;
771 } else if (!strcasecmp(argv[1], "attr1")) {
772
773 if (xfs_sb_version_hasattr2(&mp->m_sb)) {
774 if (!(mp->m_sb.sb_features2 &=
775 ~XFS_SB_VERSION2_ATTR2BIT))
776 mp->m_sb.sb_versionnum &=
777 ~XFS_SB_VERSION_MOREBITSBIT;
778 }
779 xfs_sb_version_addattr(&mp->m_sb);
780 version = mp->m_sb.sb_versionnum;
781 features = mp->m_sb.sb_features2;
782 } else if (!strcasecmp(argv[1], "attr2")) {
783 xfs_sb_version_addattr(&mp->m_sb);
784 xfs_sb_version_addattr2(&mp->m_sb);
785 version = mp->m_sb.sb_versionnum;
786 features = mp->m_sb.sb_features2;
787 } else if (!strcasecmp(argv[1], "projid32bit")) {
788 xfs_sb_version_addprojid32bit(&mp->m_sb);
789 version = mp->m_sb.sb_versionnum;
790 features = mp->m_sb.sb_features2;
791 } else {
792 dbprintf(_("%s: invalid version change command \"%s\"\n"),
793 progname, argv[1]);
794 return 0;
795 }
796
797 if (version) {
798 dbprintf(_("writing all SBs\n"));
799 for (ag = 0; ag < mp->m_sb.sb_agcount; ag++)
800 if (!do_version(ag, version, features)) {
801 dbprintf(_("failed to set versionnum "
802 "in AG %d\n"), ag);
803 break;
804 }
805 mp->m_sb.sb_versionnum = version;
806 mp->m_sb.sb_features2 = features;
807 }
808 }
809
810 if (argc == 3) { /* VERSIONNUM + FEATURES2 */
811 char *sp;
812
813 version = mp->m_sb.sb_versionnum;
814 features = mp->m_sb.sb_features2;
815 mp->m_sb.sb_versionnum = strtoul(argv[1], &sp, 0);
816 mp->m_sb.sb_features2 = strtoul(argv[2], &sp, 0);
817 }
818
819 dbprintf(_("versionnum [0x%x+0x%x] = %s\n"), mp->m_sb.sb_versionnum,
820 mp->m_sb.sb_features2, version_string(&mp->m_sb));
821
822 if (argc == 3) { /* now reset... */
823 mp->m_sb.sb_versionnum = version;
824 mp->m_sb.sb_features2 = features;
825 return 0;
826 }
827
828 return 0;
829 }