diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2017-10-23 13:40:38 +0200 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2017-11-30 20:43:25 +0100 |
commit | 80750adb228c57de36a7ef39cb1a7e3c06ca44d4 (patch) | |
tree | 1250071b75cf2e1700ec0263b1e5aee4e93716d4 /src/partition/growfs.c | |
parent | util-lib: export cryptsetup logging glue function (diff) | |
download | systemd-80750adb228c57de36a7ef39cb1a7e3c06ca44d4.tar.xz systemd-80750adb228c57de36a7ef39cb1a7e3c06ca44d4.zip |
Add systemd-growfs tool
Diffstat (limited to '')
-rw-r--r-- | src/partition/growfs.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/partition/growfs.c b/src/partition/growfs.c new file mode 100644 index 0000000000..e8dc6da449 --- /dev/null +++ b/src/partition/growfs.c @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <fcntl.h> +#include <linux/magic.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/vfs.h> + +#include "device-nodes.h" +#include "escape.h" +#include "fd-util.h" +#include "format-util.h" +#include "log.h" +#include "missing.h" +#include "mount-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "strv.h" + +static int resize_ext4(int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) { + assert((uint64_t) (int) blocksize == blocksize); + + if (ioctl(mountfd, EXT4_IOC_RESIZE_FS, &numblocks) != 0) + return -errno; + + return 0; +} + +static int resize_btrfs(int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) { + struct btrfs_ioctl_vol_args args = {}; + int r; + + assert((uint64_t) (int) blocksize == blocksize); + + r = snprintf(args.name, sizeof(args.name), "%"PRIu64, numblocks * blocksize); + /* The buffer is large enough for any number to fit... */ + assert((size_t) r < sizeof(args.name)); + + if (ioctl(mountfd, BTRFS_IOC_RESIZE, &args) != 0) + return -errno; + + return 0; +} + +int main(int argc, char *argv[]) { + dev_t devno; + _cleanup_close_ int mountfd = -1, devfd = -1; + int blocksize; + uint64_t size, numblocks; + char devpath[DEV_NUM_PATH_MAX], fb[FORMAT_BYTES_MAX]; + struct statfs sfs; + int r; + + if (argc != 2) { + log_error("This program requires one argument (the mountpoint)."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + r = path_is_mount_point(argv[1], NULL, 0); + if (r < 0) { + log_error_errno(r, "Failed to check if \"%s\" is a mount point: %m", argv[1]); + return EXIT_FAILURE; + } + if (r == 0) { + log_error_errno(r, "\"%s\" is not a mount point: %m", argv[1]); + return EXIT_FAILURE; + } + + r = get_block_device(argv[1], &devno); + if (r < 0) { + log_error_errno(r, "Failed to determine block device of \"%s\": %m", argv[1]); + return EXIT_FAILURE; + } + + mountfd = open(argv[1], O_RDONLY|O_CLOEXEC); + if (mountfd < 0) { + log_error_errno(errno, "Failed to open \"%s\": %m", argv[1]); + return EXIT_FAILURE; + } + + xsprintf_dev_num_path(devpath, "block", devno); + devfd = open(devpath, O_RDONLY|O_CLOEXEC); + if (devfd < 0) { + log_error_errno(errno, "Failed to open \"%s\": %m", devpath); + return EXIT_FAILURE; + } + + if (ioctl(devfd, BLKBSZGET, &blocksize) != 0) { + log_error_errno(errno, "Failed to query block size of \"%s\": %m", devpath); + return EXIT_FAILURE; + } + + if (ioctl(devfd, BLKGETSIZE64, &size) != 0) { + log_error_errno(errno, "Failed to query size of \"%s\": %m", devpath); + return EXIT_FAILURE; + } + + if (size % blocksize != 0) + log_notice("Partition size %"PRIu64" is not a multiple of the blocksize %d," + " ignoring %"PRIu64" bytes", size, blocksize, size % blocksize); + + numblocks = size / blocksize; + + if (fstatfs(mountfd, &sfs) < 0) { + log_error_errno(errno, "Failed to stat file system \"%s\": %m", argv[1]); + return EXIT_FAILURE; + } + + switch(sfs.f_type) { + case EXT4_SUPER_MAGIC: + r = resize_ext4(mountfd, devfd, numblocks, blocksize); + break; + case BTRFS_SUPER_MAGIC: + r = resize_btrfs(mountfd, devfd, numblocks, blocksize); + break; + default: + log_error("Don't know how to resize fs %llx on \"%s\"", + (long long unsigned) sfs.f_type, argv[1]); + return EXIT_FAILURE; + } + + if (r < 0) { + log_error_errno(r, "Failed to resize \"%s\" to %"PRIu64" blocks: %m", + argv[1], numblocks); + return EXIT_FAILURE; + } + + log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64" blocks of %d bytes).", + argv[1], format_bytes(fb, sizeof fb, size), numblocks, blocksize); + return EXIT_SUCCESS; +} |