root/tests/sys/cddl/zfs/bin/mkfile.c
/*-
 * Copyright (c) 2001-2013
 *      HATANO Tomomi.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>

#define MKFILE_WBUF     ((size_t)(1048576))     /* Is 1M a reasonable value? */

/* SunOS's mkfile(8) sets "sticky bit." */
#define MKFILE_FLAG     (O_WRONLY | O_CREAT | O_TRUNC)
#define MKFILE_MODE     (S_IRUSR | S_IWUSR | S_ISVTX)

static char     buf[MKFILE_WBUF];
static int      nofill = 0;
static int      verbose = 0;

static void
usage()
{
        fprintf(stderr,
            "Usage: mkfile [-nv] <size>[e|p|t|g|m|k|b] <filename> ...\n");
}

static unsigned long long
getsize(char *s)
{
        int sh;
        unsigned long long length;
        char *suffix;

        /*
         * NOTE: We don't handle 'Z' (zetta) or 'Y' (yotta) suffixes yet.
         * These are too large to store in unsigned long long (64bits).
         * In the future, we'll have to use larger type,
         * something like uint128_t.
         */
        length = strtoull(s, &suffix, 10);
        sh = 0;
        switch (tolower(*suffix)) {
        case 'e':       /* Exabytes. */
                sh = 60;
                break;
        case 'p':       /* Petabytes. */
                sh = 50;
                break;
        case 't':       /* Terabytes. */
                sh = 40;
                break;
        case 'g':       /* Gigabytes. */
                sh = 30;
                break;
        case 'm':       /* Megabytes. */
                sh = 20;
                break;
        case 'k':       /* Kilobytes. */
                sh = 10;
                break;
        case 'b':       /* Blocks. */
                sh = 9;
                break;
        case '\0':      /* Bytes. */
                break;
        default:        /* Unknown... */
                errno = EINVAL;
                return 0;
        }
        if (sh) {
                unsigned long long l;

                l = length;
                length <<= sh;
                /* Check overflow. */
                if ((length >> sh) != l) {
                        errno = ERANGE;
                        return 0;
                }
        }

        return length;
}

static int
create_file(char *f, unsigned long long s)
{
        int fd;
        size_t w;
        ssize_t ws;

        if (verbose) {
                fprintf(stdout, "%s %llu bytes\n", f, s);
                fflush(stdout);
        }

        /* Open file to create. */
        if ((fd = open(f, MKFILE_FLAG, MKFILE_MODE)) < 0) {
                return -1;
        }

        /* Seek to the end and write 1 byte. */
        if ((lseek(fd, (off_t)(s - 1LL), SEEK_SET) == (off_t)-1) ||
            (write(fd, buf, (size_t)1) == (ssize_t)-1)) {
                /*
                 * We don't close(fd) here to avoid overwriting errno.
                 * This is fd-leak, but is not harmful
                 * because returning error causes mkfile(8) to exit.
                 */
                return -1;
        }

        /* Fill. */
        if (!nofill) {
                if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
                        /* Same as above. */
                        return -1;
                }
                while (s) {
                        w = (s > MKFILE_WBUF) ? MKFILE_WBUF : s;
                        if ((ws = write(fd, buf, w)) == (ssize_t)-1) {
                                /* Same as above. */
                                return -1;
                        }
                        s -= ws;
                }
        }
        close(fd);

        return 0;
}

int
main(int argc, char *argv[])
{
        unsigned long long fsize;
        int ch;

        /* We have at least 2 arguments. */
        if (argc < 3) {
                usage();
                return EXIT_FAILURE;
        }

        /* Options. */
        while ((ch = getopt(argc, argv, "nv")) != -1) {
                switch (ch) {
                case 'n':
                        nofill = 1;
                        break;
                case 'v':
                        verbose = 1;
                        break;
                default:
                        usage();
                        return EXIT_FAILURE;
                }
        }
        argc -= optind;
        argv += optind;

        /* File size to create. */
        if ((fsize = getsize(*argv)) == 0) {
                perror(*argv);
                return EXIT_FAILURE;
        }

        /* Filenames to create. */
        bzero(buf, MKFILE_WBUF);
        while (++argv, --argc) {
                if (create_file(*argv, fsize) == -1) {
                        perror(*argv);
                        return EXIT_FAILURE;
                }
        }

        return EXIT_SUCCESS;
}