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"
29 static const char *arg_target
= NULL
;
30 static bool arg_dry_run
= false;
32 static int resize_ext4(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
33 assert((uint64_t) (int) blocksize
== blocksize
);
38 if (ioctl(mountfd
, EXT4_IOC_RESIZE_FS
, &numblocks
) != 0)
39 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (ext4): %m",
45 static int resize_btrfs(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
46 struct btrfs_ioctl_vol_args args
= {};
49 assert((uint64_t) (int) blocksize
== blocksize
);
51 /* https://bugzilla.kernel.org/show_bug.cgi?id=118111 */
52 if (numblocks
* blocksize
< 256*1024*1024) {
53 log_warning("%s: resizing of btrfs volumes smaller than 256M is not supported", path
);
57 r
= snprintf(args
.name
, sizeof(args
.name
), "%"PRIu64
, numblocks
* blocksize
);
58 /* The buffer is large enough for any number to fit... */
59 assert((size_t) r
< sizeof(args
.name
));
64 if (ioctl(mountfd
, BTRFS_IOC_RESIZE
, &args
) != 0)
65 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (btrfs): %m",
71 #if HAVE_LIBCRYPTSETUP
72 static int resize_crypt_luks_device(dev_t devno
, const char *fstype
, dev_t main_devno
) {
73 _cleanup_free_
char *devpath
= NULL
, *main_devpath
= NULL
;
74 _cleanup_(crypt_freep
) struct crypt_device
*cd
= NULL
;
75 _cleanup_close_
int main_devfd
= -1;
79 r
= device_path_make_major_minor(S_IFBLK
, main_devno
, &main_devpath
);
81 return log_error_errno(r
, "Failed to format device major/minor path: %m");
83 main_devfd
= open(main_devpath
, O_RDONLY
|O_CLOEXEC
);
85 return log_error_errno(errno
, "Failed to open \"%s\": %m", main_devpath
);
87 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
88 return log_error_errno(errno
, "Failed to query size of \"%s\" (before resize): %m",
91 log_debug("%s is %"PRIu64
" bytes", main_devpath
, size
);
92 r
= device_path_make_major_minor(S_IFBLK
, devno
, &devpath
);
94 return log_error_errno(r
, "Failed to format major/minor path: %m");
96 r
= crypt_init(&cd
, devpath
);
98 return log_error_errno(r
, "crypt_init(\"%s\") failed: %m", devpath
);
100 crypt_set_log_callback(cd
, cryptsetup_log_glue
, NULL
);
102 r
= crypt_load(cd
, CRYPT_LUKS
, NULL
);
104 return log_debug_errno(r
, "Failed to load LUKS metadata for %s: %m", devpath
);
109 r
= crypt_resize(cd
, main_devpath
, 0);
111 return log_error_errno(r
, "crypt_resize() of %s failed: %m", devpath
);
113 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
114 log_warning_errno(errno
, "Failed to query size of \"%s\" (after resize): %m",
117 log_debug("%s is now %"PRIu64
" bytes", main_devpath
, size
);
123 static int maybe_resize_slave_device(const char *mountpath
, dev_t main_devno
) {
124 _cleanup_free_
char *fstype
= NULL
, *devpath
= NULL
;
128 #if HAVE_LIBCRYPTSETUP
129 crypt_set_log_callback(NULL
, cryptsetup_log_glue
, NULL
);
130 crypt_set_debug_level(1);
133 r
= get_block_device_harder(mountpath
, &devno
);
135 return log_error_errno(r
, "Failed to determine underlying block device of \"%s\": %m",
138 log_debug("Underlying device %d:%d, main dev %d:%d, %s",
139 major(devno
), minor(devno
),
140 major(main_devno
), minor(main_devno
),
141 devno
== main_devno
? "same" : "different");
142 if (devno
== main_devno
)
145 r
= device_path_make_major_minor(S_IFBLK
, devno
, &devpath
);
147 return log_error_errno(r
, "Failed to format device major/minor path: %m");
149 r
= probe_filesystem(devpath
, &fstype
);
151 return log_warning_errno(r
, "Cannot reliably determine probe \"%s\", refusing to proceed.", devpath
);
153 return log_warning_errno(r
, "Failed to probe \"%s\": %m", devpath
);
155 #if HAVE_LIBCRYPTSETUP
156 if (streq_ptr(fstype
, "crypto_LUKS"))
157 return resize_crypt_luks_device(devno
, fstype
, main_devno
);
160 log_debug("Don't know how to resize %s of type %s, ignoring", devpath
, strnull(fstype
));
164 static int help(void) {
165 _cleanup_free_
char *link
= NULL
;
168 r
= terminal_urlify_man("systemd-growfs@.service", "8", &link
);
172 printf("%s [OPTIONS...] /path/to/mountpoint\n\n"
173 "Grow filesystem or encrypted payload to device size.\n\n"
175 " -h --help Show this help and exit\n"
176 " --version Print version string and exit\n"
177 " -n --dry-run Just print what would be done\n"
178 "\nSee the %s for details.\n"
179 , program_invocation_short_name
186 static int parse_argv(int argc
, char *argv
[]) {
193 static const struct option options
[] = {
194 { "help", no_argument
, NULL
, 'h' },
195 { "version" , no_argument
, NULL
, ARG_VERSION
},
196 { "dry-run", no_argument
, NULL
, 'n' },
203 while ((c
= getopt_long(argc
, argv
, "hn", options
, NULL
)) >= 0)
219 assert_not_reached("Unhandled option");
222 if (optind
+ 1 != argc
)
223 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
224 "%s excepts exactly one argument (the mount point).",
225 program_invocation_short_name
);
227 arg_target
= argv
[optind
];
232 int main(int argc
, char *argv
[]) {
233 _cleanup_close_
int mountfd
= -1, devfd
= -1;
234 _cleanup_free_
char *devpath
= NULL
;
235 uint64_t size
, numblocks
;
236 char fb
[FORMAT_BYTES_MAX
];
244 r
= parse_argv(argc
, argv
);
250 r
= path_is_mount_point(arg_target
, NULL
, 0);
252 log_error_errno(r
, "Failed to check if \"%s\" is a mount point: %m", arg_target
);
256 log_error_errno(r
, "\"%s\" is not a mount point: %m", arg_target
);
260 r
= get_block_device(arg_target
, &devno
);
262 log_error_errno(r
, "Failed to determine block device of \"%s\": %m", arg_target
);
266 r
= maybe_resize_slave_device(arg_target
, devno
);
270 mountfd
= open(arg_target
, O_RDONLY
|O_CLOEXEC
);
272 log_error_errno(errno
, "Failed to open \"%s\": %m", arg_target
);
276 r
= device_path_make_major_minor(S_IFBLK
, devno
, &devpath
);
278 log_error_errno(r
, "Failed to format device major/minor path: %m");
282 devfd
= open(devpath
, O_RDONLY
|O_CLOEXEC
);
284 log_error_errno(errno
, "Failed to open \"%s\": %m", devpath
);
288 if (ioctl(devfd
, BLKBSZGET
, &blocksize
) != 0) {
289 log_error_errno(errno
, "Failed to query block size of \"%s\": %m", devpath
);
293 if (ioctl(devfd
, BLKGETSIZE64
, &size
) != 0) {
294 log_error_errno(errno
, "Failed to query size of \"%s\": %m", devpath
);
298 if (size
% blocksize
!= 0)
299 log_notice("Partition size %"PRIu64
" is not a multiple of the blocksize %d,"
300 " ignoring %"PRIu64
" bytes", size
, blocksize
, size
% blocksize
);
302 numblocks
= size
/ blocksize
;
304 if (fstatfs(mountfd
, &sfs
) < 0) {
305 log_error_errno(errno
, "Failed to stat file system \"%s\": %m", arg_target
);
310 case EXT4_SUPER_MAGIC
:
311 r
= resize_ext4(arg_target
, mountfd
, devfd
, numblocks
, blocksize
);
313 case BTRFS_SUPER_MAGIC
:
314 r
= resize_btrfs(arg_target
, mountfd
, devfd
, numblocks
, blocksize
);
317 log_error("Don't know how to resize fs %llx on \"%s\"",
318 (long long unsigned) sfs
.f_type
, arg_target
);
325 log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64
" blocks of %d bytes).",
326 arg_target
, format_bytes(fb
, sizeof fb
, size
), numblocks
, blocksize
);