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/>.
23 #include <linux/magic.h>
24 #include <sys/ioctl.h>
25 #include <sys/mount.h>
27 #include <sys/types.h>
30 #include "crypt-util.h"
31 #include "device-nodes.h"
32 #include "dissect-image.h"
35 #include "format-util.h"
38 #include "mount-util.h"
39 #include "parse-util.h"
40 #include "path-util.h"
43 static int resize_ext4(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
44 assert((uint64_t) (int) blocksize
== blocksize
);
46 if (ioctl(mountfd
, EXT4_IOC_RESIZE_FS
, &numblocks
) != 0)
47 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (ext4): %m",
53 static int resize_btrfs(const char *path
, int mountfd
, int devfd
, uint64_t numblocks
, uint64_t blocksize
) {
54 struct btrfs_ioctl_vol_args args
= {};
57 assert((uint64_t) (int) blocksize
== blocksize
);
59 /* https://bugzilla.kernel.org/show_bug.cgi?id=118111 */
60 if (numblocks
* blocksize
< 256*1024*1024) {
61 log_warning("%s: resizing of btrfs volumes smaller than 256M is not supported", path
);
65 r
= snprintf(args
.name
, sizeof(args
.name
), "%"PRIu64
, numblocks
* blocksize
);
66 /* The buffer is large enough for any number to fit... */
67 assert((size_t) r
< sizeof(args
.name
));
69 if (ioctl(mountfd
, BTRFS_IOC_RESIZE
, &args
) != 0)
70 return log_error_errno(errno
, "Failed to resize \"%s\" to %"PRIu64
" blocks (btrfs): %m",
76 static int resize_crypt_luks_device(dev_t devno
, const char *fstype
, dev_t main_devno
) {
77 char devpath
[DEV_NUM_PATH_MAX
], main_devpath
[DEV_NUM_PATH_MAX
];
78 _cleanup_close_
int main_devfd
= -1;
79 _cleanup_(crypt_freep
) struct crypt_device
*cd
= NULL
;
83 xsprintf_dev_num_path(main_devpath
, "block", main_devno
);
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
);
94 xsprintf_dev_num_path(devpath
, "block", devno
);
95 r
= crypt_init(&cd
, devpath
);
97 return log_error_errno(r
, "crypt_init(\"%s\") failed: %m", devpath
);
99 crypt_set_log_callback(cd
, cryptsetup_log_glue
, NULL
);
101 r
= crypt_load(cd
, CRYPT_LUKS
, NULL
);
103 return log_debug_errno(r
, "Failed to load LUKS metadata for %s: %m", devpath
);
105 r
= crypt_resize(cd
, main_devpath
, 0);
107 return log_error_errno(r
, "crypt_resize() of %s failed: %m", devpath
);
109 if (ioctl(main_devfd
, BLKGETSIZE64
, &size
) != 0)
110 log_warning_errno(errno
, "Failed to query size of \"%s\" (after resize): %m",
113 log_debug("%s is now %"PRIu64
" bytes", main_devpath
, size
);
118 static int maybe_resize_slave_device(const char *mountpath
, dev_t main_devno
) {
120 char devpath
[DEV_NUM_PATH_MAX
];
121 _cleanup_free_
char *fstype
= NULL
;
124 crypt_set_log_callback(NULL
, cryptsetup_log_glue
, NULL
);
125 crypt_set_debug_level(1);
127 r
= get_block_device_harder(mountpath
, &devno
);
129 return log_error_errno(r
, "Failed to determine underlying block device of \"%s\": %m",
132 log_debug("Underlying device %d:%d, main dev %d:%d, %s",
133 major(devno
), minor(devno
),
134 major(main_devno
), minor(main_devno
),
135 devno
== main_devno
? "same" : "different");
136 if (devno
== main_devno
)
139 xsprintf_dev_num_path(devpath
, "block", devno
);
140 r
= probe_filesystem(devpath
, &fstype
);
142 return log_warning_errno(r
, "Failed to probe \"%s\": %m", devpath
);
144 if (streq_ptr(fstype
, "crypto_LUKS"))
145 return resize_crypt_luks_device(devno
, fstype
, main_devno
);
147 log_debug("Don't know how to resize %s of type %s, ignoring", devpath
, strnull(fstype
));
151 int main(int argc
, char *argv
[]) {
153 _cleanup_close_
int mountfd
= -1, devfd
= -1;
155 uint64_t size
, numblocks
;
156 char devpath
[DEV_NUM_PATH_MAX
], fb
[FORMAT_BYTES_MAX
];
161 log_error("This program requires one argument (the mountpoint).");
165 log_set_target(LOG_TARGET_AUTO
);
166 log_parse_environment();
169 r
= path_is_mount_point(argv
[1], NULL
, 0);
171 log_error_errno(r
, "Failed to check if \"%s\" is a mount point: %m", argv
[1]);
175 log_error_errno(r
, "\"%s\" is not a mount point: %m", argv
[1]);
179 r
= get_block_device(argv
[1], &devno
);
181 log_error_errno(r
, "Failed to determine block device of \"%s\": %m", argv
[1]);
185 r
= maybe_resize_slave_device(argv
[1], devno
);
189 mountfd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
);
191 log_error_errno(errno
, "Failed to open \"%s\": %m", argv
[1]);
195 xsprintf_dev_num_path(devpath
, "block", devno
);
196 devfd
= open(devpath
, O_RDONLY
|O_CLOEXEC
);
198 log_error_errno(errno
, "Failed to open \"%s\": %m", devpath
);
202 if (ioctl(devfd
, BLKBSZGET
, &blocksize
) != 0) {
203 log_error_errno(errno
, "Failed to query block size of \"%s\": %m", devpath
);
207 if (ioctl(devfd
, BLKGETSIZE64
, &size
) != 0) {
208 log_error_errno(errno
, "Failed to query size of \"%s\": %m", devpath
);
212 if (size
% blocksize
!= 0)
213 log_notice("Partition size %"PRIu64
" is not a multiple of the blocksize %d,"
214 " ignoring %"PRIu64
" bytes", size
, blocksize
, size
% blocksize
);
216 numblocks
= size
/ blocksize
;
218 if (fstatfs(mountfd
, &sfs
) < 0) {
219 log_error_errno(errno
, "Failed to stat file system \"%s\": %m", argv
[1]);
224 case EXT4_SUPER_MAGIC
:
225 r
= resize_ext4(argv
[1], mountfd
, devfd
, numblocks
, blocksize
);
227 case BTRFS_SUPER_MAGIC
:
228 r
= resize_btrfs(argv
[1], mountfd
, devfd
, numblocks
, blocksize
);
231 log_error("Don't know how to resize fs %llx on \"%s\"",
232 (long long unsigned) sfs
.f_type
, argv
[1]);
239 log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64
" blocks of %d bytes).",
240 argv
[1], format_bytes(fb
, sizeof fb
, size
), numblocks
, blocksize
);