]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/mkswap.c
Merge branch 'PR/libmount-exec-errors' of github.com:karelzak/util-linux-work
[thirdparty/util-linux.git] / disk-utils / mkswap.c
index 6f6d3048a3b2e304634cbd4246c4880bcc51a434..9c80b070c754c7d416f34d53e1892d90b6afe860 100644 (file)
@@ -1,4 +1,11 @@
 /*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
  * mkswap.c - set up a linux swap device
  *
  * Copyright (C) 1991 Linus Torvalds
@@ -76,10 +83,13 @@ struct mkswap_control {
 
        int                     user_pagesize;  /* --pagesize */
        int                     pagesize;       /* final pagesize used for the header */
+       off_t                   offset;         /* offset of the header in the target */
 
        char                    *opt_label;     /* LABEL as specified on command line */
        unsigned char           *uuid;          /* UUID parsed by libbuuid */
 
+       unsigned long long      filesz;         /* desired swap file size */
+
        size_t                  nbad_extents;
 
        enum ENDIANNESS         endianness;
@@ -87,7 +97,8 @@ struct mkswap_control {
        unsigned int            check:1,        /* --check */
                                verbose:1,      /* --verbose */
                                quiet:1,        /* --quiet */
-                               force:1;        /* --force */
+                               force:1,        /* --force */
+                               file:1;         /* --file */
 };
 
 static uint32_t cpu32_to_endianness(uint32_t v, enum ENDIANNESS e)
@@ -194,14 +205,17 @@ static void __attribute__((__noreturn__)) usage(void)
        fprintf(out,
              _(" -e, --endianness=<value>  specify the endianness to use "
                                            "(%s, %s or %s)\n"), "native", "little", "big");
