/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * Copyright by the Board of Trustees of the University of Illinois. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the COPYING file, which can be found at the root of the source code * * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Programmer: Robb Matzke * Wednesday, May 13, 1998 * * Purpose: Repartitions a file family. This program can be used to * split a single file into a family of files, join a family of * files into a single file, or copy one family to another while * changing the size of the family members. It can also be used * to copy a single file to a single file with holes. */ /* See H5private.h for how to include system headers */ #include "hdf5.h" #include "H5private.h" #define NAMELEN 4096 #define GB *1024 * 1024 * 1024 /* Make these 2 private properties(defined in H5Fprivate.h) available to h5repart. * The first one updates the member file size in the superblock. The second one * change file driver from family to a single file driver. */ #define H5F_ACS_FAMILY_NEWSIZE_NAME "family_newsize" #define H5F_ACS_FAMILY_TO_SINGLE_NAME "family_to_single" /*------------------------------------------------------------------------- * Function: usage * * Purpose: Prints a usage message. * * Return: void * * Programmer: Robb Matzke * Wednesday, May 13, 1998 * * Modifications: * *------------------------------------------------------------------------- */ static void usage(const char *progname) { HDfprintf(stderr, "usage: %s [-v] [-V] [-[b|m] N[g|m|k]] [-family_to_sec2|-family_to_single] SRC DST\n", progname); HDfprintf(stderr, " -v Produce verbose output\n"); HDfprintf(stderr, " -V Print a version number and exit\n"); HDfprintf(stderr, " -b N The I/O block size, defaults to 1kB\n"); HDfprintf(stderr, " -m N The destination member size or 1GB\n"); HDfprintf(stderr, " -family_to_sec2 Deprecated version of -family_to_single (below)\n"); HDfprintf(stderr, " -family_to_single Change file driver from family to the default single-file VFD " "(windows or sec2)\n"); HDfprintf(stderr, " SRC The name of the source file\n"); HDfprintf(stderr, " DST The name of the destination files\n"); HDfprintf(stderr, "Sizes may be suffixed with 'g' for GB, 'm' for MB or " "'k' for kB.\n"); HDfprintf(stderr, "File family names include an integer printf " "format such as '%%d'\n"); HDexit(EXIT_FAILURE); } /*------------------------------------------------------------------------- * Function: get_size * * Purpose: Reads a size option of the form `-XNS' where `X' is any * letter, `N' is a multi-character positive decimal number, and * `S' is an optional suffix letter in the set [GgMmk]. The * option may also be split among two arguments as: `-X NS'. * The input value of ARGNO is the argument number for the * switch in the ARGV vector and ARGC is the number of entries * in that vector. * * Return: Success: The value N multiplied according to the * suffix S. On return ARGNO will be the number * of the next argument to process. * * Failure: Calls usage() which exits with a non-zero * status. * * Programmer: Robb Matzke * Wednesday, May 13, 1998 *------------------------------------------------------------------------- */ static off_t get_size(const char *progname, int *argno, int argc, char *argv[]) { off_t retval = -1; char *suffix; if (isdigit((int)(argv[*argno][2]))) { retval = HDstrtol(argv[*argno] + 2, &suffix, 10); (*argno)++; } else if (argv[*argno][2] || *argno + 1 >= argc) { usage(progname); } else { retval = HDstrtol(argv[*argno + 1], &suffix, 0); if (suffix == argv[*argno + 1]) usage(progname); *argno += 2; } if (suffix && suffix[0] && !suffix[1]) { switch (*suffix) { case 'G': case 'g': retval *= 1024 * 1024 * 1024; break; case 'M': case 'm': retval *= 1024 * 1024; break; case 'k': retval *= 1024; break; default: usage(progname); } } else if (suffix && suffix[0]) { usage(progname); } return retval; } /*------------------------------------------------------------------------- * Function: main * * Purpose: Split an hdf5 file * * Return: Success: * * Failure: * * Programmer: Robb Matzke * Wednesday, May 13, 1998 * * Modifications: * *------------------------------------------------------------------------- */ int main(int argc, char *argv[]) { const char *prog_name; /*program name */ size_t blk_size = 1024; /*size of each I/O block */ char * buf = NULL; /*I/O block buffer */ size_t n, i; /*counters */ ssize_t nio; /*I/O return value */ int argno = 1; /*program argument number */ int src, dst = -1; /*source & destination files */ int need_seek = FALSE; /*destination needs to seek? */ int need_write; /*data needs to be written? */ h5_stat_t sb; /*temporary file stat buffer */ int verbose = FALSE; /*display file names? */ const char *src_gen_name; /*general source name */ char * src_name = NULL; /*source member name */ int src_is_family; /*is source name a family name? */ int src_membno = 0; /*source member number */ const char *dst_gen_name; /*general destination name */ char * dst_name = NULL; /*destination member name */ int dst_is_family; /*is dst name a family name? */ int dst_membno = 0; /*destination member number */ off_t left_overs = 0; /*amount of zeros left over */ off_t src_offset = 0; /*offset in source member */ off_t dst_offset = 0; /*offset in destination member */ off_t src_size; /*source logical member size */ off_t src_act_size; /*source actual member size */ off_t dst_size = 1 GB; /*destination logical memb size */ hid_t fapl; /*file access property list */ hid_t file; hsize_t hdsize; /*destination logical memb size */ hbool_t family_to_single = FALSE; /*change family to single file driver? */ /* * Get the program name from argv[0]. Use only the last component. */ if ((prog_name = HDstrrchr(argv[0], '/'))) prog_name++; else prog_name = argv[0]; /* * Parse switches. */ while (argno < argc && '-' == argv[argno][0]) { if (!HDstrcmp(argv[argno], "-v")) { verbose = TRUE; argno++; } else if (!HDstrcmp(argv[argno], "-V")) { HDprintf("This is %s version %u.%u release %u\n", prog_name, H5_VERS_MAJOR, H5_VERS_MINOR, H5_VERS_RELEASE); HDexit(EXIT_SUCCESS); } else if (!HDstrcmp(argv[argno], "-family_to_sec2")) { family_to_single = TRUE; argno++; } else if (!HDstrcmp(argv[argno], "-family_to_single")) { family_to_single = TRUE; argno++; } else if ('b' == argv[argno][1]) { blk_size = (size_t)get_size(prog_name, &argno, argc, argv); } else if ('m' == argv[argno][1]) { dst_size = get_size(prog_name, &argno, argc, argv); } else { usage(prog_name); } /* end if */ } /* end while */ /* allocate names */ if (NULL == (src_name = (char *)HDcalloc((size_t)NAMELEN, sizeof(char)))) HDexit(EXIT_FAILURE); if (NULL == (dst_name = (char *)HDcalloc((size_t)NAMELEN, sizeof(char)))) HDexit(EXIT_FAILURE); /* * Get the name for the source file and open the first member. The size * of the first member determines the logical size of all the members. */ if (argno >= argc) usage(prog_name); src_gen_name = argv[argno++]; HDsprintf(src_name, src_gen_name, src_membno); src_is_family = strcmp(src_name, src_gen_name); if ((src = HDopen(src_name, O_RDONLY)) < 0) { HDperror(src_name); HDexit(EXIT_FAILURE); } if (HDfstat(src, &sb) < 0) { HDperror("fstat"); HDexit(EXIT_FAILURE); } src_size = src_act_size = sb.st_size; if (verbose) HDfprintf(stderr, "< %s\n", src_name); /* * Get the name for the destination file and open the first member. */ if (argno >= argc) usage(prog_name); dst_gen_name = argv[argno++]; HDsprintf(dst_name, dst_gen_name, dst_membno); dst_is_family = HDstrcmp(dst_name, dst_gen_name); if ((dst = HDopen(dst_name, O_RDWR | O_CREAT | O_TRUNC, H5_POSIX_CREATE_MODE_RW)) < 0) { HDperror(dst_name); HDexit(EXIT_FAILURE); } if (verbose) HDfprintf(stderr, "> %s\n", dst_name); /* No more arguments */ if (argno < argc) usage(prog_name); /* Now the real work, split the file */ buf = (char *)HDmalloc(blk_size); while (src_offset < src_size) { /* Read a block. The amount to read is the minimum of: * 1. The I/O block size * 2. What's left to write in the destination member * 3. Left over zeros or what's left in the source member. */ n = blk_size; if (dst_is_family) n = (size_t)MIN((off_t)n, dst_size - dst_offset); if (left_overs) { n = (size_t)MIN((off_t)n, left_overs); left_overs = left_overs - (off_t)n; need_write = FALSE; } else if (src_offset < src_act_size) { n = (size_t)MIN((off_t)n, src_act_size - src_offset); if ((nio = HDread(src, buf, n)) < 0) { HDperror("read"); HDexit(EXIT_FAILURE); } else if ((size_t)nio != n) { HDfprintf(stderr, "%s: short read\n", src_name); HDexit(EXIT_FAILURE); } for (i = 0; i < n; i++) { if (buf[i]) break; } need_write = (i < n); } else { n = 0; left_overs = src_size - src_act_size; need_write = FALSE; } /* * If the block contains non-zero data then write it to the * destination, otherwise just remember that we'll have to do a seek * later in the destination when we finally get non-zero data. */ if (need_write) { if (need_seek && HDlseek(dst, dst_offset, SEEK_SET) < 0) { HDperror("HDlseek"); HDexit(EXIT_FAILURE); } if ((nio = HDwrite(dst, buf, n)) < 0) { HDperror("write"); HDexit(EXIT_FAILURE); } else if ((size_t)nio != n) { HDfprintf(stderr, "%s: short write\n", dst_name); HDexit(EXIT_FAILURE); } need_seek = FALSE; } else { need_seek = TRUE; } /* * Update the source offset and open the next source family member if * necessary. The source stream ends at the first member which * cannot be opened because it doesn't exist. At the end of the * source stream, update the destination offset and break out of the * loop. The destination offset must be updated so we can fix * trailing holes. */ src_offset = src_offset + (off_t)n; if (src_offset == src_act_size) { HDclose(src); if (!src_is_family) { dst_offset = dst_offset + (off_t)n; break; } HDsprintf(src_name, src_gen_name, ++src_membno); if ((src = HDopen(src_name, O_RDONLY)) < 0 && ENOENT == errno) { dst_offset = dst_offset + (off_t)n; break; } else if (src < 0) { HDperror(src_name); HDexit(EXIT_FAILURE); } if (HDfstat(src, &sb) < 0) { HDperror("fstat"); HDexit(EXIT_FAILURE); } src_act_size = sb.st_size; if (src_act_size > src_size) { HDfprintf(stderr, "%s: member truncated to %lu bytes\n", src_name, (unsigned long)src_size); } src_offset = 0; if (verbose) HDfprintf(stderr, "< %s\n", src_name); } /* * Update the destination offset, opening a new member if one will be * needed. The first member is extended to the logical member size * but other members might be smaller if they end with a hole. */ dst_offset = dst_offset + (off_t)n; if (dst_is_family && dst_offset == dst_size) { if (0 == dst_membno) { if (HDlseek(dst, dst_size - 1, SEEK_SET) < 0) { HDperror("HDHDlseek"); HDexit(EXIT_FAILURE); } if (HDread(dst, buf, 1) < 0) { HDperror("read"); HDexit(EXIT_FAILURE); } if (HDlseek(dst, dst_size - 1, SEEK_SET) < 0) { HDperror("HDlseek"); HDexit(EXIT_FAILURE); } if (HDwrite(dst, buf, 1) < 0) { HDperror("write"); HDexit(EXIT_FAILURE); } } HDclose(dst); HDsprintf(dst_name, dst_gen_name, ++dst_membno); if ((dst = HDopen(dst_name, O_RDWR | O_CREAT | O_TRUNC, H5_POSIX_CREATE_MODE_RW)) < 0) { HDperror(dst_name); HDexit(EXIT_FAILURE); } dst_offset = 0; need_seek = FALSE; if (verbose) HDfprintf(stderr, "> %s\n", dst_name); } } /* * Make sure the last family member is the right size and then close it. * The last member can't end with a hole or hdf5 will think that the * family has been truncated. */ if (need_seek) { if (HDlseek(dst, dst_offset - 1, SEEK_SET) < 0) { HDperror("HDlseek"); HDexit(EXIT_FAILURE); } if (HDread(dst, buf, 1) < 0) { HDperror("read"); HDexit(EXIT_FAILURE); } if (HDlseek(dst, dst_offset - 1, SEEK_SET) < 0) { HDperror("HDlseek"); HDexit(EXIT_FAILURE); } if (HDwrite(dst, buf, 1) < 0) { HDperror("write"); HDexit(EXIT_FAILURE); } } HDclose(dst); /* Modify family driver information saved in superblock through private property. * These private properties are for this tool only. */ if ((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0) { HDperror("H5Pcreate"); HDexit(EXIT_FAILURE); } if (family_to_single) { /* The user wants to change file driver from family to a single-file VFD. * Open the file with the sec2, windows, etc. driver. This property signals * the library to ignore the family driver information saved in the superblock. */ if (H5Pset(fapl, H5F_ACS_FAMILY_TO_SINGLE_NAME, &family_to_single) < 0) { HDperror("H5Pset"); HDexit(EXIT_FAILURE); } } else { /* Modify family size saved in superblock through private property. It signals * library to save the new member size(specified in command line) in superblock. * This private property is for this tool only. */ if (H5Pset_fapl_family(fapl, H5F_FAMILY_DEFAULT, H5P_DEFAULT) < 0) { HDperror("H5Pset_fapl_family"); HDexit(EXIT_FAILURE); } /* Set the property of the new member size as hsize_t */ hdsize = (hsize_t)dst_size; if (H5Pset(fapl, H5F_ACS_FAMILY_NEWSIZE_NAME, &hdsize) < 0) { HDperror("H5Pset"); HDexit(EXIT_FAILURE); } } /* If the new file is a family file, try to open file for "read and write" to * flush metadata. Flushing metadata will update the superblock to the new * member size. If the original file is a family file and the new file is a single * file, the property FAMILY_TO_SINGLE will signal the library to switch to default * single-file driver when the new file is opened. If the original file is a single * file and the new file can only be a single file, reopen the new file should fail. * There's nothing to do in this case. */ H5E_BEGIN_TRY { file = H5Fopen(dst_gen_name, H5F_ACC_RDWR, fapl); } H5E_END_TRY; if (file >= 0) { if (H5Fclose(file) < 0) { HDperror("H5Fclose"); HDexit(EXIT_FAILURE); } /* end if */ } /* end if */ if (H5Pclose(fapl) < 0) { HDperror("H5Pclose"); HDexit(EXIT_FAILURE); } /* end if */ /* Free resources and return */ HDfree(src_name); HDfree(dst_name); HDfree(buf); return EXIT_SUCCESS; } /* end main */