#include "clusterautoconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _(String) gettext(String) #include #include "libgfs2.h" #include "gfs2_mkfs.h" #include "progress.h" #ifdef GFS2_HAS_UUID #include #endif static void print_usage(const char *prog_name) { int i; const char *option, *param, *desc; const char *options[] = { /* Translators: This is a usage string printed with --help. and here are the commandline parameters, e.g. mkfs.gfs2 -b -j /dev/sda */ "-b", _(""), _("File system block size, in bytes"), "-c", _(""), _("Size of quota change file, in megabytes"), "-D", NULL, _("Enable debugging code"), "-h", NULL, _("Display this help, then exit"), "-J", _(""), _("Size of journals, in megabytes"), "-j", _(""), _("Number of journals"), "-K", NULL, _("Don't try to discard unused blocks"), "-O", NULL, _("Don't ask for confirmation"), "-o", _("[=][,...]"), _("Specify extended options. See '-o help'."), "-p", _(""), _("Name of the locking protocol"), "-q", NULL, _("Don't print anything"), "-r", _(""), _("Size of resource groups, in megabytes"), "-t", _(""), _("Name of the lock table"), "-V", NULL, _("Display program version information, then exit"), NULL, NULL, NULL /* Must be kept at the end */ }; printf("%s\n", _("Usage:")); printf("%s [%s] <%s> [%s]\n\n", prog_name, _("options"), _("device"), _("size")); printf(_("Create a gfs2 file system on a device. If a size, in blocks, is not " "specified, the whole device will be used.")); printf("\n\n%s\n", _("Options:")); for (i = 0; options[i] != NULL; i += 3) { option = options[i]; param = options[i + 1]; desc = options[i + 2]; printf("%3s %-22s %s\n", option, param ? param : "", desc); } } static void print_ext_opts(void) { int i; const char *options[] = { "help", _("Display this help, then exit"), "swidth=N", _("Specify the stripe width of the device, overriding probed values"), "sunit=N", _("Specify the stripe unit of the device, overriding probed values"), "align=[0|1]", _("Disable or enable alignment of resource groups"), NULL, NULL }; printf(_("Extended options:\n")); for (i = 0; options[i] != NULL; i += 2) { printf("%15s %-22s\n", options[i], options[i + 1]); } } /** * Values probed by libblkid: * alignment_offset: offset, in bytes, of the start of the dev from its natural alignment * logical_sector_size: smallest addressable unit * minimum_io_size: device's preferred unit of I/O. RAID stripe unit. * optimal_io_size: biggest I/O we can submit without incurring a penalty. RAID stripe width. * physical_sector_size: the smallest unit we can write atomically */ struct mkfs_dev { int fd; const char *path; struct stat stat; uint64_t size; unsigned long alignment_offset; unsigned long logical_sector_size; unsigned long minimum_io_size; unsigned long optimal_io_size; unsigned long physical_sector_size; unsigned int got_topol:1; }; struct mkfs_opts { unsigned bsize; unsigned qcsize; unsigned jsize; unsigned rgsize; unsigned long sunit; unsigned long swidth; uint64_t fssize; uint32_t journals; const char *lockproto; const char *locktable; struct mkfs_dev dev; unsigned discard:1; unsigned got_bsize:1; unsigned got_qcsize:1; unsigned got_jsize:1; unsigned got_rgsize:1; unsigned got_sunit:1; unsigned got_swidth:1; unsigned got_fssize:1; unsigned got_journals:1; unsigned got_lockproto:1; unsigned got_locktable:1; unsigned got_device:1; unsigned got_topol:1; unsigned override:1; unsigned quiet:1; unsigned debug:1; unsigned confirm:1; unsigned align:1; }; static void opts_init(struct mkfs_opts *opts) { memset(opts, 0, sizeof(*opts)); opts->discard = 1; opts->journals = 1; opts->bsize = GFS2_DEFAULT_BSIZE; opts->jsize = GFS2_DEFAULT_JSIZE; opts->qcsize = GFS2_DEFAULT_QCSIZE; opts->rgsize = GFS2_DEFAULT_RGSIZE; opts->lockproto = "lock_dlm"; opts->locktable = ""; opts->confirm = 1; opts->align = 1; } struct gfs2_inum *mkfs_journals = NULL; #ifndef BLKDISCARD #define BLKDISCARD _IO(0x12,119) #endif static int discard_blocks(int fd, uint64_t len, int debug) { __uint64_t range[2]; range[0] = 0; range[1] = len; if (debug) /* Translators: "discard" is a request sent to a storage device to * discard a range of blocks. */ printf(_("Issuing discard request: range: %llu - %llu..."), (unsigned long long)range[0], (unsigned long long)range[1]); if (ioctl(fd, BLKDISCARD, &range) < 0) { if (debug) printf("%s = %d\n", _("error"), errno); return errno; } if (debug) printf(_("Successful.\n")); return 0; } /** * Convert a human-readable size string to a long long. * Copied and adapted from xfs_mkfs.c. */ static long long cvtnum(unsigned int blocksize, unsigned int sectorsize, const char *s) { long long i; char *sp; i = strtoll(s, &sp, 0); if (i == 0 && sp == s) return -1LL; if (*sp == '\0') return i; *sp = tolower(*sp); if (*sp == 'b' && sp[1] == '\0') { if (blocksize) return i * blocksize; fprintf(stderr, _("Block size not available yet.\n")); exit(1); } if (*sp == 's' && sp[1] == '\0') { if (sectorsize) return i * sectorsize; return i * GFS2_BASIC_BLOCK; } if (*sp == 'k' && sp[1] == '\0') return 1024LL * i; if (*sp == 'm' && sp[1] == '\0') return 1024LL * 1024LL * i; if (*sp == 'g' && sp[1] == '\0') return 1024LL * 1024LL * 1024LL * i; if (*sp == 't' && sp[1] == '\0') return 1024LL * 1024LL * 1024LL * 1024LL * i; if (*sp == 'p' && sp[1] == '\0') return 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * i; if (*sp == 'e' && sp[1] == '\0') return 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * i; return -1LL; } static unsigned long parse_ulong(struct mkfs_opts *opts, const char *key, const char *val) { long long l; if (val == NULL || *val == '\0') { fprintf(stderr, _("Missing argument to '%s'\n"), key); exit(-1); } l = cvtnum(opts->bsize, 0, val); if (l > ULONG_MAX || l < 0) { fprintf(stderr, _("Value of '%s' is invalid\n"), key); exit(-1); } return (unsigned long)l; } static unsigned parse_bool(struct mkfs_opts *opts, const char *key, const char *val) { if (strnlen(val, 2) == 1) { if (*val == '0') return 0; if (*val == '1') return 1; } fprintf(stderr, _("Option '%s' must be either 1 or 0\n"), key); exit(-1); } static int parse_topology(struct mkfs_opts *opts, char *str) { char *opt; unsigned i = 0; unsigned long *topol[5] = { &opts->dev.alignment_offset, &opts->dev.logical_sector_size, &opts->dev.minimum_io_size, &opts->dev.optimal_io_size, &opts->dev.physical_sector_size }; while ((opt = strsep(&str, ":")) != NULL) { if (i > 4) { fprintf(stderr, "Too many topology values.\n"); return 1; } *topol[i] = parse_ulong(opts, "test_topology", opt); i++; } if (i < 5) { fprintf(stderr, "Too few topology values.\n"); return 1; } return 0; } static void opt_parse_extended(char *str, struct mkfs_opts *opts) { char *opt; while ((opt = strsep(&str, ",")) != NULL) { char *key = strsep(&opt, "="); char *val = strsep(&opt, "="); if (key == NULL || *key == '\0') { fprintf(stderr, _("Missing argument to '-o' option\n")); exit(-1); } if (strcmp("sunit", key) == 0) { opts->sunit = parse_ulong(opts, "sunit", val); opts->got_sunit = 1; } else if (strcmp("swidth", key) == 0) { opts->swidth = parse_ulong(opts, "swidth", val); opts->got_swidth = 1; } else if (strcmp("align", key) == 0) { opts->align = parse_bool(opts, "align", val); } else if (strcmp("test_topology", key) == 0) { if (parse_topology(opts, val) != 0) exit(-1); opts->got_topol = (opts->dev.logical_sector_size != 0 && opts->dev.physical_sector_size != 0); } else if (strcmp("help", key) == 0) { print_ext_opts(); exit(0); } else { fprintf(stderr, _("Invalid option '%s'\n"), key); exit(-1); } } } static void opts_get(int argc, char *argv[], struct mkfs_opts *opts) { int c; while (1) { c = getopt(argc, argv, "-b:c:DhJ:j:KOo:p:qr:t:V"); if (c == -1) break; switch (c) { case 'b': opts->bsize = atoi(optarg); opts->got_bsize = 1; break; case 'c': opts->qcsize = atoi(optarg); opts->got_qcsize = 1; break; case 'D': opts->debug = 1; lgfs2_set_debug(1); break; case 'h': print_usage(argv[0]); exit(0); case 'J': opts->jsize = atoi(optarg); opts->got_jsize = 1; break; case 'j': opts->journals = atoi(optarg); opts->got_journals = 1; break; case 'K': opts->discard = 0; break; case 'O': opts->override = 1; break; case 'p': opts->lockproto = optarg; opts->got_lockproto = 1; break; case 't': opts->locktable = optarg; opts->got_locktable = 1; break; case 'q': opts->quiet = 1; break; case 'r': opts->rgsize = atoi(optarg); opts->got_rgsize = 1; break; case 'o': opt_parse_extended(optarg, opts); break; case 'V': printf("mkfs.gfs2 %s (built %s %s)\n", VERSION, __DATE__, __TIME__); printf(REDHAT_COPYRIGHT "\n"); exit(EXIT_SUCCESS); break; case ':': case '?': fprintf(stderr, _("Please use '-h' for help.\n")); exit(EXIT_FAILURE); break; case 1: if (strcmp(optarg, "gfs2") == 0) continue; if (!opts->got_device) { opts->dev.path = optarg; opts->got_device = 1; } else if (!opts->got_fssize && isdigit(optarg[0])) { opts->fssize = atol(optarg); opts->got_fssize = 1; } else die( _("More than one device specified (try -h for help)\n")); break; default: die( _("Invalid option: %c\n"), c); break; }; } } /** * test_locking - Make sure the GFS2 is set up to use the right lock protocol * @lockproto: the lock protocol to mount * @locktable: the locktable name * */ static void test_locking(struct mkfs_opts *opts) { const char *c; /* Translators: A lock table is a string identifying a gfs2 file system * in a cluster, e.g. cluster_name:fs_name */ const char *errprefix = _("Invalid lock table:"); int table_required = (strcmp(opts->lockproto, "lock_gulm") == 0) || (strcmp(opts->lockproto, "lock_dlm") == 0); if ((strcmp(opts->lockproto, "lock_nolock") != 0) && !table_required) die( _("Invalid lock protocol: %s\n"), opts->lockproto); /* When lock_*lm is given as the lock protocol, require a lock table too */ if (!opts->got_locktable) { if (table_required) { fprintf(stderr, _("No lock table specified.\n")); exit(-1); } return; } /* User gave a lock table option, validate it */ if (*opts->locktable == '\0') { fprintf(stderr, _("Lock table is empty.\n")); exit(-1); } for (c = opts->locktable; *c; c++) { if (!isalnum(*c) && (*c != '-') && (*c != '_') && (*c != ':')) die("%s %s '%c'\n", errprefix, _("invalid character"), *c); } c = strstr(opts->locktable, ":"); if (!c) die("%s %s\n", errprefix, _("missing colon")); if (c == opts->locktable) die("%s %s\n", errprefix, _("cluster name is missing")); if (c - opts->locktable > 32) die("%s %s\n", errprefix, _("cluster name is too long")); c++; if (strstr(c, ":")) die("%s %s\n", errprefix, _("contains more than one colon")); if (!strlen(c)) die("%s %s\n", errprefix, _("file system name is missing")); if (strlen(c) > 30) die("%s %s\n", errprefix, _("file system name is too long")); } static void are_you_sure(void) { while (1) { char *line = NULL; size_t len = 0; int ret; int res; /* Translators: We use rpmatch(3) to match the answers to y/n questions in the user's own language, so the [y/n] here must also be translated to match one of the letters in the pattern printed by `locale -k yesexpr` and one of the letters in the pattern printed by `locale -k noexpr` */ printf( _("Are you sure you want to proceed? [y/n] ")); ret = getline(&line, &len, stdin); res = rpmatch(line); free(line); if (ret <= 0) continue; if (res == 1) /* Yes */ return; if (res == 0) /* No */ exit(1); /* Unrecognized input; go again. */ }; } static unsigned choose_blocksize(struct mkfs_opts *opts) { unsigned int x; unsigned int bsize = opts->bsize; struct mkfs_dev *dev = &opts->dev; int got_topol = (dev->got_topol || opts->got_topol); if (got_topol && opts->debug) { printf("alignment_offset: %lu\n", dev->alignment_offset); printf("logical_sector_size: %lu\n", dev->logical_sector_size); printf("minimum_io_size: %lu\n", dev->minimum_io_size); printf("optimal_io_size: %lu\n", dev->optimal_io_size); printf("physical_sector_size: %lu\n", dev->physical_sector_size); } if (got_topol && dev->alignment_offset != 0) { fprintf(stderr, _("Warning: device is not properly aligned. This may harm performance.\n")); dev->physical_sector_size = dev->logical_sector_size; } if (!opts->got_bsize && got_topol) { if (dev->optimal_io_size <= getpagesize() && dev->optimal_io_size >= GFS2_DEFAULT_BSIZE) bsize = dev->optimal_io_size; else if (dev->physical_sector_size <= getpagesize() && dev->physical_sector_size >= GFS2_DEFAULT_BSIZE) bsize = dev->physical_sector_size; } /* Block sizes must be a power of two from 512 to 65536 */ for (x = 512; x; x <<= 1) if (x == bsize) break; if (!x || bsize > getpagesize()) die( _("Block size must be a power of two between 512 and %d\n"), getpagesize()); if (bsize < dev->logical_sector_size) { die( _("Error: Block size %d is less than minimum logical " "block size (%lu).\n"), bsize, dev->logical_sector_size); } if (bsize < dev->physical_sector_size) { printf( _("Warning: Block size %d is inefficient because it " "is less than the physical block size (%lu).\n"), bsize, dev->physical_sector_size); opts->confirm = 1; } return bsize; } static void opts_check(struct mkfs_opts *opts) { if (!opts->got_device) { fprintf(stderr, _("No device specified. Use -h for help\n")); exit(1); } test_locking(opts); if (GFS2_MIN_RGSIZE > opts->rgsize || opts->rgsize > GFS2_MAX_RGSIZE) /* Translators: gfs2 file systems are split into equal sized chunks called resource groups. We're checking that the user gave a valid size for them. */ die( _("bad resource group size\n")); if (!opts->journals) die( _("no journals specified\n")); if (opts->jsize < GFS2_MIN_JSIZE || opts->jsize > GFS2_MAX_JSIZE) die( _("bad journal size\n")); if (!opts->qcsize || opts->qcsize > 64) die( _("bad quota change size\n")); if ((opts->got_sunit && !opts->got_swidth) || (!opts->got_sunit && opts->got_swidth)) { fprintf(stderr, _("Stripe unit and stripe width must be specified together\n")); exit(1); } } static void print_results(struct gfs2_sb *sb, struct mkfs_opts *opts, uint64_t rgrps, uint64_t fssize) { printf("%-27s%s\n", _("Device:"), opts->dev.path); printf("%-27s%u\n", _("Block size:"), sb->sb_bsize); printf("%-27s%.2f %s (%"PRIu64" %s)\n", _("Device size:"), /* Translators: "GB" here means "gigabytes" */ (opts->dev.size / ((float)(1 << 30))), _("GB"), (opts->dev.size / sb->sb_bsize), _("blocks")); printf("%-27s%.2f %s (%"PRIu64" %s)\n", _("Filesystem size:"), (fssize / ((float)(1 << 30)) * sb->sb_bsize), _("GB"), fssize, _("blocks")); printf("%-27s%u\n", _("Journals:"), opts->journals); printf("%-27s%uMB\n", _("Journal size:"), opts->jsize); printf("%-27s%"PRIu64"\n", _("Resource groups:"), rgrps); printf("%-27s\"%s\"\n", _("Locking protocol:"), opts->lockproto); printf("%-27s\"%s\"\n", _("Lock table:"), opts->locktable); #ifdef GFS2_HAS_UUID { char readable_uuid[36+1]; uuid_unparse(sb->sb_uuid, readable_uuid); /* Translators: "UUID" = universally unique identifier. */ printf("%-27s%s\n", _("UUID:"), readable_uuid); } #endif } static void warn_of_destruction(const char *path) { struct stat lnkstat; char *abspath = NULL; if (lstat(path, &lnkstat) == -1) { perror(_("Failed to lstat the device")); exit(EXIT_FAILURE); } if (S_ISLNK(lnkstat.st_mode)) { abspath = canonicalize_file_name(path); if (abspath == NULL) { perror(_("Could not find the absolute path of the device")); exit(EXIT_FAILURE); } /* Translators: Example: "/dev/vg/lv is a symbolic link to /dev/dm-2" */ printf( _("%s is a symbolic link to %s\n"), path, abspath); path = abspath; } printf(_("This will destroy any data on %s\n"), path); free(abspath); } static int zero_gap(struct gfs2_sbd *sdp, uint64_t addr, size_t blocks) { struct iovec *iov; char *zerobuf; ssize_t wrote; unsigned i; if (blocks == 0) return 0; iov = calloc(blocks, sizeof(*iov)); if (iov == NULL) { perror(_("Failed to zero blocks\n")); return 1; } zerobuf = calloc(1, sdp->bsize); if (zerobuf == NULL) { perror(_("Failed to zero blocks\n")); free(iov); return 1; } for (i = 0; i < blocks; i++) { iov[i].iov_base = zerobuf; iov[i].iov_len = sdp->bsize; } wrote = pwritev(sdp->device_fd, iov, blocks, addr * sdp->bsize); if (wrote != blocks * sdp->bsize) { fprintf(stderr, _("Zeroing write failed at block %"PRIu64"\n"), addr); free(zerobuf); free(iov); return 1; } free(zerobuf); free(iov); return 0; } static lgfs2_rgrps_t rgs_init(struct mkfs_opts *opts, struct gfs2_sbd *sdp) { lgfs2_rgrps_t rgs; uint64_t al_base = 0; uint64_t al_off = 0; if (opts->align && opts->got_sunit) { if ((opts->sunit % sdp->bsize) != 0) { fprintf(stderr, _("Stripe unit (%lu) must be a multiple of block size (%u)\n"), opts->sunit, sdp->bsize); exit(1); } else if ((opts->swidth % opts->sunit) != 0) { fprintf(stderr, _("Stripe width (%lu) must be a multiple of stripe unit (%lu)\n"), opts->swidth, opts->sunit); exit(1); } else { al_base = opts->swidth / sdp->bsize; al_off = opts->sunit / sdp->bsize; } } else if (opts->align) { if (opts->dev.optimal_io_size <= opts->dev.physical_sector_size || opts->dev.minimum_io_size <= opts->dev.physical_sector_size || (opts->dev.optimal_io_size % opts->dev.minimum_io_size) != 0) { /* If optimal_io_size is not a multiple of minimum_io_size then the values are not reliable swidth and sunit values, so disable rgrp alignment */ opts->align = 0; } else { al_base = opts->dev.optimal_io_size / sdp->bsize; al_off = opts->dev.minimum_io_size / sdp->bsize; } } rgs = lgfs2_rgrps_init(sdp, al_base, al_off); if (rgs == NULL) { perror(_("Could not initialise resource groups")); exit(-1); } if (opts->debug) { printf(" rgrp align = "); if (opts->align) printf("%"PRIu64"+%"PRIu64" blocks\n", al_base, al_off); else printf("(disabled)\n"); } return rgs; } static int place_rgrp(struct gfs2_sbd *sdp, lgfs2_rgrp_t rg, int debug) { uint64_t prev_end = (GFS2_SB_ADDR * GFS2_BASIC_BLOCK / sdp->bsize) + 1; const struct gfs2_rindex *ri = lgfs2_rgrp_index(rg); lgfs2_rgrp_t prev = lgfs2_rgrp_prev(rg); int err = 0; if (prev != NULL) { prev_end = lgfs2_rgrp_index(prev)->ri_data0 + lgfs2_rgrp_index(prev)->ri_data; } err = zero_gap(sdp, prev_end, ri->ri_addr - prev_end); if (err != 0) return -1; err = lgfs2_rgrp_write(sdp->device_fd, rg); if (err != 0) { perror(_("Failed to write resource group")); return -1; } if (debug) { gfs2_rindex_print(ri); printf("\n"); } sdp->blks_total += ri->ri_data; sdp->fssize = ri->ri_data0 + ri->ri_data; sdp->rgrps++; return 0; } static int add_rgrp(lgfs2_rgrps_t rgs, uint64_t *addr, uint32_t len, lgfs2_rgrp_t *rg) { struct gfs2_rindex ri; uint64_t nextaddr; /* When we get to the end of the device, it's only an error if we have more structures left to write, i.e. when len is != 0. */ nextaddr = lgfs2_rindex_entry_new(rgs, &ri, *addr, len); if (nextaddr == 0) { if (len != 0) { perror(_("Failed to create resource group index entry")); return -1; } else { return 1; } } *rg = lgfs2_rgrps_append(rgs, &ri, nextaddr - *addr); if (*rg == NULL) { perror(_("Failed to create resource group")); return -1; } *addr = nextaddr; return 0; } static int place_journals(struct gfs2_sbd *sdp, lgfs2_rgrps_t rgs, struct mkfs_opts *opts, uint64_t *rgaddr) { struct gfs2_progress_bar progress; uint64_t jfsize = lgfs2_space_for_data(sdp, sdp->bsize, opts->jsize << 20); uint32_t rgsize = lgfs2_rgsize_for_data(jfsize, sdp->bsize); unsigned j; gfs2_progress_init(&progress, opts->journals, _("Adding journals: "), opts->quiet); /* We'll build the jindex later so remember where we put the journals */ mkfs_journals = calloc(opts->journals, sizeof(*mkfs_journals)); if (mkfs_journals == NULL) return 1; *rgaddr = lgfs2_rgrp_align_addr(rgs, LGFS2_SB_ADDR(sdp) + 1); rgsize = lgfs2_rgrp_align_len(rgs, rgsize); for (j = 0; j < opts->journals; j++) { int result; lgfs2_rgrp_t rg; struct gfs2_inode in = {0}; gfs2_progress_update(&progress, (j + 1)); if (opts->debug) printf(_("Placing resource group for journal%u\n"), j); result = add_rgrp(rgs, rgaddr, rgsize, &rg); if (result > 0) break; else if (result < 0) return result; result = lgfs2_rgrp_bitbuf_alloc(rg); if (result != 0) { perror(_("Failed to allocate space for bitmap buffer")); return result; } /* Allocate at the beginning of the rgrp, bypassing extent search */ in.i_di.di_num.no_addr = lgfs2_rgrp_index(rg)->ri_data0; /* In order to keep writes sequential here, we have to allocate the journal, then write the rgrp header (which is now in its final form) and then write the journal out */ result = lgfs2_file_alloc(rg, opts->jsize << 20, &in, GFS2_DIF_SYSTEM, S_IFREG | 0600); if (result != 0) { fprintf(stderr, _("Failed to allocate space for journal %u\n"), j); return result; } if (opts->debug) gfs2_dinode_print(&in.i_di); result = place_rgrp(sdp, rg, opts->debug); if (result != 0) return result; lgfs2_rgrp_bitbuf_free(rg); result = lgfs2_write_filemeta(&in); if (result != 0) { fprintf(stderr, _("Failed to write journal %u\n"), j); return result; } result = lgfs2_write_journal_data(&in); if (result != 0) { fprintf(stderr, _("Failed to write data blocks for journal %u\n"), j); return result; } mkfs_journals[j] = in.i_di.di_num; } gfs2_progress_close(&progress, _("Done\n")); return 0; } static int place_rgrps(struct gfs2_sbd *sdp, lgfs2_rgrps_t rgs, uint64_t *rgaddr, struct mkfs_opts *opts) { struct gfs2_progress_bar progress; uint32_t rgblks = ((opts->rgsize << 20) / sdp->bsize); uint32_t rgnum; int result; rgnum = lgfs2_rgrps_plan(rgs, sdp->device.length - *rgaddr, rgblks); gfs2_progress_init(&progress, (rgnum + opts->journals), _("Building resource groups: "), opts->quiet); while (1) { lgfs2_rgrp_t rg; result = add_rgrp(rgs, rgaddr, 0, &rg); if (result > 0) break; else if (result < 0) return result; result = place_rgrp(sdp, rg, opts->debug); if (result != 0) { fprintf(stderr, _("Failed to build resource groups\n")); return result; } gfs2_progress_update(&progress, (sdp->rgrps)); } gfs2_progress_close(&progress, _("Done\n")); posix_fadvise(sdp->device_fd, 0, sdp->fssize * sdp->bsize, POSIX_FADV_DONTNEED); return 0; } /* * Find a reasonable journal file size (in blocks) given the number of blocks * in the filesystem. For very small filesystems, it is not reasonable to * have a journal that fills more than half of the filesystem. * * n.b. comments assume 4k blocks * * This was copied and adapted from e2fsprogs. */ static int default_journal_size(unsigned bsize, uint64_t num_blocks) { int min_blocks = (GFS2_MIN_JSIZE << 20) / bsize; if (num_blocks < 2 * min_blocks) return -1; if (num_blocks < 131072) /* 512 MB */ return min_blocks; /* 8 MB */ if (num_blocks < 512*1024) /* 2 GB */ return (4096); /* 16 MB */ if (num_blocks < 2048*1024) /* 8 GB */ return (8192); /* 32 MB */ if (num_blocks < 4096*1024) /* 16 GB */ return (16384); /* 64 MB */ if (num_blocks < 262144*1024) /* 1 TB */ return (32768); /* 128 MB */ if (num_blocks < 2621440UL*1024) /* 10 TB */ return (131072); /* 512 MB */ return 262144; /* 1 GB */ } static void sbd_init(struct gfs2_sbd *sdp, struct mkfs_opts *opts, unsigned bsize) { memset(sdp, 0, sizeof(struct gfs2_sbd)); sdp->time = time(NULL); sdp->rgtree.osi_node = NULL; sdp->rgsize = opts->rgsize; sdp->qcsize = opts->qcsize; sdp->jsize = opts->jsize; sdp->md.journals = opts->journals; sdp->device_fd = opts->dev.fd; sdp->bsize = sdp->sd_sb.sb_bsize = bsize; if (compute_constants(sdp)) { perror(_("Failed to compute file system constants")); exit(1); } sdp->device.length = opts->dev.size / sdp->bsize; if (opts->got_fssize) { if (opts->fssize > sdp->device.length) { fprintf(stderr, _("Specified size is bigger than the device.")); die("%s %.2f %s (%"PRIu64" %s)\n", _("Device size:"), opts->dev.size / ((float)(1 << 30)), _("GB"), opts->dev.size / sdp->bsize, _("blocks")); } sdp->device.length = opts->fssize; } /* opts->jsize has already been max/min checked but we need to check it makes sense for the device size, or set a sensible default, if one will fit. For user-provided journal sizes, limit it to half of the fs. */ if (!opts->got_jsize) { int default_jsize = default_journal_size(sdp->bsize, sdp->device.length / opts->journals); if (default_jsize < 0) { fprintf(stderr, _("gfs2 will not fit on this device.\n")); exit(1); } opts->jsize = (default_jsize * sdp->bsize) >> 20; } else if ((((opts->jsize * opts->journals) << 20) / sdp->bsize) > (sdp->device.length / 2)) { unsigned max_jsize = (sdp->device.length / 2 * sdp->bsize / opts->journals) >> 20; fprintf(stderr, _("gfs2 will not fit on this device.\n")); if (max_jsize >= GFS2_MIN_JSIZE) fprintf(stderr, _("Maximum size for %u journals on this device is %uMB.\n"), opts->journals, max_jsize); exit(1); } sdp->jsize = opts->jsize; } static int probe_contents(struct mkfs_dev *dev) { int ret; const char *contents; blkid_probe pr = blkid_new_probe(); if (pr == NULL || blkid_probe_set_device(pr, dev->fd, 0, 0) != 0 || blkid_probe_enable_superblocks(pr, TRUE) != 0 || blkid_probe_enable_partitions(pr, TRUE) != 0) { fprintf(stderr, _("Failed to create probe\n")); return -1; } if (!S_ISREG(dev->stat.st_mode) && blkid_probe_enable_topology(pr, TRUE) != 0) { fprintf(stderr, _("Failed to create probe\n")); return -1; } ret = blkid_do_fullprobe(pr); if (ret == -1) { fprintf(stderr, _("Failed to probe device\n")); return -1; } if (ret == 1) return 0; if (!blkid_probe_lookup_value(pr, "TYPE", &contents, NULL)) { printf(_("It appears to contain an existing filesystem (%s)\n"), contents); } else if (!blkid_probe_lookup_value(pr, "PTTYPE", &contents, NULL)) { printf(_("It appears to contain a partition table (%s).\n"), contents); } if (!S_ISREG(dev->stat.st_mode)) { blkid_topology tp = blkid_probe_get_topology(pr); if (tp != NULL) { dev->alignment_offset = blkid_topology_get_alignment_offset(tp); dev->logical_sector_size = blkid_topology_get_logical_sector_size(tp); dev->minimum_io_size = blkid_topology_get_minimum_io_size(tp); dev->optimal_io_size = blkid_topology_get_optimal_io_size(tp); dev->physical_sector_size = blkid_topology_get_physical_sector_size(tp); dev->got_topol = (dev->logical_sector_size != 0 && dev->physical_sector_size != 0); } } blkid_free_probe(pr); return 0; } static void open_dev(struct mkfs_dev *dev, int withprobe) { int error; dev->fd = open(dev->path, O_RDWR|O_CLOEXEC|O_EXCL); if (dev->fd < 0) { perror(dev->path); exit(1); } /* Freshen up the cache */ posix_fadvise(dev->fd, 0, 0, POSIX_FADV_DONTNEED); /* Turn off readahead, we're just writing new blocks */ posix_fadvise(dev->fd, 0, 0, POSIX_FADV_RANDOM); error = fstat(dev->fd, &dev->stat); if (error < 0) { perror(dev->path); exit(1); } if (S_ISREG(dev->stat.st_mode)) { dev->size = dev->stat.st_size; } else if (S_ISBLK(dev->stat.st_mode)) { dev->size = lseek(dev->fd, 0, SEEK_END); if (dev->size < 1) { fprintf(stderr, _("Device '%s' is too small\n"), dev->path); exit(1); } } else { fprintf(stderr, _("'%s' is not a block device or regular file\n"), dev->path); exit(1); } if (withprobe && (probe_contents(dev) != 0)) exit(1); } int main(int argc, char *argv[]) { struct gfs2_sbd sbd; struct gfs2_sb sb; struct mkfs_opts opts; lgfs2_rgrps_t rgs; uint64_t rgaddr; int error; unsigned bsize; setlocale(LC_ALL, ""); textdomain("gfs2-utils"); srandom(time(NULL) ^ getpid()); opts_init(&opts); opts_get(argc, argv, &opts); opts_check(&opts); open_dev(&opts.dev, !opts.got_topol); bsize = choose_blocksize(&opts); if (S_ISREG(opts.dev.stat.st_mode)) { opts.got_bsize = 1; /* Use default block size for regular files */ } sbd_init(&sbd, &opts, bsize); lgfs2_sb_init(&sb, bsize); if (opts.debug) { printf(_("File system options:\n")); printf(" bsize = %u\n", sbd.bsize); printf(" qcsize = %u\n", sbd.qcsize); printf(" jsize = %u\n", sbd.jsize); printf(" journals = %u\n", sbd.md.journals); printf(" proto = %s\n", opts.lockproto); printf(" table = %s\n", opts.locktable); printf(" rgsize = %u\n", sbd.rgsize); printf(" fssize = %"PRIu64"\n", opts.fssize); printf(" sunit = %lu\n", opts.sunit); printf(" swidth = %lu\n", opts.swidth); } rgs = rgs_init(&opts, &sbd); warn_of_destruction(opts.dev.path); if (opts.confirm && !opts.override) are_you_sure(); if (!S_ISREG(opts.dev.stat.st_mode) && opts.discard) { if (!opts.quiet) { printf("%s", _("Discarding device contents (may take a while on large devices): ")); fflush(stdout); } discard_blocks(opts.dev.fd, opts.dev.size, opts.debug); if (!opts.quiet) printf("%s", _("Done\n")); } rgaddr = lgfs2_rgrp_align_addr(rgs, LGFS2_SB_ADDR(&sbd) + 1); error = place_journals(&sbd, rgs, &opts, &rgaddr); if (error != 0) { fprintf(stderr, _("Failed to create journals\n")); exit(1); } error = place_rgrps(&sbd, rgs, &rgaddr, &opts); if (error) { fprintf(stderr, _("Failed to build resource groups\n")); exit(1); } sbd.rgtree.osi_node = lgfs2_rgrps_root(rgs); // Temporary error = build_master(&sbd); if (error) { fprintf(stderr, _("Error building '%s': %s\n"), "master", strerror(errno)); exit(EXIT_FAILURE); } sb.sb_master_dir = sbd.master_dir->i_di.di_num; error = lgfs2_build_jindex(sbd.master_dir, mkfs_journals, opts.journals); if (error) { fprintf(stderr, _("Error building '%s': %s\n"), "jindex", strerror(errno)); exit(EXIT_FAILURE); } free(mkfs_journals); error = build_per_node(&sbd); if (error) { fprintf(stderr, _("Error building '%s': %s\n"), "per_node", strerror(errno)); exit(EXIT_FAILURE); } error = build_inum(&sbd); if (error) { fprintf(stderr, _("Error building '%s': %s\n"), "inum", strerror(errno)); exit(EXIT_FAILURE); } gfs2_lookupi(sbd.master_dir, "inum", 4, &sbd.md.inum); error = build_statfs(&sbd); if (error) { fprintf(stderr, _("Error building '%s': %s\n"), "statfs", strerror(errno)); exit(EXIT_FAILURE); } gfs2_lookupi(sbd.master_dir, "statfs", 6, &sbd.md.statfs); error = build_rindex(&sbd); if (error) { fprintf(stderr, _("Error building '%s': %s\n"), "rindex", strerror(errno)); exit(EXIT_FAILURE); } if (!opts.quiet) { printf("%s", _("Creating quota file: ")); fflush(stdout); } error = build_quota(&sbd); if (error) { fprintf(stderr, _("Error building '%s': %s\n"), "quota", strerror(errno)); exit(EXIT_FAILURE); } if (!opts.quiet) printf("%s", _("Done\n")); build_root(&sbd); sb.sb_root_dir = sbd.md.rooti->i_di.di_num; strncpy(sb.sb_lockproto, opts.lockproto, GFS2_LOCKNAME_LEN); strncpy(sb.sb_locktable, opts.locktable, GFS2_LOCKNAME_LEN); sb.sb_lockproto[GFS2_LOCKNAME_LEN - 1] = '\0'; sb.sb_locktable[GFS2_LOCKNAME_LEN - 1] = '\0'; do_init_inum(&sbd); do_init_statfs(&sbd); inode_put(&sbd.md.rooti); inode_put(&sbd.master_dir); inode_put(&sbd.md.inum); inode_put(&sbd.md.statfs); lgfs2_rgrps_free(&rgs); if (!opts.quiet) { printf("%s", _("Writing superblock and syncing: ")); fflush(stdout); } error = lgfs2_sb_write(&sb, opts.dev.fd, sbd.bsize); if (error) { perror(_("Failed to write superblock\n")); exit(EXIT_FAILURE); } error = fsync(opts.dev.fd); if (error){ perror(opts.dev.path); exit(EXIT_FAILURE); } posix_fadvise(opts.dev.fd, 0, 0, POSIX_FADV_DONTNEED); error = close(opts.dev.fd); if (error){ perror(opts.dev.path); exit(EXIT_FAILURE); } if (!opts.quiet) { printf("%s", _("Done\n")); print_results(&sb, &opts, sbd.rgrps, sbd.fssize); } return 0; }