1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2017 Zbigniew Jędrzejewski-Szmek
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <linux/magic.h>
25 #include <sys/ioctl.h>
26 #include <sys/mount.h>
28 #include <sys/types.h>
31 #include "crypt-util.h"
32 #include "device-nodes.h"
33 #include "dissect-image.h"
36 #include "format-util.h"
39 #include "mount-util.h"
40 #include "parse-util.h"
41 #include "path-util.h"
44 const char *arg_target
= NULL
;
45 bool arg_dry_run
= false;
47 static int resize_ext4(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
48 assert((uint64_t) (int) blocksize
== blocksize
);
53 if (ioctl(mountfd
, EXT4_IOC_RESIZE_FS
, &numblocks
) != 0)
54 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (ext4): %m",
60 static int resize_btrfs(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
61 struct btrfs_ioctl_vol_args args
= {};
64 assert((uint64_t) (int) blocksize
== blocksize
);
66 /* https://bugzilla.kernel.org/show_bug.cgi?id=118111 */
67 if (numblocks
* blocksize
< 256*1024*1024) {
68 log_warning("%s: resizing of btrfs volumes smaller than 256M is not supported", path
);
72 r
= snprintf(args
.name
, sizeof(args
.name
), "%"PRIu64
, numblocks
* blocksize
);
73 /* The buffer is large enough for any number to fit... */
74 assert((size_t) r
< sizeof(args
.name
));
79 if (ioctl(mountfd
, BTRFS_IOC_RESIZE
, &args
) != 0)
80 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (btrfs): %m",
86 static int resize_crypt_luks_device(dev_t devno
, const char *fstype
, dev_t main_devno
) {
87 char devpath
[DEV_NUM_PATH_MAX
], main_devpath
[DEV_NUM_PATH_MAX
];
88 _cleanup_close_
int main_devfd
= -1;
89 _cleanup_(crypt_freep
) struct crypt_device
*cd
= NULL
;
93 xsprintf_dev_num_path(main_devpath
, "block", main_devno
);
94 main_devfd
= open(main_devpath
, O_RDONLY
|O_CLOEXEC
);
96 return log_error_errno(errno
, "Failed to open \"%s\": %m", main_devpath
);
98 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
99 return log_error_errno(errno
, "Failed to query size of \"%s\" (before resize): %m",
102 log_debug("%s is %"PRIu64
" bytes", main_devpath
, size
);
104 xsprintf_dev_num_path(devpath
, "block", devno
);
105 r
= crypt_init(&cd
, devpath
);
107 return log_error_errno(r
, "crypt_init(\"%s\") failed: %m", devpath
);
109 crypt_set_log_callback(cd
, cryptsetup_log_glue
, NULL
);
111 r
= crypt_load(cd
, CRYPT_LUKS
, NULL
);
113 return log_debug_errno(r
, "Failed to load LUKS metadata for %s: %m", devpath
);
118 r
= crypt_resize(cd
, main_devpath
, 0);
120 return log_error_errno(r
, "crypt_resize() of %s failed: %m", devpath
);
122 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
123 log_warning_errno(errno
, "Failed to query size of \"%s\" (after resize): %m",
126 log_debug("%s is now %"PRIu64
" bytes", main_devpath
, size
);
131 static int maybe_resize_slave_device(const char *mountpath
, dev_t main_devno
) {
133 char devpath
[DEV_NUM_PATH_MAX
];
134 _cleanup_free_
char *fstype
= NULL
;
137 crypt_set_log_callback(NULL
, cryptsetup_log_glue
, NULL
);
138 crypt_set_debug_level(1);
140 r
= get_block_device_harder(mountpath
, &devno
);
142 return log_error_errno(r
, "Failed to determine underlying block device of \"%s\": %m",
145 log_debug("Underlying device %d:%d, main dev %d:%d, %s",
146 major(devno
), minor(devno
),
147 major(main_devno
), minor(main_devno
),
148 devno
== main_devno
? "same" : "different");
149 if (devno
== main_devno
)
152 xsprintf_dev_num_path(devpath
, "block", devno
);
153 r
= probe_filesystem(devpath
, &fstype
);
155 return log_warning_errno(r
, "Failed to probe \"%s\": %m", devpath
);
157 if (streq_ptr(fstype
, "crypto_LUKS"))
158 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 void help(void) {
165 printf("%s [OPTIONS...] /path/to/mountpoint\n\n"
166 "Grow filesystem or encrypted payload to device size.\n\n"
168 " -h --help Show this help and exit\n"
169 " --version Print version string and exit\n"
170 " -n --dry-run Just print what would be done\n"
171 , program_invocation_short_name
);
174 static int parse_argv(int argc
, char *argv
[]) {
181 static const struct option options
[] = {
182 { "help", no_argument
, NULL
, 'h' },
183 { "version" , no_argument
, NULL
, ARG_VERSION
},
184 { "dry-run", no_argument
, NULL
, 'n' },
191 while ((c
= getopt_long(argc
, argv
, "hn", options
, NULL
)) >= 0)
209 assert_not_reached("Unhandled option");
212 if (optind
+ 1 != argc
) {
213 log_error("%s excepts exactly one argument (the mount point).",
214 program_invocation_short_name
);
218 arg_target
= argv
[optind
];
223 int main(int argc
, char *argv
[]) {
225 _cleanup_close_
int mountfd
= -1, devfd
= -1;
227 uint64_t size
, numblocks
;
228 char devpath
[DEV_NUM_PATH_MAX
], fb
[FORMAT_BYTES_MAX
];
232 log_set_target(LOG_TARGET_AUTO
);
233 log_parse_environment();
236 r
= parse_argv(argc
, argv
);
242 r
= path_is_mount_point(arg_target
, NULL
, 0);
244 log_error_errno(r
, "Failed to check if \"%s\" is a mount point: %m", arg_target
);
248 log_error_errno(r
, "\"%s\" is not a mount point: %m", arg_target
);
252 r
= get_block_device(arg_target
, &devno
);
254 log_error_errno(r
, "Failed to determine block device of \"%s\": %m", arg_target
);
258 r
= maybe_resize_slave_device(arg_target
, devno
);
262 mountfd
= open(arg_target
, O_RDONLY
|O_CLOEXEC
);
264 log_error_errno(errno
, "Failed to open \"%s\": %m", arg_target
);
268 xsprintf_dev_num_path(devpath
, "block", devno
);
269 devfd
= open(devpath
, O_RDONLY
|O_CLOEXEC
);
271 log_error_errno(errno
, "Failed to open \"%s\": %m", devpath
);
275 if (ioctl(devfd
, BLKBSZGET
, &blocksize
) != 0) {
276 log_error_errno(errno
, "Failed to query block size of \"%s\": %m", devpath
);
280 if (ioctl(devfd
, BLKGETSIZE64
, &size
) != 0) {
281 log_error_errno(errno
, "Failed to query size of \"%s\": %m", devpath
);
285 if (size
% blocksize
!= 0)
286 log_notice("Partition size %"PRIu64
" is not a multiple of the blocksize %d,"
287 " ignoring %"PRIu64
" bytes", size
, blocksize
, size
% blocksize
);
289 numblocks
= size
/ blocksize
;
291 if (fstatfs(mountfd
, &sfs
) < 0) {
292 log_error_errno(errno
, "Failed to stat file system \"%s\": %m", arg_target
);
297 case EXT4_SUPER_MAGIC
:
298 r
= resize_ext4(arg_target
, mountfd
, devfd
, numblocks
, blocksize
);
300 case BTRFS_SUPER_MAGIC
:
301 r
= resize_btrfs(arg_target
, mountfd
, devfd
, numblocks
, blocksize
);
304 log_error("Don't know how to resize fs %llx on \"%s\"",
305 (long long unsigned) sfs
.f_type
, arg_target
);
312 log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64
" blocks of %d bytes).",
313 arg_target
, format_bytes(fb
, sizeof fb
, size
), numblocks
, blocksize
);