From 39c9a9613396d4bcdbf75d88726bdd980f75c822 Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 04 2020 09:57:38 +0000 Subject: Apply patch bz1833141-1-gfs2_jadd_Handle_out_of_space_issues.patch patch_name: bz1833141-1-gfs2_jadd_Handle_out_of_space_issues.patch present_in_specfile: true --- diff --git a/gfs2/mkfs/main_jadd.c b/gfs2/mkfs/main_jadd.c index efe91e3..c542480 100644 --- a/gfs2/mkfs/main_jadd.c +++ b/gfs2/mkfs/main_jadd.c @@ -396,6 +396,8 @@ static void gather_info(struct gfs2_sbd *sdp, struct jadd_opts *opts) exit(EXIT_FAILURE); } sdp->bsize = statbuf.f_bsize; + sdp->blks_total = statbuf.f_blocks; + sdp->blks_alloced = sdp->blks_total - statbuf.f_bfree; } static void find_current_journals(struct jadd_opts *opts) @@ -527,13 +529,43 @@ static void add_j(struct gfs2_sbd *sdp, struct jadd_opts *opts) } } +static int check_fit(struct gfs2_sbd *sdp, struct jadd_opts *opts) +{ + /* Compute how much space we'll need for the new journals + * Number of blocks needed per added journal: + * 1 block for the ir inode + * 1 block for the sc inode + * for sizes of the qc and journal inodes, use lgfs2_space_for_data() + * to calculate. + */ + uint64_t blks_per_j, total_blks; + + blks_per_j = 1 + 1 + + lgfs2_space_for_data(sdp, sdp->bsize, sdp->qcsize << 20) + + lgfs2_space_for_data(sdp, sdp->bsize, sdp->jsize << 20); + total_blks = opts->journals * blks_per_j; + + if (total_blks > (sdp->blks_total - sdp->blks_alloced)) { + printf( _("\nInsufficient space on the device to add %u %uMB " + "journals (%uMB QC size)\n\n"), + opts->journals, sdp->jsize, sdp->qcsize); + printf( _("Required space : %*lu blks (%lu blks per " + "journal)\n"), 10, total_blks, blks_per_j); + printf( _("Available space : %*lu blks\n\n"), 10, + sdp->blks_total - sdp->blks_alloced); + errno = ENOSPC; + return 1; + } + return 0; +} + int main(int argc, char *argv[]) { struct jadd_opts opts = {0}; struct gfs2_sbd sbd, *sdp = &sbd; struct metafs mfs = {0}; struct mntent *mnt; - unsigned int total; + unsigned int total, ret = 0; setlocale(LC_ALL, ""); textdomain("gfs2-utils"); @@ -574,6 +606,12 @@ int main(int argc, char *argv[]) } find_current_journals(&opts); + ret = check_fit(sdp, &opts); + if (ret) { + perror(_("Failed to add journals")); + goto out; + } + total = opts.orig_journals + opts.journals; for (opts.journals = opts.orig_journals; opts.journals < total; @@ -588,13 +626,16 @@ int main(int argc, char *argv[]) add_j(sdp, &opts); } +out: free(opts.new_inode); free(opts.per_node); free(opts.jindex); close(sdp->path_fd); cleanup_metafs(&mfs); sync(); - print_results(&opts); - return 0; + if (!ret) + print_results(&opts); + + return ret; } diff --git a/gfs2/mkfs/main_jadd.c.bz1833141-1-gfs2_jadd_Handle_out_of_space_issues b/gfs2/mkfs/main_jadd.c.bz1833141-1-gfs2_jadd_Handle_out_of_space_issues new file mode 100644 index 0000000..efe91e3 --- /dev/null +++ b/gfs2/mkfs/main_jadd.c.bz1833141-1-gfs2_jadd_Handle_out_of_space_issues @@ -0,0 +1,600 @@ +#include "clusterautoconfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define _(String) gettext(String) + +#include +#include +#include +#include "libgfs2.h" +#include "gfs2_mkfs.h" +#include "metafs.h" + +#define RANDOM(values) ((values) * (random() / (RAND_MAX + 1.0))) + +struct jadd_opts { + char *path; + char *new_inode; + char *per_node; + char *jindex; + unsigned orig_journals; + unsigned journals; + unsigned quiet:1; + unsigned debug:1; +}; + +#define JA_FL_SET 0 +#define JA_FL_CLEAR 1 +static void set_flags(int fd, int op, uint32_t flags) +{ + int err; + uint32_t val; + + err = ioctl(fd, FS_IOC_GETFLAGS, &val); + if (err) { + perror("GETFLAGS"); + exit(EXIT_FAILURE); + } + + if (op == JA_FL_SET) + val |= flags; + else if (op == JA_FL_CLEAR) + val &= ~flags; + + err = ioctl(fd, FS_IOC_SETFLAGS, &val); + if (err) { + perror("SETFLAGS"); + exit(EXIT_FAILURE); + } +} + +static int rename2system(struct jadd_opts *opts, const char *new_dir, const char *new_name) +{ + char *newpath; + int ret; + + if (asprintf(&newpath, "%s/%s", new_dir, new_name) < 0) { + perror(_("Failed to allocate new path")); + return -1; + } + + ret = rename(opts->new_inode, newpath); + free(newpath); + return ret; +} + +static int build_paths(const char *metafs_path, struct jadd_opts *opts) +{ + int i = 0; + struct { + char **path; + const char *tail; + } p[] = { + { &opts->new_inode, "new_inode" }, + { &opts->per_node, "per_node" }, + { &opts->jindex, "jindex" }, + { NULL, NULL} + }; + + while (p[i].path != NULL) { + if (asprintf(p[i].path, "%s/%s", metafs_path, p[i].tail) < 0) { + while (i > 0) + free(*p[--i].path); + return 1; + } + if (opts->debug) + printf("%d: %s\n", i, *p[i].path); + i++; + } + return 0; +} + +/** + * print_usage - print out usage information + * @prog_name: The name of this program + */ + +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. gfs2_jadd -j /dev/sda */ + "-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"), + "-q", NULL, _("Don't print anything"), + "-V", NULL, _("Display version information, then exit"), + NULL, NULL, NULL /* Must be kept at the end */ + }; + + printf("%s\n", _("Usage:")); + printf("%s [%s] <%s>\n\n", prog_name, _("options"), _("device")); + printf(_("Adds journals to a GFS2 file system.")); + 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 %-15s %s\n", option, param ? param : "", desc); + } +} + +/** + * decode_arguments - decode command line arguments and fill in the struct gfs2_sbd + * @argc: + * @argv: + * @sdp: the decoded command line arguments + * + */ + +static void decode_arguments(int argc, char *argv[], struct gfs2_sbd *sdp, struct jadd_opts *opts) +{ + int cont = TRUE; + int optchar; + + while (cont) { + optchar = getopt(argc, argv, "c:DhJ:j:qu:V"); + + switch (optchar) { + case 'c': + sdp->qcsize = atoi(optarg); + break; + case 'D': + opts->debug = 1; + lgfs2_set_debug(1); + break; + case 'h': + print_usage(argv[0]); + exit(0); + break; + case 'J': + sdp->jsize = atoi(optarg); + break; + case 'j': + opts->journals = atoi(optarg); + break; + case 'q': + opts->quiet = 1; + break; + case 'V': + printf("gfs2_jadd %s (built %s %s)\n", VERSION, + __DATE__, __TIME__); + printf(REDHAT_COPYRIGHT "\n"); + exit(0); + break; + case ':': + case '?': + fprintf(stderr, _("Please use '-h' for help.\n")); + exit(EXIT_FAILURE); + break; + case EOF: + cont = FALSE; + break; + default: + die( _("Invalid option: %c\n"), optchar); + break; + }; + } + + if (optind < argc) { + opts->path = argv[optind]; + optind++; + } else + die( _("no path specified (try -h for help)\n")); + + if (optind < argc) + die( _("Unrecognized argument: %s\n"), argv[optind]); + + if (opts->debug) { + printf( _("Command line arguments:\n")); + printf(" qcsize = %u\n", sdp->qcsize); + printf(" jsize = %u\n", sdp->jsize); + printf(" journals = %u\n", sdp->md.journals); + printf(" quiet = %u\n", opts->quiet); + printf(" path = %s\n", opts->path); + } +} + +static void verify_arguments(struct gfs2_sbd *sdp, struct jadd_opts *opts) +{ + if (!opts->journals) + die( _("no journals specified\n")); + if (sdp->jsize < 32 || sdp->jsize > 1024) + die( _("bad journal size\n")); + if (!sdp->qcsize || sdp->qcsize > 64) + die( _("bad quota change size\n")); +} + +static void print_results(struct jadd_opts *opts) +{ + if (opts->debug) + printf("\n"); + else if (opts->quiet) + return; + + printf( _("Filesystem: %s\n"), opts->path); + printf( _("Old journals: %u\n"), opts->orig_journals); + printf( _("New journals: %u\n"), opts->journals); +} + +static int create_new_inode(struct jadd_opts *opts, uint64_t *addr) +{ + char *name = opts->new_inode; + int fd; + int error; + + for (;;) { + fd = open(name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, 0600); + if (fd >= 0) + break; + if (errno == EEXIST) { + error = unlink(name); + if (error){ + perror("unlink"); + exit(EXIT_FAILURE); + } + } else{ + perror("create"); + exit(EXIT_FAILURE); + } + } + if (addr != NULL) { + struct stat st; + + fstat(fd, &st); + *addr = st.st_ino; + } + + return fd; +} + +static void add_ir(struct jadd_opts *opts) +{ + int fd; + char new_name[256]; + int error; + + fd = create_new_inode(opts, NULL); + + { + struct gfs2_inum_range ir; + + set_flags(fd, JA_FL_SET, FS_JOURNAL_DATA_FL); + memset(&ir, 0, sizeof(struct gfs2_inum_range)); + if (write(fd, (void*)&ir, sizeof(struct gfs2_inum_range)) != + sizeof(struct gfs2_inum_range)) { + perror("add_ir"); + exit(EXIT_FAILURE); + } + } + + close(fd); + + sprintf(new_name, "inum_range%u", opts->journals); + error = rename2system(opts, opts->per_node, new_name); + if (error < 0 && errno != EEXIST){ + perror("add_ir rename2system"); + exit(EXIT_FAILURE); + } +} + +static void add_sc(struct jadd_opts *opts) +{ + int fd; + char new_name[256]; + int error; + + fd = create_new_inode(opts, NULL); + + { + struct gfs2_statfs_change sc; + set_flags(fd, JA_FL_SET, FS_JOURNAL_DATA_FL); + + memset(&sc, 0, sizeof(struct gfs2_statfs_change)); + if (write(fd, (void*)&sc, sizeof(struct gfs2_statfs_change)) != + sizeof(struct gfs2_statfs_change)) { + perror("add_sc"); + exit(EXIT_FAILURE); + } + } + + close(fd); + + sprintf(new_name, "statfs_change%u", opts->journals); + error = rename2system(opts, opts->per_node, new_name); + if (error < 0 && errno != EEXIST){ + perror("add_sc rename2system"); + exit(EXIT_FAILURE); + } +} + +static void add_qc(struct gfs2_sbd *sdp, struct jadd_opts *opts) +{ + int fd; + char new_name[256]; + int error; + + fd = create_new_inode(opts, NULL); + + { + char buf[sdp->bsize]; + unsigned int blocks = + sdp->qcsize << (20 - sdp->sd_sb.sb_bsize_shift); + unsigned int x; + struct gfs2_meta_header mh; + + set_flags(fd, JA_FL_CLEAR, FS_JOURNAL_DATA_FL); + memset(buf, 0, sdp->bsize); + + for (x=0; xbsize) != sdp->bsize) { + perror("add_qc"); + exit(EXIT_FAILURE); + } + } + + lseek(fd, 0, SEEK_SET); + + memset(&mh, 0, sizeof(struct gfs2_meta_header)); + mh.mh_magic = GFS2_MAGIC; + mh.mh_type = GFS2_METATYPE_QC; + mh.mh_format = GFS2_FORMAT_QC; + gfs2_meta_header_out(&mh, buf); + + for (x=0; xbsize) != sdp->bsize) { + perror("add_qc"); + exit(EXIT_FAILURE); + } + } + + error = fsync(fd); + if (error){ + perror("add_qc fsync"); + exit(EXIT_FAILURE); + } + } + + close(fd); + + sprintf(new_name, "quota_change%u", opts->journals); + error = rename2system(opts, opts->per_node, new_name); + if (error < 0 && errno != EEXIST){ + perror("add_qc rename2system"); + exit(EXIT_FAILURE); + } +} + +static void gather_info(struct gfs2_sbd *sdp, struct jadd_opts *opts) +{ + struct statfs statbuf; + if (statfs(opts->path, &statbuf) < 0) { + perror(opts->path); + exit(EXIT_FAILURE); + } + sdp->bsize = statbuf.f_bsize; +} + +static void find_current_journals(struct jadd_opts *opts) +{ + struct dirent *dp; + DIR *dirp; + unsigned existing_journals = 0; + + dirp = opendir(opts->jindex); + if (!dirp) { + perror("jindex"); + exit(EXIT_FAILURE); + } + while (dirp) { + if ((dp = readdir(dirp)) != NULL) { + if (strncmp(dp->d_name, "journal", 7) == 0) + existing_journals++; + } else + goto close; + } +close: + closedir(dirp); + if (existing_journals == 0) { + die( _("No journals found. Did you run mkfs.gfs2 correctly?\n")); + } + + opts->orig_journals = existing_journals; +} + +#ifdef GFS2_HAS_LH_V2 +static uint64_t find_block_address(int fd, off_t offset, unsigned bsize) +{ + struct { + struct fiemap fm; + struct fiemap_extent fe; + } fme; + int ret; + + fme.fm.fm_start = offset; + fme.fm.fm_length = 1; + fme.fm.fm_flags = FIEMAP_FLAG_SYNC; + fme.fm.fm_extent_count = 1; + + ret = ioctl(fd, FS_IOC_FIEMAP, &fme.fm); + if (ret != 0 || fme.fm.fm_mapped_extents != 1) { + fprintf(stderr, "Failed to find log header block address\n"); + return 0; + } + return fme.fe.fe_physical / bsize; +} +#endif + +static void add_j(struct gfs2_sbd *sdp, struct jadd_opts *opts) +{ + int fd; + char new_name[256]; + int error; + uint64_t addr; + + fd = create_new_inode(opts, &addr); + + { + char buf[sdp->bsize]; + unsigned int blocks = + sdp->jsize << (20 - sdp->sd_sb.sb_bsize_shift); + unsigned int x; + struct gfs2_log_header lh; + uint64_t seq = RANDOM(blocks); + off_t off = 0; + + set_flags(fd, JA_FL_CLEAR, FS_JOURNAL_DATA_FL); + memset(buf, 0, sdp->bsize); + for (x=0; xbsize) != sdp->bsize) { + perror("add_j"); + exit(EXIT_FAILURE); + } + } + + lseek(fd, 0, SEEK_SET); + + memset(&lh, 0, sizeof(struct gfs2_log_header)); + lh.lh_header.mh_magic = GFS2_MAGIC; + lh.lh_header.mh_type = GFS2_METATYPE_LH; + lh.lh_header.mh_format = GFS2_FORMAT_LH; + lh.lh_flags = GFS2_LOG_HEAD_UNMOUNT; +#ifdef GFS2_HAS_LH_V2 + lh.lh_flags |= GFS2_LOG_HEAD_USERSPACE; + lh.lh_jinode = addr; +#endif + for (x=0; xlh_hash = cpu_to_be32(hash); +#ifdef GFS2_HAS_LH_V2 + ((struct gfs2_log_header *)buf)->lh_addr = cpu_to_be64( + find_block_address(fd, off, sdp->bsize)); + hash = lgfs2_log_header_crc(buf, sdp->bsize); + ((struct gfs2_log_header *)buf)->lh_crc = cpu_to_be32(hash); +#endif + if (write(fd, buf, sdp->bsize) != sdp->bsize) { + perror("add_j"); + exit(EXIT_FAILURE); + } + + if (++seq == blocks) + seq = 0; + off += sdp->bsize; + } + + error = fsync(fd); + if (error){ + perror("add_j fsync"); + exit(EXIT_FAILURE); + } + } + + close(fd); + + sprintf(new_name, "journal%u", opts->journals); + error = rename2system(opts, opts->jindex, new_name); + if (error < 0 && errno != EEXIST){ + perror("add_j rename2system"); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char *argv[]) +{ + struct jadd_opts opts = {0}; + struct gfs2_sbd sbd, *sdp = &sbd; + struct metafs mfs = {0}; + struct mntent *mnt; + unsigned int total; + + setlocale(LC_ALL, ""); + textdomain("gfs2-utils"); + srandom(time(NULL) ^ getpid()); + + memset(sdp, 0, sizeof(struct gfs2_sbd)); + sdp->jsize = GFS2_DEFAULT_JSIZE; + sdp->qcsize = GFS2_DEFAULT_QCSIZE; + opts.journals = 1; + + decode_arguments(argc, argv, sdp, &opts); + verify_arguments(sdp, &opts); + + sbd.path_fd = lgfs2_open_mnt_dir(opts.path, O_RDONLY|O_CLOEXEC, &mnt); + if (sbd.path_fd < 0) { + fprintf(stderr, _("Error looking up mount '%s': %s\n"), opts.path, strerror(errno)); + exit(EXIT_FAILURE); + } + if (mnt == NULL) { + fprintf(stderr, _("%s: not a mounted gfs2 file system\n"), opts.path); + exit(EXIT_FAILURE); + } + gather_info(sdp, &opts); + mfs.context = copy_context_opt(mnt); + if (mount_gfs2_meta(&mfs, mnt->mnt_dir, opts.debug)) { + perror("GFS2 metafs"); + exit(EXIT_FAILURE); + } + + if (build_paths(mfs.path, &opts)) { + perror(_("Failed to build paths")); + exit(EXIT_FAILURE); + } + + if (compute_constants(sdp)) { + perror(_("Failed to compute file system constants")); + exit(EXIT_FAILURE); + } + find_current_journals(&opts); + + total = opts.orig_journals + opts.journals; + for (opts.journals = opts.orig_journals; + opts.journals < total; + opts.journals++) { + if (metafs_interrupted) { + cleanup_metafs(&mfs); + exit(130); + } + add_ir(&opts); + add_sc(&opts); + add_qc(sdp, &opts); + add_j(sdp, &opts); + } + + free(opts.new_inode); + free(opts.per_node); + free(opts.jindex); + close(sdp->path_fd); + cleanup_metafs(&mfs); + sync(); + print_results(&opts); + + return 0; +}