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