]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/storage/storage_utils.c
tree-wide: use "ptmx" and "pts" as terminal terms
[mirror_lxc.git] / src / lxc / storage / storage_utils.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE 1
5 #endif
6 #include <ctype.h>
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <grp.h>
11 #include <inttypes.h>
12 #include <libgen.h>
13 #include <sched.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <sys/mount.h>
18 #include <sys/prctl.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23
24 #include "config.h"
25 #include "log.h"
26 #include "nbd.h"
27 #include "parse.h"
28 #include "storage.h"
29 #include "storage_utils.h"
30 #include "syscall_wrappers.h"
31 #include "utils.h"
32
33 #ifndef HAVE_STRLCPY
34 #include "include/strlcpy.h"
35 #endif
36
37 #ifndef BLKGETSIZE64
38 #define BLKGETSIZE64 _IOR(0x12, 114, size_t)
39 #endif
40
41 lxc_log_define(storage_utils, lxc);
42
43 /*
44 * attach_block_device returns true if all went well,
45 * meaning either a block device was attached or was not
46 * needed. It returns false if something went wrong and
47 * container startup should be stopped.
48 */
49 bool attach_block_device(struct lxc_conf *conf)
50 {
51 char *path;
52
53 if (!conf->rootfs.path)
54 return true;
55
56 path = conf->rootfs.path;
57 if (!requires_nbd(path))
58 return true;
59
60 path = strchr(path, ':');
61 if (!path)
62 return false;
63
64 path++;
65 if (!attach_nbd(path, conf))
66 return false;
67
68 return true;
69 }
70
71 /*
72 * return block size of dev->src in units of bytes
73 */
74 int blk_getsize(struct lxc_storage *bdev, uint64_t *size)
75 {
76 int fd, ret;
77 const char *src;
78
79 src = lxc_storage_get_path(bdev->src, bdev->type);
80
81 fd = open(src, O_RDONLY | O_CLOEXEC);
82 if (fd < 0) {
83 SYSERROR("Failed to open \"%s\"", src);
84 return -1;
85 }
86
87 /* size of device in bytes */
88 ret = ioctl(fd, BLKGETSIZE64, size);
89 if (ret < 0)
90 SYSERROR("Failed to get block size of dev-src");
91
92 close(fd);
93 return ret;
94 }
95
96 void detach_block_device(struct lxc_conf *conf)
97 {
98 if (conf->nbd_idx != -1)
99 detach_nbd_idx(conf->nbd_idx);
100 }
101
102 /*
103 * Given a lxc_storage (presumably blockdev-based), detect the fstype
104 * by trying mounting (in a private mntns) it.
105 * @lxc_storage: bdev to investigate
106 * @type: preallocated char* in which to write the fstype
107 * @len: length of passed in char*
108 * Returns length of fstype, of -1 on error
109 */
110 int detect_fs(struct lxc_storage *bdev, char *type, int len)
111 {
112 int ret;
113 int p[2];
114 size_t linelen;
115 pid_t pid;
116 FILE *f;
117 char *sp1, *sp2, *sp3;
118 const char *l, *srcdev;
119 char devpath[PATH_MAX];
120 char *line = NULL;
121
122 if (!bdev || !bdev->src || !bdev->dest)
123 return -1;
124
125 srcdev = lxc_storage_get_path(bdev->src, bdev->type);
126
127 ret = pipe(p);
128 if (ret < 0) {
129 SYSERROR("Failed to create pipe");
130 return -1;
131 }
132
133 pid = fork();
134 if (pid < 0) {
135 SYSERROR("Failed to fork process");
136 return -1;
137 }
138
139 if (pid > 0) {
140 int status;
141
142 close(p[1]);
143 memset(type, 0, len);
144
145 ret = read(p[0], type, len - 1);
146 if (ret < 0) {
147 SYSERROR("Failed to read FSType from pipe");
148 } else if (ret == 0) {
149 ERROR("FSType not found - child exited early");
150 ret = -1;
151 }
152
153 close(p[0]);
154 wait(&status);
155
156 if (ret < 0)
157 return ret;
158
159 type[len - 1] = '\0';
160 INFO("Detected FSType \"%s\" for \"%s\"", type, srcdev);
161
162 return ret;
163 }
164
165 if (unshare(CLONE_NEWNS) < 0)
166 _exit(EXIT_FAILURE);
167
168 if (detect_shared_rootfs())
169 if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) {
170 SYSERROR("Failed to make / rslave");
171 ERROR("Continuing...");
172 }
173
174 ret = mount_unknown_fs(srcdev, bdev->dest, bdev->mntopts);
175 if (ret < 0) {
176 ERROR("Failed to mount \"%s\" onto \"%s\" to detect FSType", srcdev,
177 bdev->dest);
178 _exit(EXIT_FAILURE);
179 }
180
181 l = linkderef(srcdev, devpath);
182 if (!l)
183 _exit(EXIT_FAILURE);
184
185 f = fopen("/proc/self/mounts", "r");
186 if (!f)
187 _exit(EXIT_FAILURE);
188
189 while (getline(&line, &linelen, f) != -1) {
190 sp1 = strchr(line, ' ');
191 if (!sp1)
192 _exit(EXIT_FAILURE);
193
194 *sp1 = '\0';
195 if (strcmp(line, l))
196 continue;
197
198 sp2 = strchr(sp1 + 1, ' ');
199 if (!sp2)
200 _exit(EXIT_FAILURE);
201 *sp2 = '\0';
202
203 sp3 = strchr(sp2 + 1, ' ');
204 if (!sp3)
205 _exit(EXIT_FAILURE);
206 *sp3 = '\0';
207
208 sp2++;
209 if (write(p[1], sp2, strlen(sp2)) != strlen(sp2))
210 _exit(EXIT_FAILURE);
211
212 _exit(EXIT_SUCCESS);
213 }
214
215 _exit(EXIT_FAILURE);
216 }
217
218 int do_mkfs_exec_wrapper(void *args)
219 {
220 int ret;
221 char *mkfs;
222 char **data = args;
223 /* strlen("mkfs.")
224 * +
225 * strlen(data[0])
226 * +
227 * \0
228 */
229 size_t len = 5 + strlen(data[0]) + 1;
230
231 mkfs = malloc(len);
232 if (!mkfs)
233 return -1;
234
235 ret = snprintf(mkfs, len, "mkfs.%s", data[0]);
236 if (ret < 0 || (size_t)ret >= len) {
237 free(mkfs);
238 return -1;
239 }
240
241 TRACE("Executing \"%s %s\"", mkfs, data[1]);
242 execlp(mkfs, mkfs, data[1], (char *)NULL);
243
244 SYSERROR("Failed to run \"%s %s\"", mkfs, data[1]);
245 free(mkfs);
246
247 return -1;
248 }
249
250 /*
251 * This will return 1 for physical disks, qemu-nbd, loop, etc right now only lvm
252 * is a block device.
253 */
254 int is_blktype(struct lxc_storage *b)
255 {
256 if (strcmp(b->type, "lvm") == 0)
257 return 1;
258
259 return 0;
260 }
261
262 int mount_unknown_fs(const char *rootfs, const char *target,
263 const char *options)
264 {
265 size_t i;
266 int ret;
267 struct cbarg {
268 const char *rootfs;
269 const char *target;
270 const char *options;
271 } cbarg = {
272 .rootfs = rootfs,
273 .target = target,
274 .options = options,
275 };
276
277 /*
278 * find the filesystem type with brute force:
279 * first we check with /etc/filesystems, in case the modules
280 * are auto-loaded and fall back to the supported kernel fs
281 */
282 char *fsfile[] = {
283 "/etc/filesystems",
284 "/proc/filesystems",
285 };
286
287 for (i = 0; i < sizeof(fsfile) / sizeof(fsfile[0]); i++) {
288 if (access(fsfile[i], F_OK))
289 continue;
290
291 ret = lxc_file_for_each_line(fsfile[i], find_fstype_cb, &cbarg);
292 if (ret < 0) {
293 ERROR("Failed to parse \"%s\"", fsfile[i]);
294 return -1;
295 }
296
297 if (ret)
298 return 0;
299 }
300
301 ERROR("Failed to determine FSType for \"%s\"", rootfs);
302
303 return -1;
304 }
305
306 /*
307 * These are copied from conf.c. However as conf.c will be moved to using
308 * the callback system, they can be pulled from there eventually, so we
309 * don't need to pollute utils.c with these low level functions
310 */
311 int find_fstype_cb(char *buffer, void *data)
312 {
313 struct cbarg {
314 const char *rootfs;
315 const char *target;
316 const char *options;
317 } *cbarg = data;
318 unsigned long mntflags = 0;
319 char *mntdata = NULL;
320 char *fstype;
321
322 /* we don't try 'nodev' entries */
323 if (strstr(buffer, "nodev"))
324 return 0;
325
326 fstype = buffer;
327 fstype += lxc_char_left_gc(fstype, strlen(fstype));
328 fstype[lxc_char_right_gc(fstype, strlen(fstype))] = '\0';
329
330 DEBUG("Trying to mount \"%s\"->\"%s\" with FSType \"%s\"", cbarg->rootfs,
331 cbarg->target, fstype);
332
333 if (parse_mntopts(cbarg->options, &mntflags, &mntdata) < 0) {
334 free(mntdata);
335 return 0;
336 }
337
338 if (mount(cbarg->rootfs, cbarg->target, fstype, mntflags, mntdata)) {
339 SYSDEBUG("Failed to mount");
340 free(mntdata);
341 return 0;
342 }
343
344 free(mntdata);
345
346 INFO("Mounted \"%s\" on \"%s\", with FSType \"%s\"", cbarg->rootfs,
347 cbarg->target, fstype);
348
349 return 1;
350 }
351
352 const char *linkderef(const char *path, char *dest)
353 {
354 struct stat sbuf;
355 ssize_t ret;
356
357 ret = stat(path, &sbuf);
358 if (ret < 0) {
359 SYSERROR("Failed to get status of file - \"%s\"", path);
360 return NULL;
361 }
362
363 if (!S_ISLNK(sbuf.st_mode))
364 return path;
365
366 ret = readlink(path, dest, PATH_MAX);
367 if (ret < 0) {
368 SYSERROR("Failed to read link of \"%s\"", path);
369 return NULL;
370 } else if (ret >= PATH_MAX) {
371 ERROR("The name of link of \"%s\" is too long", path);
372 return NULL;
373 }
374 dest[ret] = '\0';
375
376 return dest;
377 }
378
379 /*
380 * is an unprivileged user allowed to make this kind of snapshot
381 */
382 bool unpriv_snap_allowed(struct lxc_storage *b, const char *t, bool snap,
383 bool maybesnap)
384 {
385 if (!t) {
386 /* New type will be same as original (unless snap && b->type ==
387 * dir, in which case it will be overlayfs -- which is also
388 * allowed).
389 */
390 if (strcmp(b->type, "dir") == 0 ||
391 strcmp(b->type, "overlay") == 0 ||
392 strcmp(b->type, "overlayfs") == 0 ||
393 strcmp(b->type, "btrfs") == 0 ||
394 strcmp(b->type, "loop") == 0)
395 return true;
396
397 return false;
398 }
399
400 /* Unprivileged users can copy and snapshot dir, overlayfs, and loop.
401 * In particular, not zfs, btrfs, or lvm.
402 */
403 if (strcmp(t, "dir") == 0 ||
404 strcmp(t, "overlay") == 0 ||
405 strcmp(t, "overlayfs") == 0 ||
406 strcmp(t, "btrfs") == 0 ||
407 strcmp(t, "loop") == 0)
408 return true;
409
410 return false;
411 }
412
413 uint64_t get_fssize(char *s)
414 {
415 uint64_t ret;
416 char *end;
417
418 ret = strtoull(s, &end, 0);
419 if (end == s) {
420 ERROR("Invalid blockdev size '%s', using default size", s);
421 return 0;
422 }
423
424 while (isblank(*end))
425 end++;
426
427 if (*end == '\0') {
428 ret *= 1024ULL * 1024ULL; /* MB by default */
429 } else if (*end == 'b' || *end == 'B') {
430 ret *= 1ULL;
431 } else if (*end == 'k' || *end == 'K') {
432 ret *= 1024ULL;
433 } else if (*end == 'm' || *end == 'M') {
434 ret *= 1024ULL * 1024ULL;
435 } else if (*end == 'g' || *end == 'G') {
436 ret *= 1024ULL * 1024ULL * 1024ULL;
437 } else if (*end == 't' || *end == 'T') {
438 ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
439 } else {
440 ERROR("Invalid blockdev unit size '%c' in '%s', using default size", *end, s);
441 return 0;
442 }
443
444 return ret;
445 }
446
447 bool is_valid_storage_type(const char *type)
448 {
449 if (strcmp(type, "dir") == 0 ||
450 strcmp(type, "btrfs") == 0 ||
451 strcmp(type, "loop") == 0 ||
452 strcmp(type, "lvm") == 0 ||
453 strcmp(type, "nbd") == 0 ||
454 strcmp(type, "overlay") == 0 ||
455 strcmp(type, "overlayfs") == 0 ||
456 strcmp(type, "rbd") == 0 ||
457 strcmp(type, "zfs") == 0)
458 return true;
459
460 return false;
461 }
462
463 int storage_destroy_wrapper(void *data)
464 {
465 struct lxc_conf *conf = data;
466
467 (void)lxc_setgroups(0, NULL);
468
469 if (setgid(0) < 0) {
470 SYSERROR("Failed to setgid to 0");
471 return -1;
472 }
473
474 if (setuid(0) < 0) {
475 SYSERROR("Failed to setuid to 0");
476 return -1;
477 }
478
479 if (!storage_destroy(conf)) {
480 ERROR("Failed to destroy storage");
481 return -1;
482 }
483
484 return 0;
485 }