1 /* SPDX-License-Identifier: LGPL-2.1+ */
6 #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"
22 #include "mount-util.h"
23 #include "parse-util.h"
24 #include "path-util.h"
26 #include "terminal-util.h"
28 static const char *arg_target
= NULL
;
29 static bool arg_dry_run
= false;
31 static int resize_ext4(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
32 assert((uint64_t) (int) blocksize
== blocksize
);
37 if (ioctl(mountfd
, EXT4_IOC_RESIZE_FS
, &numblocks
) != 0)
38 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (ext4): %m",
44 static int resize_btrfs(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
45 struct btrfs_ioctl_vol_args args
= {};
48 assert((uint64_t) (int) blocksize
== blocksize
);
50 /* https://bugzilla.kernel.org/show_bug.cgi?id=118111 */
51 if (numblocks
* blocksize
< 256*1024*1024) {
52 log_warning("%s: resizing of btrfs volumes smaller than 256M is not supported", path
);
56 r
= snprintf(args
.name
, sizeof(args
.name
), "%"PRIu64
, numblocks
* blocksize
);
57 /* The buffer is large enough for any number to fit... */
58 assert((size_t) r
< sizeof(args
.name
));
63 if (ioctl(mountfd
, BTRFS_IOC_RESIZE
, &args
) != 0)
64 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (btrfs): %m",
70 #if HAVE_LIBCRYPTSETUP
71 static int resize_crypt_luks_device(dev_t devno
, const char *fstype
, dev_t main_devno
) {
72 char devpath
[DEV_NUM_PATH_MAX
], main_devpath
[DEV_NUM_PATH_MAX
];
73 _cleanup_close_
int main_devfd
= -1;
74 _cleanup_(crypt_freep
) struct crypt_device
*cd
= NULL
;
78 xsprintf_dev_num_path(main_devpath
, "block", main_devno
);
79 main_devfd
= open(main_devpath
, O_RDONLY
|O_CLOEXEC
);
81 return log_error_errno(errno
, "Failed to open \"%s\": %m", main_devpath
);
83 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
84 return log_error_errno(errno
, "Failed to query size of \"%s\" (before resize): %m",
87 log_debug("%s is %"PRIu64
" bytes", main_devpath
, size
);
89 xsprintf_dev_num_path(devpath
, "block", devno
);
90 r
= crypt_init(&cd
, devpath
);
92 return log_error_errno(r
, "crypt_init(\"%s\") failed: %m", devpath
);
94 crypt_set_log_callback(cd
, cryptsetup_log_glue
, NULL
);
96 r
= crypt_load(cd
, CRYPT_LUKS
, NULL
);
98 return log_debug_errno(r
, "Failed to load LUKS metadata for %s: %m", devpath
);
103 r
= crypt_resize(cd
, main_devpath
, 0);
105 return log_error_errno(r
, "crypt_resize() of %s failed: %m", devpath
);
107 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
108 log_warning_errno(errno
, "Failed to query size of \"%s\" (after resize): %m",
111 log_debug("%s is now %"PRIu64
" bytes", main_devpath
, size
);
117 static int maybe_resize_slave_device(const char *mountpath
, dev_t main_devno
) {
119 char devpath
[DEV_NUM_PATH_MAX
];
120 _cleanup_free_
char *fstype
= NULL
;
123 #if HAVE_LIBCRYPTSETUP
124 crypt_set_log_callback(NULL
, cryptsetup_log_glue
, NULL
);
125 crypt_set_debug_level(1);
128 r
= get_block_device_harder(mountpath
, &devno
);
130 return log_error_errno(r
, "Failed to determine underlying block device of \"%s\": %m",
133 log_debug("Underlying device %d:%d, main dev %d:%d, %s",
134 major(devno
), minor(devno
),
135 major(main_devno
), minor(main_devno
),
136 devno
== main_devno
? "same" : "different");
137 if (devno
== main_devno
)
140 xsprintf_dev_num_path(devpath
, "block", devno
);
141 r
= probe_filesystem(devpath
, &fstype
);
143 return log_warning_errno(r
, "Cannot reliably determine probe \"%s\", refusing to proceed.", devpath
);
145 return log_warning_errno(r
, "Failed to probe \"%s\": %m", devpath
);
147 #if HAVE_LIBCRYPTSETUP
148 if (streq_ptr(fstype
, "crypto_LUKS"))
149 return resize_crypt_luks_device(devno
, fstype
, main_devno
);
152 log_debug("Don't know how to resize %s of type %s, ignoring", devpath
, strnull(fstype
));
156 static int help(void) {
157 _cleanup_free_
char *link
= NULL
;
160 r
= terminal_urlify_man("systemd-growfs@.service", "8", &link
);
164 printf("%s [OPTIONS...] /path/to/mountpoint\n\n"
165 "Grow filesystem or encrypted payload to device size.\n\n"
167 " -h --help Show this help and exit\n"
168 " --version Print version string and exit\n"
169 " -n --dry-run Just print what would be done\n"
170 "\nSee the %s for details.\n"
171 , program_invocation_short_name
178 static int parse_argv(int argc
, char *argv
[]) {
185 static const struct option options
[] = {
186 { "help", no_argument
, NULL
, 'h' },
187 { "version" , no_argument
, NULL
, ARG_VERSION
},
188 { "dry-run", no_argument
, NULL
, 'n' },
195 while ((c
= getopt_long(argc
, argv
, "hn", options
, NULL
)) >= 0)
211 assert_not_reached("Unhandled option");
214 if (optind
+ 1 != argc
) {
215 log_error("%s excepts exactly one argument (the mount point).",
216 program_invocation_short_name
);
220 arg_target
= argv
[optind
];
225 int main(int argc
, char *argv
[]) {
227 _cleanup_close_
int mountfd
= -1, devfd
= -1;
229 uint64_t size
, numblocks
;
230 char devpath
[DEV_NUM_PATH_MAX
], fb
[FORMAT_BYTES_MAX
];
234 log_set_target(LOG_TARGET_AUTO
);
235 log_parse_environment();
238 r
= parse_argv(argc
, argv
);
244 r
= path_is_mount_point(arg_target
, NULL
, 0);
246 log_error_errno(r
, "Failed to check if \"%s\" is a mount point: %m", arg_target
);
250 log_error_errno(r
, "\"%s\" is not a mount point: %m", arg_target
);
254 r
= get_block_device(arg_target
, &devno
);
256 log_error_errno(r
, "Failed to determine block device of \"%s\": %m", arg_target
);
260 r
= maybe_resize_slave_device(arg_target
, devno
);
264 mountfd
= open(arg_target
, O_RDONLY
|O_CLOEXEC
);
266 log_error_errno(errno
, "Failed to open \"%s\": %m", arg_target
);
270 xsprintf_dev_num_path(devpath
, "block", devno
);
271 devfd
= open(devpath
, O_RDONLY
|O_CLOEXEC
);
273 log_error_errno(errno
, "Failed to open \"%s\": %m", devpath
);
277 if (ioctl(devfd
, BLKBSZGET
, &blocksize
) != 0) {
278 log_error_errno(errno
, "Failed to query block size of \"%s\": %m", devpath
);
282 if (ioctl(devfd
, BLKGETSIZE64
, &size
) != 0) {
283 log_error_errno(errno
, "Failed to query size of \"%s\": %m", devpath
);
287 if (size
% blocksize
!= 0)
288 log_notice("Partition size %"PRIu64
" is not a multiple of the blocksize %d,"
289 " ignoring %"PRIu64
" bytes", size
, blocksize
, size
% blocksize
);
291 numblocks
= size
/ blocksize
;
293 if (fstatfs(mountfd
, &sfs
) < 0) {
294 log_error_errno(errno
, "Failed to stat file system \"%s\": %m", arg_target
);
299 case EXT4_SUPER_MAGIC
:
300 r
= resize_ext4(arg_target
, mountfd
, devfd
, numblocks
, blocksize
);
302 case BTRFS_SUPER_MAGIC
:
303 r
= resize_btrfs(arg_target
, mountfd
, devfd
, numblocks
, blocksize
);
306 log_error("Don't know how to resize fs %llx on \"%s\"",
307 (long long unsigned) sfs
.f_type
, arg_target
);
314 log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64
" blocks of %d bytes).",
315 arg_target
, format_bytes(fb
, sizeof fb
, size
), numblocks
, blocksize
);