+       fputs(_(" -o, --offset OFFSET       specify the offset in the device\n"), out);
+       fputs(_(" -s, --size SIZE           specify the size of a swap file in bytes\n"), out);
+       fputs(_(" -F, --file                create a swap file\n"), out);
        fputs(_("     --verbose             verbose output\n"), out);
 
        fprintf(out,
              _("     --lock[=<mode>]       use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
 
-       printf(USAGE_HELP_OPTIONS(27));
+       fprintf(out, USAGE_HELP_OPTIONS(27));
 
-       printf(USAGE_MAN_TAIL("mkswap(8)"));
+       fprintf(out, USAGE_MAN_TAIL("mkswap(8)"));
        exit(EXIT_SUCCESS);
 }
 
@@ -339,16 +353,22 @@ done:
 /* return size in pages */
 static unsigned long long get_size(const struct mkswap_control *ctl)
 {
-       int fd;
        unsigned long long size;
 
-       fd = open(ctl->devname, O_RDONLY);
-       if (fd < 0)
-               err(EXIT_FAILURE, _("cannot open %s"), ctl->devname);
-       if (blkdev_get_size(fd, &size) == 0)
-               size /= ctl->pagesize;
-
-       close(fd);
+       if (ctl->file && ctl->filesz)
+               size = ctl->filesz;
+       else {
+               int fd = open(ctl->devname, O_RDONLY);
+               if (fd < 0)
+                       err(EXIT_FAILURE, _("cannot open %s"), ctl->devname);
+               if (blkdev_get_size(fd, &size) < 0)
+                       err(EXIT_FAILURE, _("cannot determine size of %s"), ctl->devname);
+               if ((unsigned long long) ctl->offset > size)
+                       errx(EXIT_FAILURE, _("offset larger than file size"));
+               close(fd);
+       }
+       size -= ctl->offset;
+       size /= ctl->pagesize;
        return size;
 }
 
@@ -369,11 +389,33 @@ static void open_device(struct mkswap_control *ctl)
        assert(ctl);
        assert(ctl->devname);
 
-       if (stat(ctl->devname, &ctl->devstat) < 0)
-               err(EXIT_FAILURE, _("stat of %s failed"), ctl->devname);
-       ctl->fd = open_blkdev_or_file(&ctl->devstat, ctl->devname, O_RDWR);
+       if (ctl->file) {
+               if (stat(ctl->devname, &ctl->devstat) == 0) {
+                       if (!S_ISREG(ctl->devstat.st_mode))
+                               err(EXIT_FAILURE, _("cannot create swap file %s: node isn't regular file"), ctl->devname);
+                       if (chmod(ctl->devname, 0600) < 9)
+                               err(EXIT_FAILURE, _("cannot set permissions on swap file %s"), ctl->devname);
+               }
+               ctl->fd = open(ctl->devname, O_RDWR | O_CREAT, 0600);
+       } else {
+               if (stat(ctl->devname, &ctl->devstat) < 0)
+                       err(EXIT_FAILURE, _("stat of %s failed"), ctl->devname);
+               ctl->fd = open_blkdev_or_file(&ctl->devstat, ctl->devname, O_RDWR);
+       }
        if (ctl->fd < 0)
                err(EXIT_FAILURE, _("cannot open %s"), ctl->devname);
+       if (ctl->file && ctl->filesz) {
+               if (ftruncate(ctl->fd, ctl->filesz) < 0)
+                       err(EXIT_FAILURE, _("couldn't allocate swap file %s"), ctl->devname);
+#ifdef HAVE_POSIX_FALLOCATE
+               errno = posix_fallocate(ctl->fd, 0, ctl->filesz);
+               if (errno)
+                       err(EXIT_FAILURE, _("couldn't allocate swap file %s"), ctl->devname);
+#elif defined(HAVE_FALLOCATE)
+               if (fallocate(ctl->fd, 0, 0, ctl->filesz) < 0)
+                       err(EXIT_FAILURE, _("couldn't allocate swap file %s"), ctl->devname);
+#endif
+       }
 
        if (blkdev_lock(ctl->fd, ctl->devname, ctl->lockmode) != 0)
                exit(EXIT_FAILURE);
@@ -464,11 +506,15 @@ static void wipe_device(struct mkswap_control *ctl)
 
 static void write_header_to_device(struct mkswap_control *ctl)
 {
+       off_t offset;
+
        assert(ctl);
        assert(ctl->fd > -1);
        assert(ctl->signature_page);
 
-       if (lseek(ctl->fd, SIGNATURE_OFFSET, SEEK_SET) != SIGNATURE_OFFSET)
+       offset = SIGNATURE_OFFSET + ctl->offset;
+
+       if (lseek(ctl->fd, offset, SEEK_SET) != offset)
                errx(EXIT_FAILURE, _("unable to rewind swap-device"));
 
        if (write_all(ctl->fd, (char *) ctl->signature_page + SIGNATURE_OFFSET,
@@ -502,6 +548,9 @@ int main(int argc, char **argv)
                { "swapversion", required_argument, NULL, 'v' },
                { "uuid",        required_argument, NULL, 'U' },
                { "endianness",  required_argument, NULL, 'e' },
+               { "offset",      required_argument, NULL, 'o' },
+               { "size",        required_argument, NULL, 's' },
+               { "file",        no_argument,       NULL, 'F' },
                { "version",     no_argument,       NULL, 'V' },
                { "help",        no_argument,       NULL, 'h' },
                { "lock",        optional_argument, NULL, OPT_LOCK },
@@ -520,7 +569,7 @@ int main(int argc, char **argv)
        textdomain(PACKAGE);
        close_stdout_atexit();
 
-       while((c = getopt_long(argc, argv, "cfp:qL:v:U:e:Vh", longopts, NULL)) != -1) {
+       while((c = getopt_long(argc, argv, "cfp:qL:v:U:e:o:s:FVh", longopts, NULL)) != -1) {
 
                err_exclusive_options(c, longopts, excl, excl_st);
 
@@ -566,6 +615,16 @@ int main(int argc, char **argv)
                                        _("invalid endianness %s is not supported"), optarg);
                        }
                        break;
+               case 'o':
+                       ctl.offset = str2unum_or_err(optarg,
+                                       10, _("Invalid offset"), SINT_MAX(off_t));
+                       break;
+               case 's':
+                       ctl.filesz = strtosize_or_err(optarg, _("Invalid size"));
+                       break;
+               case 'F':
+                       ctl.file = 1;
+                       break;
                case 'V':
                        print_version(EXIT_SUCCESS);
                        break;
@@ -696,7 +755,8 @@ int main(int argc, char **argv)
 
 #ifdef HAVE_LIBSELINUX
        if (S_ISREG(ctl.devstat.st_mode) && is_selinux_enabled() > 0) {
-               char *context_string, *oldcontext;
+               const char *context_string;
+               char *oldcontext;
                context_t newcontext;
 
                if (fgetfilecon(ctl.fd, &oldcontext) < 0) {