]>
Commit | Line | Data |
---|---|---|
cc73685d | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
304b4cf3 | 2 | |
1160ce89 CB |
3 | #include "config.h" |
4 | ||
304b4cf3 | 5 | #include <dirent.h> |
cd5f5e48 CB |
6 | #include <errno.h> |
7 | #include <inttypes.h> | |
8 | #include <linux/loop.h> | |
304b4cf3 CB |
9 | #include <stdint.h> |
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
7e009d52 | 12 | #include <sys/stat.h> |
304b4cf3 | 13 | #include <sys/types.h> |
d38dd64a | 14 | #include <unistd.h> |
304b4cf3 | 15 | |
304b4cf3 | 16 | #include "log.h" |
28d832c4 | 17 | #include "loop.h" |
6b4e204c | 18 | #include "memory_utils.h" |
28d832c4 | 19 | #include "storage.h" |
f2d5a09d | 20 | #include "storage_utils.h" |
304b4cf3 CB |
21 | #include "utils.h" |
22 | ||
10bc1861 | 23 | lxc_log_define(loop, lxc); |
304b4cf3 CB |
24 | |
25 | static int do_loop_create(const char *path, uint64_t size, const char *fstype); | |
304b4cf3 CB |
26 | |
27 | /* | |
28 | * No idea what the original blockdev will be called, but the copy will be | |
29 | * called $lxcpath/$lxcname/rootdev | |
30 | */ | |
10bc1861 CB |
31 | int loop_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, |
32 | const char *oldname, const char *cname, const char *oldpath, | |
33 | const char *lxcpath, int snap, uint64_t newsize, | |
34 | struct lxc_conf *conf) | |
304b4cf3 | 35 | { |
6b4e204c | 36 | __do_free char *srcdev = NULL; |
304b4cf3 CB |
37 | uint64_t size = newsize; |
38 | int len, ret; | |
cd5f5e48 | 39 | char fstype[100] = "ext4"; |
304b4cf3 CB |
40 | |
41 | if (snap) { | |
cd5f5e48 | 42 | ERROR("The loop storage driver does not support snapshots"); |
304b4cf3 CB |
43 | return -1; |
44 | } | |
45 | ||
46 | if (!orig->dest || !orig->src) | |
47 | return -1; | |
48 | ||
49 | len = strlen(lxcpath) + strlen(cname) + strlen("rootdev") + 3; | |
6b4e204c | 50 | srcdev = must_realloc(NULL, len); |
304b4cf3 | 51 | ret = snprintf(srcdev, len, "%s/%s/rootdev", lxcpath, cname); |
cd5f5e48 CB |
52 | if (ret < 0 || ret >= len) { |
53 | ERROR("Failed to create string"); | |
304b4cf3 | 54 | return -1; |
cd5f5e48 | 55 | } |
304b4cf3 CB |
56 | |
57 | new->src = malloc(len + 5); | |
cd5f5e48 CB |
58 | if (!new->src) { |
59 | ERROR("Failed to allocate memory"); | |
304b4cf3 | 60 | return -1; |
cd5f5e48 CB |
61 | } |
62 | ||
63 | ret = snprintf(new->src, (len + 5), "loop:%s", srcdev); | |
64 | if (ret < 0 || ret >= (len + 5)) { | |
65 | ERROR("Failed to create string"); | |
304b4cf3 | 66 | return -1; |
cd5f5e48 | 67 | } |
304b4cf3 CB |
68 | |
69 | new->dest = malloc(len); | |
cd5f5e48 CB |
70 | if (!new->dest) { |
71 | ERROR("Failed to allocate memory"); | |
304b4cf3 | 72 | return -1; |
cd5f5e48 CB |
73 | } |
74 | ||
304b4cf3 | 75 | ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname); |
cd5f5e48 CB |
76 | if (ret < 0 || ret >= len) { |
77 | ERROR("Failed to create string"); | |
304b4cf3 | 78 | return -1; |
cd5f5e48 | 79 | } |
304b4cf3 | 80 | |
cd5f5e48 CB |
81 | /* It's tempting to say: if orig->src == loopback and !newsize, then |
82 | * copy the loopback file. However, we'd have to make sure to correctly | |
83 | * keep holes! So punt for now. | |
84 | */ | |
304b4cf3 | 85 | if (is_blktype(orig)) { |
cd5f5e48 | 86 | /* detect size */ |
304b4cf3 | 87 | if (!newsize && blk_getsize(orig, &size) < 0) { |
cd5f5e48 CB |
88 | ERROR("Failed to detect size of loop file \"%s\"", |
89 | orig->src); | |
304b4cf3 CB |
90 | return -1; |
91 | } | |
cd5f5e48 CB |
92 | |
93 | /* detect filesystem */ | |
304b4cf3 | 94 | if (detect_fs(orig, fstype, 100) < 0) { |
cd5f5e48 | 95 | INFO("Failed to detect filesystem type for \"%s\"", orig->src); |
304b4cf3 CB |
96 | return -1; |
97 | } | |
cd5f5e48 | 98 | } else if (!newsize) { |
304b4cf3 CB |
99 | size = DEFAULT_FS_SIZE; |
100 | } | |
cd5f5e48 CB |
101 | |
102 | ret = do_loop_create(srcdev, size, fstype); | |
103 | if (ret < 0) { | |
104 | ERROR("Failed to create loop storage volume \"%s\" with " | |
105 | "filesystem \"%s\" and size \"%" PRIu64 "\"", | |
106 | srcdev, fstype, size); | |
107 | return -1; | |
108 | } | |
109 | ||
110 | return 0; | |
304b4cf3 CB |
111 | } |
112 | ||
10bc1861 | 113 | int loop_create(struct lxc_storage *bdev, const char *dest, const char *n, |
facdf925 | 114 | struct bdev_specs *specs, const struct lxc_conf *conf) |
304b4cf3 | 115 | { |
63fce0c1 | 116 | __do_free char *srcdev = NULL; |
304b4cf3 CB |
117 | const char *fstype; |
118 | uint64_t sz; | |
119 | int ret, len; | |
304b4cf3 CB |
120 | |
121 | if (!specs) | |
122 | return -1; | |
123 | ||
cd5f5e48 CB |
124 | /* <dest> is passed in as <lxcpath>/<lxcname>/rootfs, <srcdev> will |
125 | * be <lxcpath>/<lxcname>/rootdev, and <src> will be "loop:<srcdev>". | |
126 | */ | |
304b4cf3 | 127 | len = strlen(dest) + 2; |
6b4e204c | 128 | srcdev = must_realloc(NULL, len); |
304b4cf3 CB |
129 | |
130 | ret = snprintf(srcdev, len, "%s", dest); | |
cd5f5e48 CB |
131 | if (ret < 0 || ret >= len) { |
132 | ERROR("Failed to create string"); | |
133 | return -1; | |
134 | } | |
135 | ||
136 | ret = sprintf(srcdev + len - 4, "dev"); | |
137 | if (ret < 0) { | |
138 | ERROR("Failed to create string"); | |
304b4cf3 | 139 | return -1; |
cd5f5e48 | 140 | } |
304b4cf3 CB |
141 | |
142 | bdev->src = malloc(len + 5); | |
cd5f5e48 CB |
143 | if (!bdev->src) { |
144 | ERROR("Failed to allocate memory"); | |
304b4cf3 | 145 | return -1; |
cd5f5e48 CB |
146 | } |
147 | ||
304b4cf3 | 148 | ret = snprintf(bdev->src, len + 5, "loop:%s", srcdev); |
cd5f5e48 CB |
149 | if (ret < 0 || ret >= len + 5) { |
150 | ERROR("Failed to create string"); | |
304b4cf3 | 151 | return -1; |
cd5f5e48 | 152 | } |
304b4cf3 CB |
153 | |
154 | sz = specs->fssize; | |
155 | if (!sz) | |
156 | sz = DEFAULT_FS_SIZE; | |
157 | ||
158 | fstype = specs->fstype; | |
159 | if (!fstype) | |
160 | fstype = DEFAULT_FSTYPE; | |
161 | ||
cd5f5e48 CB |
162 | bdev->dest = strdup(dest); |
163 | if (!bdev->dest) { | |
164 | ERROR("Failed to duplicate string \"%s\"", dest); | |
304b4cf3 | 165 | return -1; |
cd5f5e48 | 166 | } |
304b4cf3 | 167 | |
539c3977 | 168 | ret = lxc_mkdir_p(bdev->dest, 0755); |
cd5f5e48 CB |
169 | if (ret < 0) { |
170 | ERROR("Failed creating directory \"%s\"", bdev->dest); | |
304b4cf3 CB |
171 | return -1; |
172 | } | |
173 | ||
cd5f5e48 CB |
174 | |
175 | ret = do_loop_create(srcdev, sz, fstype); | |
176 | if (ret < 0) { | |
177 | ERROR("Failed to create loop storage volume \"%s\" with " | |
178 | "filesystem \"%s\" and size \"%" PRIu64 "\"", | |
179 | srcdev, fstype, sz); | |
180 | return -1; | |
181 | } | |
182 | ||
183 | return 0; | |
304b4cf3 CB |
184 | } |
185 | ||
10bc1861 | 186 | int loop_destroy(struct lxc_storage *orig) { |
70d6bd97 CB |
187 | char *dir; |
188 | ||
189 | dir = orig->src; | |
190 | if (strncmp(orig->src, "loop:", 5) == 0) | |
191 | dir += 5; | |
192 | ||
193 | return unlink(dir); | |
304b4cf3 CB |
194 | } |
195 | ||
3d2ae1e2 | 196 | bool loop_detect(const char *path) |
304b4cf3 | 197 | { |
7e009d52 CB |
198 | int ret; |
199 | struct stat s; | |
200 | ||
f7ac4459 | 201 | if (!strncmp(path, "loop:", 5)) |
3d2ae1e2 | 202 | return true; |
7e009d52 CB |
203 | |
204 | ret = stat(path, &s); | |
205 | if (ret < 0) | |
3d2ae1e2 | 206 | return false; |
7e009d52 CB |
207 | |
208 | if (__S_ISTYPE(s.st_mode, S_IFREG)) | |
3d2ae1e2 | 209 | return true; |
7e009d52 | 210 | |
3d2ae1e2 | 211 | return false; |
304b4cf3 CB |
212 | } |
213 | ||
10bc1861 | 214 | int loop_mount(struct lxc_storage *bdev) |
304b4cf3 | 215 | { |
c6868a1f | 216 | int ret, loopfd; |
339de297 | 217 | char loname[PATH_MAX]; |
41dc7155 | 218 | const char *src; |
304b4cf3 CB |
219 | |
220 | if (strcmp(bdev->type, "loop")) | |
221 | return -22; | |
7e009d52 | 222 | |
304b4cf3 CB |
223 | if (!bdev->src || !bdev->dest) |
224 | return -22; | |
304b4cf3 | 225 | |
7e009d52 | 226 | /* skip prefix */ |
cd5f5e48 | 227 | src = lxc_storage_get_path(bdev->src, bdev->type); |
7e009d52 CB |
228 | |
229 | loopfd = lxc_prepare_loop_dev(src, loname, LO_FLAGS_AUTOCLEAR); | |
a5b18cb1 | 230 | if (loopfd < 0) { |
cd5f5e48 | 231 | ERROR("Failed to prepare loop device for loop file \"%s\"", src); |
c6868a1f | 232 | return -1; |
a5b18cb1 | 233 | } |
cd5f5e48 | 234 | DEBUG("Prepared loop device \"%s\"", loname); |
304b4cf3 CB |
235 | |
236 | ret = mount_unknown_fs(loname, bdev->dest, bdev->mntopts); | |
cd5f5e48 CB |
237 | if (ret < 0) { |
238 | ERROR("Failed to mount rootfs \"%s\" on \"%s\" via loop device \"%s\"", | |
239 | bdev->src, bdev->dest, loname); | |
240 | close(loopfd); | |
241 | return -1; | |
242 | } | |
304b4cf3 | 243 | |
cd5f5e48 CB |
244 | bdev->lofd = loopfd; |
245 | DEBUG("Mounted rootfs \"%s\" on \"%s\" via loop device \"%s\"", | |
246 | bdev->src, bdev->dest, loname); | |
247 | ||
248 | return 0; | |
304b4cf3 CB |
249 | } |
250 | ||
10bc1861 | 251 | int loop_umount(struct lxc_storage *bdev) |
304b4cf3 | 252 | { |
cd5f5e48 | 253 | int ret, saved_errno; |
304b4cf3 CB |
254 | |
255 | if (strcmp(bdev->type, "loop")) | |
256 | return -22; | |
cd5f5e48 | 257 | |
304b4cf3 CB |
258 | if (!bdev->src || !bdev->dest) |
259 | return -22; | |
cd5f5e48 | 260 | |
304b4cf3 | 261 | ret = umount(bdev->dest); |
cd5f5e48 | 262 | saved_errno = errno; |
304b4cf3 CB |
263 | if (bdev->lofd >= 0) { |
264 | close(bdev->lofd); | |
265 | bdev->lofd = -1; | |
266 | } | |
cd5f5e48 CB |
267 | errno = saved_errno; |
268 | ||
269 | if (ret < 0) { | |
270 | SYSERROR("Failed to umount \"%s\"", bdev->dest); | |
271 | return -1; | |
272 | } | |
273 | ||
274 | return 0; | |
304b4cf3 CB |
275 | } |
276 | ||
277 | static int do_loop_create(const char *path, uint64_t size, const char *fstype) | |
278 | { | |
279 | int fd, ret; | |
8737e2a8 | 280 | off_t ret_size; |
339de297 | 281 | char cmd_output[PATH_MAX]; |
cd5f5e48 | 282 | const char *cmd_args[2] = {fstype, path}; |
a5b18cb1 | 283 | |
cd5f5e48 CB |
284 | /* create the new loopback file */ |
285 | fd = creat(path, S_IRUSR | S_IWUSR); | |
286 | if (fd < 0) { | |
287 | SYSERROR("Failed to create new loop file \"%s\"", path); | |
304b4cf3 | 288 | return -1; |
cd5f5e48 CB |
289 | } |
290 | ||
8737e2a8 LJ |
291 | ret_size = lseek(fd, size, SEEK_SET); |
292 | if (ret_size < 0) { | |
cd5f5e48 CB |
293 | SYSERROR("Failed to seek to set new loop file size for loop " |
294 | "file \"%s\"", path); | |
304b4cf3 CB |
295 | close(fd); |
296 | return -1; | |
297 | } | |
cd5f5e48 CB |
298 | |
299 | ret = write(fd, "1", 1); | |
300 | if (ret != 1) { | |
301 | SYSERROR("Failed creating new loop file \"%s\"", path); | |
304b4cf3 CB |
302 | close(fd); |
303 | return -1; | |
304 | } | |
cd5f5e48 | 305 | |
304b4cf3 CB |
306 | ret = close(fd); |
307 | if (ret < 0) { | |
cd5f5e48 | 308 | SYSERROR("Failed to create new loop file \"%s\"", path); |
304b4cf3 CB |
309 | return -1; |
310 | } | |
311 | ||
1a0e70ac | 312 | /* Create an fs in the loopback file. */ |
a5b18cb1 CB |
313 | ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper, |
314 | (void *)cmd_args); | |
cd5f5e48 CB |
315 | if (ret < 0) { |
316 | ERROR("Failed to create new filesystem \"%s\" for loop file " | |
317 | "\"%s\": %s", fstype, path, cmd_output); | |
304b4cf3 | 318 | return -1; |
cd5f5e48 | 319 | } |
304b4cf3 CB |
320 | |
321 | return 0; | |
322 | } |