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 "mountpoint-util.h"
23 #include "parse-util.h"
24 #include "path-util.h"
25 #include "pretty-print.h"
26 #include "stat-util.h"
30 static const char *arg_target
= NULL
;
31 static bool arg_dry_run
= false;
33 static int resize_ext4(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
34 assert((uint64_t) (int) blocksize
== blocksize
);
39 if (ioctl(mountfd
, EXT4_IOC_RESIZE_FS
, &numblocks
) != 0)
40 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (ext4): %m",
46 static int resize_btrfs(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
47 struct btrfs_ioctl_vol_args args
= {};
50 assert((uint64_t) (int) blocksize
== blocksize
);
52 /* https://bugzilla.kernel.org/show_bug.cgi?id=118111 */
53 if (numblocks
* blocksize
< 256*1024*1024) {
54 log_warning("%s: resizing of btrfs volumes smaller than 256M is not supported", path
);
58 r
= snprintf(args
.name
, sizeof(args
.name
), "%"PRIu64
, numblocks
* blocksize
);
59 /* The buffer is large enough for any number to fit... */
60 assert((size_t) r
< sizeof(args
.name
));
65 if (ioctl(mountfd
, BTRFS_IOC_RESIZE
, &args
) != 0)
66 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (btrfs): %m",
72 #if HAVE_LIBCRYPTSETUP
73 static int resize_crypt_luks_device(dev_t devno
, const char *fstype
, dev_t main_devno
) {
74 _cleanup_free_
char *devpath
= NULL
, *main_devpath
= NULL
;
75 _cleanup_(crypt_freep
) struct crypt_device
*cd
= NULL
;
76 _cleanup_close_
int main_devfd
= -1;
80 r
= device_path_make_major_minor(S_IFBLK
, main_devno
, &main_devpath
);
82 return log_error_errno(r
, "Failed to format device major/minor path: %m");
84 main_devfd
= open(main_devpath
, O_RDONLY
|O_CLOEXEC
);
86 return log_error_errno(errno
, "Failed to open \"%s\": %m", main_devpath
);
88 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
89 return log_error_errno(errno
, "Failed to query size of \"%s\" (before resize): %m",
92 log_debug("%s is %"PRIu64
" bytes", main_devpath
, size
);
93 r
= device_path_make_major_minor(S_IFBLK
, devno
, &devpath
);
95 return log_error_errno(r
, "Failed to format major/minor path: %m");
97 r
= crypt_init(&cd
, devpath
);
99 return log_error_errno(r
, "crypt_init(\"%s\") failed: %m", devpath
);
101 crypt_set_log_callback(cd
, cryptsetup_log_glue
, NULL
);
103 r
= crypt_load(cd
, CRYPT_LUKS
, NULL
);
105 return log_debug_errno(r
, "Failed to load LUKS metadata for %s: %m", devpath
);
110 r
= crypt_resize(cd
, main_devpath
, 0);
112 return log_error_errno(r
, "crypt_resize() of %s failed: %m", devpath
);
114 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
115 log_warning_errno(errno
, "Failed to query size of \"%s\" (after resize): %m",
118 log_debug("%s is now %"PRIu64
" bytes", main_devpath
, size
);
124 static int maybe_resize_slave_device(const char *mountpath
, dev_t main_devno
) {
125 _cleanup_free_
char *fstype
= NULL
, *devpath
= NULL
;
129 #if HAVE_LIBCRYPTSETUP
130 crypt_set_log_callback(NULL
, cryptsetup_log_glue
, NULL
);
131 crypt_set_debug_level(1);
134 r
= get_block_device_harder(mountpath
, &devno
);
136 return log_error_errno(r
, "Failed to determine underlying block device of \"%s\": %m",
139 log_debug("Underlying device %d:%d, main dev %d:%d, %s",
140 major(devno
), minor(devno
),
141 major(main_devno
), minor(main_devno
),
142 devno
== main_devno
? "same" : "different");
143 if (devno
== main_devno
)
146 r
= device_path_make_major_minor(S_IFBLK
, devno
, &devpath
);
148 return log_error_errno(r
, "Failed to format device major/minor path: %m");
150 r
= probe_filesystem(devpath
, &fstype
);
152 return log_warning_errno(r
, "Cannot reliably determine probe \"%s\", refusing to proceed.", devpath
);
154 return log_warning_errno(r
, "Failed to probe \"%s\": %m", devpath
);
156 #if HAVE_LIBCRYPTSETUP
157 if (streq_ptr(fstype
, "crypto_LUKS"))
158 return resize_crypt_luks_device(devno
, fstype
, main_devno
);
161 log_debug("Don't know how to resize %s of type %s, ignoring", devpath
, strnull(fstype
));
165 static int help(void) {
166 _cleanup_free_
char *link
= NULL
;
169 r
= terminal_urlify_man("systemd-growfs@.service", "8", &link
);
173 printf("%s [OPTIONS...] /path/to/mountpoint\n\n"
174 "Grow filesystem or encrypted payload to device size.\n\n"
176 " -h --help Show this help and exit\n"
177 " --version Print version string and exit\n"
178 " -n --dry-run Just print what would be done\n"
179 "\nSee the %s for details.\n"
180 , program_invocation_short_name
187 static int parse_argv(int argc
, char *argv
[]) {
194 static const struct option options
[] = {
195 { "help", no_argument
, NULL
, 'h' },
196 { "version" , no_argument
, NULL
, ARG_VERSION
},
197 { "dry-run", no_argument
, NULL
, 'n' },
204 while ((c
= getopt_long(argc
, argv
, "hn", options
, NULL
)) >= 0)
220 assert_not_reached("Unhandled option");
223 if (optind
+ 1 != argc
)
224 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
225 "%s excepts exactly one argument (the mount point).",
226 program_invocation_short_name
);
228 arg_target
= argv
[optind
];
233 int main(int argc
, char *argv
[]) {
234 _cleanup_close_
int mountfd
= -1, devfd
= -1;
235 _cleanup_free_
char *devpath
= NULL
;
236 uint64_t size
, numblocks
;
237 char fb
[FORMAT_BYTES_MAX
];
245 r
= parse_argv(argc
, argv
);
251 r
= path_is_mount_point(arg_target
, NULL
, 0);
253 log_error_errno(r
, "Failed to check if \"%s\" is a mount point: %m", arg_target
);
257 log_error_errno(r
, "\"%s\" is not a mount point: %m", arg_target
);
261 r
= get_block_device(arg_target
, &devno
);
263 log_error_errno(r
, "Failed to determine block device of \"%s\": %m", arg_target
);
267 r
= maybe_resize_slave_device(arg_target
, devno
);
271 mountfd
= open(arg_target
, O_RDONLY
|O_CLOEXEC
);
273 log_error_errno(errno
, "Failed to open \"%s\": %m", arg_target
);
277 r
= device_path_make_major_minor(S_IFBLK
, devno
, &devpath
);
279 log_error_errno(r
, "Failed to format device major/minor path: %m");
283 devfd
= open(devpath
, O_RDONLY
|O_CLOEXEC
);
285 log_error_errno(errno
, "Failed to open \"%s\": %m", devpath
);
289 if (ioctl(devfd
, BLKBSZGET
, &blocksize
) != 0) {
290 log_error_errno(errno
, "Failed to query block size of \"%s\": %m", devpath
);
294 if (ioctl(devfd
, BLKGETSIZE64
, &size
) != 0) {
295 log_error_errno(errno
, "Failed to query size of \"%s\": %m", devpath
);
299 if (size
% blocksize
!= 0)
300 log_notice("Partition size %"PRIu64
" is not a multiple of the blocksize %d,"
301 " ignoring %"PRIu64
" bytes", size
, blocksize
, size
% blocksize
);
303 numblocks
= size
/ blocksize
;
305 if (fstatfs(mountfd
, &sfs
) < 0) {
306 log_error_errno(errno
, "Failed to stat file system \"%s\": %m", arg_target
);
311 case EXT4_SUPER_MAGIC
:
312 r
= resize_ext4(arg_target
, mountfd
, devfd
, numblocks
, blocksize
);
314 case BTRFS_SUPER_MAGIC
:
315 r
= resize_btrfs(arg_target
, mountfd
, devfd
, numblocks
, blocksize
);
318 log_error("Don't know how to resize fs %llx on \"%s\"",
319 (long long unsigned) sfs
.f_type
, arg_target
);
326 log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64
" blocks of %d bytes).",
327 arg_target
, format_bytes(fb
, sizeof fb
, size
), numblocks
, blocksize
);