From 7377b0d99560460806eab9efa9d8224d084c2082 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 5 Jan 2024 19:50:29 -0800 Subject: [PATCH] mkswap: implement --file Addresses #2359 Signed-off-by: Vicki Pfau --- disk-utils/mkswap.8.adoc | 12 +++++-- disk-utils/mkswap.c | 67 +++++++++++++++++++++++++++++++--------- tests/ts/mkswap/mkswap | 13 ++++++++ 3 files changed, 75 insertions(+), 17 deletions(-) diff --git a/disk-utils/mkswap.8.adoc b/disk-utils/mkswap.8.adoc index ad48c99e08..17212438a6 100644 --- a/disk-utils/mkswap.8.adoc +++ b/disk-utils/mkswap.8.adoc @@ -16,7 +16,9 @@ mkswap - set up a Linux swap area == SYNOPSIS -*mkswap* [options] _device_ [_size_] +*mkswap* [options] _device_ [_blocks_] + +*mkswap* [options] --size _size_ --file _file_ == DESCRIPTION @@ -24,7 +26,7 @@ mkswap - set up a Linux swap area The _device_ argument will usually be a disk partition (something like _/dev/sdb7_) but can also be a file. The Linux kernel does not look at partition IDs, but many installation scripts will assume that partitions of hex type 82 (LINUX_SWAP) are meant to be swap partitions. (*Warning: Solaris also uses this type. Be careful not to kill your Solaris partitions.*) -The _size_ parameter is superfluous but retained for backwards compatibility. (It specifies the desired size of the swap area in 1024-byte blocks. *mkswap* will use the entire partition or file if it is omitted. Specifying it is unwise - a typo may destroy your disk.) +The _blocks_ parameter is superfluous but retained for backwards compatibility. (It specifies the desired size of the swap area in 1024-byte blocks. *mkswap* will use the entire partition or file if it is omitted. Specifying it is unwise - a typo may destroy your disk.) After creating the swap area, you need the *swapon*(8) command to start using it. Usually swap areas are listed in _/etc/fstab_ so that they can be taken into use at boot time by a *swapon -a* command in some boot script. @@ -41,6 +43,9 @@ However, *mkswap* refuses to erase the first block on a device with a disk label *-c*, *--check*:: Check the device (if it is a block device) for bad blocks before creating the swap area. If any bad blocks are found, the count is printed. +*-F*, *--file*:: +Create a swap file with the appropriate file permissions and populated blocks on disk. + *-f*, *--force*:: Go ahead even if the command is stupid. This allows the creation of a swap area larger than the file or partition it resides on. + @@ -76,6 +81,9 @@ Specify the _ENDIANNESS_ to use, valid arguments are *native*, *little* or *big* *-o*, *--offset* _offset_:: Specify the _offset_ to write the swap area to. +*-s*, *--size* _size_:: +Specify the size of the created swap file in bytes and may be followed by a multiplicative suffix: KiB (=1024), MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" is optional, e.g., "K" has the same meaning as "KiB"). If the file exists and is larger than _size_, it will be truncated to this size. This option only makes sense when used with *--file*. + *-v*, *--swapversion 1*:: Specify the swap-space version. (This option is currently pointless, as the old *-v 0* option has become obsolete and now only *-v 1* is supported. The kernel has not supported v0 swap-space format since 2.5.22 (June 2002). The new version v1 is supported since 2.1.117 (August 1998).) diff --git a/disk-utils/mkswap.c b/disk-utils/mkswap.c index 9280af1262..9c80b070c7 100644 --- a/disk-utils/mkswap.c +++ b/disk-utils/mkswap.c @@ -88,6 +88,8 @@ struct mkswap_control { 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; @@ -95,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) @@ -203,6 +206,8 @@ static void __attribute__((__noreturn__)) usage(void) _(" -e, --endianness= 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, @@ -348,20 +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) - 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")); + 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; - - close(fd); return size; } @@ -382,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); @@ -520,6 +549,8 @@ int main(int argc, char **argv) { "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 }, @@ -538,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:o: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); @@ -588,6 +619,12 @@ int main(int argc, char **argv) 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; diff --git a/tests/ts/mkswap/mkswap b/tests/ts/mkswap/mkswap index c4fdce4f03..39b8723a12 100755 --- a/tests/ts/mkswap/mkswap +++ b/tests/ts/mkswap/mkswap @@ -60,6 +60,19 @@ for PAGESIZE in 4096 8192; do cmp "$origimg" "$outimg" >> "$TS_ERRLOG" 2>&1 ts_finalize_subtest + + ts_init_subtest "$name-file" + + rm -f "$outimg" + + "$TS_CMD_MKSWAP" -q -L label -U "$UUID" -e "$ENDIANNESS" -p "$PAGESIZE" -F -s $(( PAGESIZE * 10 )) "$outimg" \ + >> "$TS_OUTPUT" 2>/dev/null \ + || ts_log "mkswap failed" + xz -dc "$TS_SELF/${BYTE_ORDER}-${PAGESIZE}.img.xz" > "$origimg" + + cmp "$origimg" "$outimg" >> "$TS_ERRLOG" 2>&1 + + ts_finalize_subtest done done -- 2.47.2