1 /* SPDX-License-Identifier: LGPL-2.1+ */
6 #include <linux/btrfs.h>
7 #include <linux/magic.h>
10 #include <sys/types.h>
13 #include "blockdev-util.h"
14 #include "crypt-util.h"
15 #include "device-nodes.h"
16 #include "dissect-image.h"
19 #include "format-util.h"
21 #include "main-func.h"
22 #include "mountpoint-util.h"
23 #include "parse-util.h"
24 #include "pretty-print.h"
25 #include "resize-fs.h"
27 static const char *arg_target
= NULL
;
28 static bool arg_dry_run
= false;
30 #if HAVE_LIBCRYPTSETUP
31 static int resize_crypt_luks_device(dev_t devno
, const char *fstype
, dev_t main_devno
) {
32 _cleanup_free_
char *devpath
= NULL
, *main_devpath
= NULL
;
33 _cleanup_(crypt_freep
) struct crypt_device
*cd
= NULL
;
34 _cleanup_close_
int main_devfd
= -1;
38 r
= device_path_make_major_minor(S_IFBLK
, main_devno
, &main_devpath
);
40 return log_error_errno(r
, "Failed to format device major/minor path: %m");
42 main_devfd
= open(main_devpath
, O_RDONLY
|O_CLOEXEC
);
44 return log_error_errno(errno
, "Failed to open \"%s\": %m", main_devpath
);
46 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
47 return log_error_errno(errno
, "Failed to query size of \"%s\" (before resize): %m",
50 log_debug("%s is %"PRIu64
" bytes", main_devpath
, size
);
51 r
= device_path_make_major_minor(S_IFBLK
, devno
, &devpath
);
53 return log_error_errno(r
, "Failed to format major/minor path: %m");
55 r
= crypt_init(&cd
, devpath
);
57 return log_error_errno(r
, "crypt_init(\"%s\") failed: %m", devpath
);
59 crypt_set_log_callback(cd
, cryptsetup_log_glue
, NULL
);
61 r
= crypt_load(cd
, CRYPT_LUKS
, NULL
);
63 return log_debug_errno(r
, "Failed to load LUKS metadata for %s: %m", devpath
);
68 r
= crypt_resize(cd
, main_devpath
, 0);
70 return log_error_errno(r
, "crypt_resize() of %s failed: %m", devpath
);
72 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
73 log_warning_errno(errno
, "Failed to query size of \"%s\" (after resize): %m",
76 log_debug("%s is now %"PRIu64
" bytes", main_devpath
, size
);
82 static int maybe_resize_underlying_device(const char *mountpath
, dev_t main_devno
) {
83 _cleanup_free_
char *fstype
= NULL
, *devpath
= NULL
;
87 #if HAVE_LIBCRYPTSETUP
88 crypt_set_log_callback(NULL
, cryptsetup_log_glue
, NULL
);
90 crypt_set_debug_level(CRYPT_DEBUG_ALL
);
93 r
= get_block_device_harder(mountpath
, &devno
);
95 return log_error_errno(r
, "Failed to determine underlying block device of \"%s\": %m",
98 log_debug("Underlying device %d:%d, main dev %d:%d, %s",
99 major(devno
), minor(devno
),
100 major(main_devno
), minor(main_devno
),
101 devno
== main_devno
? "same" : "different");
102 if (devno
== main_devno
)
105 r
= device_path_make_major_minor(S_IFBLK
, devno
, &devpath
);
107 return log_error_errno(r
, "Failed to format device major/minor path: %m");
109 r
= probe_filesystem(devpath
, &fstype
);
111 return log_warning_errno(r
, "Cannot reliably determine probe \"%s\", refusing to proceed.", devpath
);
113 return log_warning_errno(r
, "Failed to probe \"%s\": %m", devpath
);
115 #if HAVE_LIBCRYPTSETUP
116 if (streq_ptr(fstype
, "crypto_LUKS"))
117 return resize_crypt_luks_device(devno
, fstype
, main_devno
);
120 log_debug("Don't know how to resize %s of type %s, ignoring.", devpath
, strnull(fstype
));
124 static int help(void) {
125 _cleanup_free_
char *link
= NULL
;
128 r
= terminal_urlify_man("systemd-growfs@.service", "8", &link
);
132 printf("%s [OPTIONS...] /path/to/mountpoint\n\n"
133 "Grow filesystem or encrypted payload to device size.\n\n"
135 " -h --help Show this help and exit\n"
136 " --version Print version string and exit\n"
137 " -n --dry-run Just print what would be done\n"
138 "\nSee the %s for details.\n"
139 , program_invocation_short_name
146 static int parse_argv(int argc
, char *argv
[]) {
153 static const struct option options
[] = {
154 { "help", no_argument
, NULL
, 'h' },
155 { "version" , no_argument
, NULL
, ARG_VERSION
},
156 { "dry-run", no_argument
, NULL
, 'n' },
163 while ((c
= getopt_long(argc
, argv
, "hn", options
, NULL
)) >= 0)
179 assert_not_reached("Unhandled option");
182 if (optind
+ 1 != argc
)
183 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
184 "%s excepts exactly one argument (the mount point).",
185 program_invocation_short_name
);
187 arg_target
= argv
[optind
];
192 static int run(int argc
, char *argv
[]) {
193 _cleanup_close_
int mountfd
= -1, devfd
= -1;
194 _cleanup_free_
char *devpath
= NULL
;
195 uint64_t size
, newsize
;
196 char fb
[FORMAT_BYTES_MAX
];
202 r
= parse_argv(argc
, argv
);
206 r
= path_is_mount_point(arg_target
, NULL
, 0);
208 return log_error_errno(r
, "Failed to check if \"%s\" is a mount point: %m", arg_target
);
210 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "\"%s\" is not a mount point: %m", arg_target
);
212 r
= get_block_device(arg_target
, &devno
);
214 return log_error_errno(r
, "Failed to determine block device of \"%s\": %m", arg_target
);
216 r
= maybe_resize_underlying_device(arg_target
, devno
);
220 mountfd
= open(arg_target
, O_RDONLY
|O_CLOEXEC
);
222 return log_error_errno(errno
, "Failed to open \"%s\": %m", arg_target
);
224 r
= device_path_make_major_minor(S_IFBLK
, devno
, &devpath
);
226 return log_error_errno(r
, "Failed to format device major/minor path: %m");
228 devfd
= open(devpath
, O_RDONLY
|O_CLOEXEC
);
230 return log_error_errno(errno
, "Failed to open \"%s\": %m", devpath
);
232 if (ioctl(devfd
, BLKGETSIZE64
, &size
) != 0)
233 return log_error_errno(errno
, "Failed to query size of \"%s\": %m", devpath
);
235 log_debug("Resizing \"%s\" to %"PRIu64
" bytes...", arg_target
, size
);
236 r
= resize_fs(mountfd
, size
, &newsize
);
238 return log_error_errno(r
, "Failed to resize \"%s\" to %"PRIu64
" bytes: %m",
241 log_info("Successfully resized \"%s\" to %s bytes.",
243 format_bytes(fb
, sizeof fb
, newsize
));
245 log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64
" bytes lost due to blocksize).",
247 format_bytes(fb
, sizeof fb
, newsize
),
252 DEFINE_MAIN_FUNCTION(run
);