LCOV - code coverage report
Current view: top level - partition - growfs.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 163 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 7 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 160 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <errno.h>
       4                 :            : #include <fcntl.h>
       5                 :            : #include <getopt.h>
       6                 :            : #include <linux/btrfs.h>
       7                 :            : #include <linux/magic.h>
       8                 :            : #include <sys/ioctl.h>
       9                 :            : #include <sys/mount.h>
      10                 :            : #include <sys/stat.h>
      11                 :            : #include <sys/types.h>
      12                 :            : #include <sys/vfs.h>
      13                 :            : 
      14                 :            : #include "blockdev-util.h"
      15                 :            : #include "crypt-util.h"
      16                 :            : #include "device-nodes.h"
      17                 :            : #include "dissect-image.h"
      18                 :            : #include "escape.h"
      19                 :            : #include "fd-util.h"
      20                 :            : #include "format-util.h"
      21                 :            : #include "log.h"
      22                 :            : #include "missing.h"
      23                 :            : #include "mountpoint-util.h"
      24                 :            : #include "parse-util.h"
      25                 :            : #include "path-util.h"
      26                 :            : #include "pretty-print.h"
      27                 :            : #include "stat-util.h"
      28                 :            : #include "strv.h"
      29                 :            : #include "util.h"
      30                 :            : 
      31                 :            : static const char *arg_target = NULL;
      32                 :            : static bool arg_dry_run = false;
      33                 :            : 
      34                 :          0 : static int resize_ext4(const char *path, int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) {
      35         [ #  # ]:          0 :         assert((uint64_t) (int) blocksize == blocksize);
      36                 :            : 
      37         [ #  # ]:          0 :         if (arg_dry_run)
      38                 :          0 :                 return 0;
      39                 :            : 
      40         [ #  # ]:          0 :         if (ioctl(mountfd, EXT4_IOC_RESIZE_FS, &numblocks) != 0)
      41         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to resize \"%s\" to %"PRIu64" blocks (ext4): %m",
      42                 :            :                                        path, numblocks);
      43                 :            : 
      44                 :          0 :         return 0;
      45                 :            : }
      46                 :            : 
      47                 :          0 : static int resize_btrfs(const char *path, int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) {
      48                 :          0 :         struct btrfs_ioctl_vol_args args = {};
      49                 :            :         int r;
      50                 :            : 
      51         [ #  # ]:          0 :         assert((uint64_t) (int) blocksize == blocksize);
      52                 :            : 
      53                 :            :         /* https://bugzilla.kernel.org/show_bug.cgi?id=118111 */
      54         [ #  # ]:          0 :         if (numblocks * blocksize < 256*1024*1024) {
      55         [ #  # ]:          0 :                 log_warning("%s: resizing of btrfs volumes smaller than 256M is not supported", path);
      56                 :          0 :                 return -EOPNOTSUPP;
      57                 :            :         }
      58                 :            : 
      59                 :          0 :         r = snprintf(args.name, sizeof(args.name), "%"PRIu64, numblocks * blocksize);
      60                 :            :         /* The buffer is large enough for any number to fit... */
      61         [ #  # ]:          0 :         assert((size_t) r < sizeof(args.name));
      62                 :            : 
      63         [ #  # ]:          0 :         if (arg_dry_run)
      64                 :          0 :                 return 0;
      65                 :            : 
      66         [ #  # ]:          0 :         if (ioctl(mountfd, BTRFS_IOC_RESIZE, &args) != 0)
      67         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to resize \"%s\" to %"PRIu64" blocks (btrfs): %m",
      68                 :            :                                        path, numblocks);
      69                 :            : 
      70                 :          0 :         return 0;
      71                 :            : }
      72                 :            : 
      73                 :            : #if HAVE_LIBCRYPTSETUP
      74                 :          0 : static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_devno) {
      75                 :          0 :         _cleanup_free_ char *devpath = NULL, *main_devpath = NULL;
      76                 :          0 :         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
      77                 :          0 :         _cleanup_close_ int main_devfd = -1;
      78                 :            :         uint64_t size;
      79                 :            :         int r;
      80                 :            : 
      81                 :          0 :         r = device_path_make_major_minor(S_IFBLK, main_devno, &main_devpath);
      82         [ #  # ]:          0 :         if (r < 0)
      83         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to format device major/minor path: %m");
      84                 :            : 
      85                 :          0 :         main_devfd = open(main_devpath, O_RDONLY|O_CLOEXEC);
      86         [ #  # ]:          0 :         if (main_devfd < 0)
      87         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to open \"%s\": %m", main_devpath);
      88                 :            : 
      89         [ #  # ]:          0 :         if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0)
      90         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to query size of \"%s\" (before resize): %m",
      91                 :            :                                        main_devpath);
      92                 :            : 
      93         [ #  # ]:          0 :         log_debug("%s is %"PRIu64" bytes", main_devpath, size);
      94                 :          0 :         r = device_path_make_major_minor(S_IFBLK, devno, &devpath);
      95         [ #  # ]:          0 :         if (r < 0)
      96         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to format major/minor path: %m");
      97                 :            : 
      98                 :          0 :         r = crypt_init(&cd, devpath);
      99         [ #  # ]:          0 :         if (r < 0)
     100         [ #  # ]:          0 :                 return log_error_errno(r, "crypt_init(\"%s\") failed: %m", devpath);
     101                 :            : 
     102                 :          0 :         crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
     103                 :            : 
     104                 :          0 :         r = crypt_load(cd, CRYPT_LUKS, NULL);
     105         [ #  # ]:          0 :         if (r < 0)
     106         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to load LUKS metadata for %s: %m", devpath);
     107                 :            : 
     108         [ #  # ]:          0 :         if (arg_dry_run)
     109                 :          0 :                 return 0;
     110                 :            : 
     111                 :          0 :         r = crypt_resize(cd, main_devpath, 0);
     112         [ #  # ]:          0 :         if (r < 0)
     113         [ #  # ]:          0 :                 return log_error_errno(r, "crypt_resize() of %s failed: %m", devpath);
     114                 :            : 
     115         [ #  # ]:          0 :         if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0)
     116         [ #  # ]:          0 :                 log_warning_errno(errno, "Failed to query size of \"%s\" (after resize): %m",
     117                 :            :                                   devpath);
     118                 :            :         else
     119         [ #  # ]:          0 :                 log_debug("%s is now %"PRIu64" bytes", main_devpath, size);
     120                 :            : 
     121                 :          0 :         return 1;
     122                 :            : }
     123                 :            : #endif
     124                 :            : 
     125                 :          0 : static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) {
     126                 :          0 :         _cleanup_free_ char *fstype = NULL, *devpath = NULL;
     127                 :            :         dev_t devno;
     128                 :            :         int r;
     129                 :            : 
     130                 :            : #if HAVE_LIBCRYPTSETUP
     131                 :          0 :         crypt_set_log_callback(NULL, cryptsetup_log_glue, NULL);
     132         [ #  # ]:          0 :         if (DEBUG_LOGGING)
     133                 :          0 :                 crypt_set_debug_level(CRYPT_DEBUG_ALL);
     134                 :            : #endif
     135                 :            : 
     136                 :          0 :         r = get_block_device_harder(mountpath, &devno);
     137         [ #  # ]:          0 :         if (r < 0)
     138         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to determine underlying block device of \"%s\": %m",
     139                 :            :                                        mountpath);
     140                 :            : 
     141   [ #  #  #  # ]:          0 :         log_debug("Underlying device %d:%d, main dev %d:%d, %s",
     142                 :            :                   major(devno), minor(devno),
     143                 :            :                   major(main_devno), minor(main_devno),
     144                 :            :                   devno == main_devno ? "same" : "different");
     145         [ #  # ]:          0 :         if (devno == main_devno)
     146                 :          0 :                 return 0;
     147                 :            : 
     148                 :          0 :         r = device_path_make_major_minor(S_IFBLK, devno, &devpath);
     149         [ #  # ]:          0 :         if (r < 0)
     150         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to format device major/minor path: %m");
     151                 :            : 
     152                 :          0 :         r = probe_filesystem(devpath, &fstype);
     153         [ #  # ]:          0 :         if (r == -EUCLEAN)
     154         [ #  # ]:          0 :                 return log_warning_errno(r, "Cannot reliably determine probe \"%s\", refusing to proceed.", devpath);
     155         [ #  # ]:          0 :         if (r < 0)
     156         [ #  # ]:          0 :                 return log_warning_errno(r, "Failed to probe \"%s\": %m", devpath);
     157                 :            : 
     158                 :            : #if HAVE_LIBCRYPTSETUP
     159         [ #  # ]:          0 :         if (streq_ptr(fstype, "crypto_LUKS"))
     160                 :          0 :                 return resize_crypt_luks_device(devno, fstype, main_devno);
     161                 :            : #endif
     162                 :            : 
     163         [ #  # ]:          0 :         log_debug("Don't know how to resize %s of type %s, ignoring", devpath, strnull(fstype));
     164                 :          0 :         return 0;
     165                 :            : }
     166                 :            : 
     167                 :          0 : static int help(void) {
     168                 :          0 :         _cleanup_free_ char *link = NULL;
     169                 :            :         int r;
     170                 :            : 
     171                 :          0 :         r = terminal_urlify_man("systemd-growfs@.service", "8", &link);
     172         [ #  # ]:          0 :         if (r < 0)
     173                 :          0 :                 return log_oom();
     174                 :            : 
     175                 :          0 :         printf("%s [OPTIONS...] /path/to/mountpoint\n\n"
     176                 :            :                "Grow filesystem or encrypted payload to device size.\n\n"
     177                 :            :                "Options:\n"
     178                 :            :                "  -h --help          Show this help and exit\n"
     179                 :            :                "     --version       Print version string and exit\n"
     180                 :            :                "  -n --dry-run       Just print what would be done\n"
     181                 :            :                "\nSee the %s for details.\n"
     182                 :            :                , program_invocation_short_name
     183                 :            :                , link
     184                 :            :         );
     185                 :            : 
     186                 :          0 :         return 0;
     187                 :            : }
     188                 :            : 
     189                 :          0 : static int parse_argv(int argc, char *argv[]) {
     190                 :            :         enum {
     191                 :            :                 ARG_VERSION = 0x100,
     192                 :            :         };
     193                 :            : 
     194                 :            :         int c;
     195                 :            : 
     196                 :            :         static const struct option options[] = {
     197                 :            :                 { "help",         no_argument,       NULL, 'h'           },
     198                 :            :                 { "version" ,     no_argument,       NULL, ARG_VERSION   },
     199                 :            :                 { "dry-run",      no_argument,       NULL, 'n'           },
     200                 :            :                 {}
     201                 :            :         };
     202                 :            : 
     203         [ #  # ]:          0 :         assert(argc >= 0);
     204         [ #  # ]:          0 :         assert(argv);
     205                 :            : 
     206         [ #  # ]:          0 :         while ((c = getopt_long(argc, argv, "hn", options, NULL)) >= 0)
     207   [ #  #  #  #  :          0 :                 switch(c) {
                      # ]
     208                 :          0 :                 case 'h':
     209                 :          0 :                         return help();
     210                 :            : 
     211                 :          0 :                 case ARG_VERSION:
     212                 :          0 :                         return version();
     213                 :            : 
     214                 :          0 :                 case 'n':
     215                 :          0 :                         arg_dry_run = true;
     216                 :          0 :                         break;
     217                 :            : 
     218                 :          0 :                 case '?':
     219                 :          0 :                         return -EINVAL;
     220                 :            : 
     221                 :          0 :                 default:
     222                 :          0 :                         assert_not_reached("Unhandled option");
     223                 :            :                 }
     224                 :            : 
     225         [ #  # ]:          0 :         if (optind + 1 != argc)
     226         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     227                 :            :                                        "%s excepts exactly one argument (the mount point).",
     228                 :            :                                        program_invocation_short_name);
     229                 :            : 
     230                 :          0 :         arg_target = argv[optind];
     231                 :            : 
     232                 :          0 :         return 1;
     233                 :            : }
     234                 :            : 
     235                 :          0 : int main(int argc, char *argv[]) {
     236                 :          0 :         _cleanup_close_ int mountfd = -1, devfd = -1;
     237                 :          0 :         _cleanup_free_ char *devpath = NULL;
     238                 :            :         uint64_t size, numblocks;
     239                 :            :         char fb[FORMAT_BYTES_MAX];
     240                 :            :         struct statfs sfs;
     241                 :            :         dev_t devno;
     242                 :            :         int blocksize;
     243                 :            :         int r;
     244                 :            : 
     245                 :          0 :         log_setup_service();
     246                 :            : 
     247                 :          0 :         r = parse_argv(argc, argv);
     248         [ #  # ]:          0 :         if (r < 0)
     249                 :          0 :                 return EXIT_FAILURE;
     250         [ #  # ]:          0 :         if (r == 0)
     251                 :          0 :                 return EXIT_SUCCESS;
     252                 :            : 
     253                 :          0 :         r = path_is_mount_point(arg_target, NULL, 0);
     254         [ #  # ]:          0 :         if (r < 0) {
     255         [ #  # ]:          0 :                 log_error_errno(r, "Failed to check if \"%s\" is a mount point: %m", arg_target);
     256                 :          0 :                 return EXIT_FAILURE;
     257                 :            :         }
     258         [ #  # ]:          0 :         if (r == 0) {
     259         [ #  # ]:          0 :                 log_error_errno(r, "\"%s\" is not a mount point: %m", arg_target);
     260                 :          0 :                 return EXIT_FAILURE;
     261                 :            :         }
     262                 :            : 
     263                 :          0 :         r = get_block_device(arg_target, &devno);
     264         [ #  # ]:          0 :         if (r < 0) {
     265         [ #  # ]:          0 :                 log_error_errno(r, "Failed to determine block device of \"%s\": %m", arg_target);
     266                 :          0 :                 return EXIT_FAILURE;
     267                 :            :         }
     268                 :            : 
     269                 :          0 :         r = maybe_resize_slave_device(arg_target, devno);
     270         [ #  # ]:          0 :         if (r < 0)
     271                 :          0 :                 return EXIT_FAILURE;
     272                 :            : 
     273                 :          0 :         mountfd = open(arg_target, O_RDONLY|O_CLOEXEC);
     274         [ #  # ]:          0 :         if (mountfd < 0) {
     275         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to open \"%s\": %m", arg_target);
     276                 :          0 :                 return EXIT_FAILURE;
     277                 :            :         }
     278                 :            : 
     279                 :          0 :         r = device_path_make_major_minor(S_IFBLK, devno, &devpath);
     280         [ #  # ]:          0 :         if (r < 0) {
     281         [ #  # ]:          0 :                 log_error_errno(r, "Failed to format device major/minor path: %m");
     282                 :          0 :                 return EXIT_FAILURE;
     283                 :            :         }
     284                 :            : 
     285                 :          0 :         devfd = open(devpath, O_RDONLY|O_CLOEXEC);
     286         [ #  # ]:          0 :         if (devfd < 0) {
     287         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to open \"%s\": %m", devpath);
     288                 :          0 :                 return EXIT_FAILURE;
     289                 :            :         }
     290                 :            : 
     291         [ #  # ]:          0 :         if (ioctl(devfd, BLKBSZGET, &blocksize) != 0) {
     292         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to query block size of \"%s\": %m", devpath);
     293                 :          0 :                 return EXIT_FAILURE;
     294                 :            :         }
     295                 :            : 
     296         [ #  # ]:          0 :         if (ioctl(devfd, BLKGETSIZE64, &size) != 0) {
     297         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to query size of \"%s\": %m", devpath);
     298                 :          0 :                 return EXIT_FAILURE;
     299                 :            :         }
     300                 :            : 
     301         [ #  # ]:          0 :         if (size % blocksize != 0)
     302         [ #  # ]:          0 :                 log_notice("Partition size %"PRIu64" is not a multiple of the blocksize %d,"
     303                 :            :                            " ignoring %"PRIu64" bytes", size, blocksize, size % blocksize);
     304                 :            : 
     305                 :          0 :         numblocks = size / blocksize;
     306                 :            : 
     307         [ #  # ]:          0 :         if (fstatfs(mountfd, &sfs) < 0) {
     308         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to stat file system \"%s\": %m", arg_target);
     309                 :          0 :                 return EXIT_FAILURE;
     310                 :            :         }
     311                 :            : 
     312      [ #  #  # ]:          0 :         switch(sfs.f_type) {
     313                 :          0 :         case EXT4_SUPER_MAGIC:
     314                 :          0 :                 r = resize_ext4(arg_target, mountfd, devfd, numblocks, blocksize);
     315                 :          0 :                 break;
     316                 :          0 :         case BTRFS_SUPER_MAGIC:
     317                 :          0 :                 r = resize_btrfs(arg_target, mountfd, devfd, numblocks, blocksize);
     318                 :          0 :                 break;
     319                 :          0 :         default:
     320         [ #  # ]:          0 :                 log_error("Don't know how to resize fs %llx on \"%s\"",
     321                 :            :                           (long long unsigned) sfs.f_type, arg_target);
     322                 :          0 :                 return EXIT_FAILURE;
     323                 :            :         }
     324                 :            : 
     325         [ #  # ]:          0 :         if (r < 0)
     326                 :          0 :                 return EXIT_FAILURE;
     327                 :            : 
     328         [ #  # ]:          0 :         log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64" blocks of %d bytes).",
     329                 :            :                  arg_target, format_bytes(fb, sizeof fb, size), numblocks, blocksize);
     330                 :          0 :         return EXIT_SUCCESS;
     331                 :            : }

Generated by: LCOV version 1.14