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