Unverified Commit 187439b5 by Christian Brauner Committed by Stéphane Graber

storage: add storage_utils.{c.h}

non-functional changes Signed-off-by: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent 65883989
...@@ -17,6 +17,7 @@ noinst_HEADERS = \ ...@@ -17,6 +17,7 @@ noinst_HEADERS = \
bdev/lxcrbd.h \ bdev/lxcrbd.h \
bdev/lxcrsync.h \ bdev/lxcrsync.h \
bdev/lxczfs.h \ bdev/lxczfs.h \
bdev/storage_utils.h \
cgroups/cgroup.h \ cgroups/cgroup.h \
caps.h \ caps.h \
conf.h \ conf.h \
...@@ -82,6 +83,7 @@ liblxc_la_SOURCES = \ ...@@ -82,6 +83,7 @@ liblxc_la_SOURCES = \
bdev/lxcrbd.c bdev/lxcrbd.h \ bdev/lxcrbd.c bdev/lxcrbd.h \
bdev/lxcrsync.c bdev/lxcrsync.h \ bdev/lxcrsync.c bdev/lxcrsync.h \
bdev/lxczfs.c bdev/lxczfs.h \ bdev/lxczfs.c bdev/lxczfs.h \
bdev/storage_utils.c bdev/storage_utils.h \
cgroups/cgfs.c \ cgroups/cgfs.c \
cgroups/cgfsng.c \ cgroups/cgfsng.c \
cgroups/cgroup.c cgroups/cgroup.h \ cgroups/cgroup.c cgroups/cgroup.h \
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
#include "lxczfs.h" #include "lxczfs.h"
#include "namespace.h" #include "namespace.h"
#include "parse.h" #include "parse.h"
#include "storage_utils.h"
#include "utils.h" #include "utils.h"
#ifndef BLKGETSIZE64 #ifndef BLKGETSIZE64
...@@ -200,86 +201,85 @@ static const struct bdev_type bdevs[] = { ...@@ -200,86 +201,85 @@ static const struct bdev_type bdevs[] = {
static const size_t numbdevs = sizeof(bdevs) / sizeof(struct bdev_type); static const size_t numbdevs = sizeof(bdevs) / sizeof(struct bdev_type);
/* helpers */ static const struct bdev_type *get_bdev_by_name(const char *name)
static const struct bdev_type *bdev_query(struct lxc_conf *conf,
const char *src);
static struct bdev *bdev_get(const char *type);
static struct bdev *do_bdev_create(const char *dest, const char *type,
const char *cname, struct bdev_specs *specs);
static int find_fstype_cb(char *buffer, void *data);
static char *linkderef(char *path, char *dest);
static bool unpriv_snap_allowed(struct bdev *b, const char *t, bool snap,
bool maybesnap);
/* the bulk of this needs to become a common helper */
char *dir_new_path(char *src, const char *oldname, const char *name,
const char *oldpath, const char *lxcpath)
{ {
char *ret, *p, *p2; size_t i, cmplen;
int l1, l2, nlen;
nlen = strlen(src) + 1;
l1 = strlen(oldpath);
p = src;
/* if src starts with oldpath, look for oldname only after
* that path */
if (strncmp(src, oldpath, l1) == 0) {
p += l1;
nlen += (strlen(lxcpath) - l1);
}
l2 = strlen(oldname);
while ((p = strstr(p, oldname)) != NULL) {
p += l2;
nlen += strlen(name) - l2;
}
ret = malloc(nlen); cmplen = strcspn(name, ":");
if (!ret) if (cmplen == 0)
return NULL; return NULL;
p = ret; for (i = 0; i < numbdevs; i++)
if (strncmp(src, oldpath, l1) == 0) { if (strncmp(bdevs[i].name, name, cmplen) == 0)
p += sprintf(p, "%s", lxcpath); break;
src += l1;
}
while ((p2 = strstr(src, oldname)) != NULL) { if (i == numbdevs)
strncpy(p, src, p2 - src); // copy text up to oldname return NULL;
p += p2 - src; // move target pointer (p)
p += sprintf(p, "%s", DEBUG("Detected rootfs type \"%s\"", bdevs[i].name);
name); // print new name in place of oldname return &bdevs[i];
src = p2 + l2; // move src to end of oldname
}
sprintf(p, "%s", src); // copy the rest of src
return ret;
} }
/* const struct bdev_type *bdev_query(struct lxc_conf *conf, const char *src)
* attach_block_device returns true if all went well,
* meaning either a block device was attached or was not
* needed. It returns false if something went wrong and
* container startup should be stopped.
*/
bool attach_block_device(struct lxc_conf *conf)
{ {
char *path; size_t i;
const struct bdev_type *bdev;
if (!conf->rootfs.path) bdev = get_bdev_by_name(src);
return true; if (bdev)
return bdev;
path = conf->rootfs.path; for (i = 0; i < numbdevs; i++)
if (!requires_nbd(path)) if (bdevs[i].ops->detect(src))
return true; break;
path = strchr(path, ':'); if (i == numbdevs)
if (!path) return NULL;
return false;
path++; DEBUG("Detected rootfs type \"%s\"", bdevs[i].name);
if (!attach_nbd(path, conf)) return &bdevs[i];
return false; }
return true; struct bdev *bdev_get(const char *type)
{
size_t i;
struct bdev *bdev;
for (i = 0; i < numbdevs; i++) {
if (strcmp(bdevs[i].name, type) == 0)
break;
}
if (i == numbdevs)
return NULL;
bdev = malloc(sizeof(struct bdev));
if (!bdev)
return NULL;
memset(bdev, 0, sizeof(struct bdev));
bdev->ops = bdevs[i].ops;
bdev->type = bdevs[i].name;
return bdev;
}
static struct bdev *do_bdev_create(const char *dest, const char *type,
const char *cname, struct bdev_specs *specs)
{
struct bdev *bdev;
bdev = bdev_get(type);
if (!bdev)
return NULL;
if (bdev->ops->create(bdev, dest, cname, specs) < 0) {
bdev_put(bdev);
return NULL;
}
return bdev;
} }
bool bdev_can_backup(struct lxc_conf *conf) bool bdev_can_backup(struct lxc_conf *conf)
...@@ -529,29 +529,6 @@ bool bdev_destroy(struct lxc_conf *conf) ...@@ -529,29 +529,6 @@ bool bdev_destroy(struct lxc_conf *conf)
return ret; return ret;
} }
int bdev_destroy_wrapper(void *data)
{
struct lxc_conf *conf = data;
if (setgid(0) < 0) {
ERROR("Failed to setgid to 0");
return -1;
}
if (setgroups(0, NULL) < 0)
WARN("Failed to clear groups");
if (setuid(0) < 0) {
ERROR("Failed to setuid to 0");
return -1;
}
if (!bdev_destroy(conf))
return -1;
return 0;
}
struct bdev *bdev_init(struct lxc_conf *conf, const char *src, const char *dst, struct bdev *bdev_init(struct lxc_conf *conf, const char *src, const char *dst,
const char *mntopts) const char *mntopts)
{ {
...@@ -607,219 +584,6 @@ void bdev_put(struct bdev *bdev) ...@@ -607,219 +584,6 @@ void bdev_put(struct bdev *bdev)
free(bdev); free(bdev);
} }
/*
* return block size of dev->src in units of bytes
*/
int blk_getsize(struct bdev *bdev, uint64_t *size)
{
int fd, ret;
char *path = bdev->src;
if (strcmp(bdev->type, "loop") == 0)
path = bdev->src + 5;
fd = open(path, O_RDONLY);
if (fd < 0)
return -1;
ret = ioctl(fd, BLKGETSIZE64, size); // size of device in bytes
close(fd);
return ret;
}
void detach_block_device(struct lxc_conf *conf)
{
if (conf->nbd_idx != -1)
detach_nbd_idx(conf->nbd_idx);
}
/*
* Given a bdev (presumably blockdev-based), detect the fstype
* by trying mounting (in a private mntns) it.
* @bdev: bdev to investigate
* @type: preallocated char* in which to write the fstype
* @len: length of passed in char*
* Returns length of fstype, of -1 on error
*/
int detect_fs(struct bdev *bdev, char *type, int len)
{
int p[2], ret;
size_t linelen;
pid_t pid;
FILE *f;
char *sp1, *sp2, *sp3, *line = NULL;
char *srcdev;
if (!bdev || !bdev->src || !bdev->dest)
return -1;
srcdev = bdev->src;
if (strcmp(bdev->type, "loop") == 0)
srcdev = bdev->src + 5;
ret = pipe(p);
if (ret < 0)
return -1;
if ((pid = fork()) < 0)
return -1;
if (pid > 0) {
int status;
close(p[1]);
memset(type, 0, len);
ret = read(p[0], type, len - 1);
close(p[0]);
if (ret < 0) {
SYSERROR("error reading from pipe");
wait(&status);
return -1;
} else if (ret == 0) {
ERROR("child exited early - fstype not found");
wait(&status);
return -1;
}
wait(&status);
type[len - 1] = '\0';
INFO("detected fstype %s for %s", type, srcdev);
return ret;
}
if (unshare(CLONE_NEWNS) < 0)
exit(1);
if (detect_shared_rootfs()) {
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) {
SYSERROR("Failed to make / rslave");
ERROR("Continuing...");
}
}
ret = mount_unknown_fs(srcdev, bdev->dest, bdev->mntopts);
if (ret < 0) {
ERROR("failed mounting %s onto %s to detect fstype", srcdev,
bdev->dest);
exit(1);
}
// if symlink, get the real dev name
char devpath[MAXPATHLEN];
char *l = linkderef(srcdev, devpath);
if (!l)
exit(1);
f = fopen("/proc/self/mounts", "r");
if (!f)
exit(1);
while (getline(&line, &linelen, f) != -1) {
sp1 = strchr(line, ' ');
if (!sp1)
exit(1);
*sp1 = '\0';
if (strcmp(line, l))
continue;
sp2 = strchr(sp1 + 1, ' ');
if (!sp2)
exit(1);
*sp2 = '\0';
sp3 = strchr(sp2 + 1, ' ');
if (!sp3)
exit(1);
*sp3 = '\0';
sp2++;
if (write(p[1], sp2, strlen(sp2)) != strlen(sp2))
exit(1);
exit(0);
}
exit(1);
}
int do_mkfs_exec_wrapper(void *args)
{
int ret;
char *mkfs;
char **data = args;
/* strlen("mkfs.")
* +
* strlen(data[0])
* +
* \0
*/
size_t len = 5 + strlen(data[0]) + 1;
mkfs = malloc(len);
if (!mkfs)
return -1;
ret = snprintf(mkfs, len, "mkfs.%s", data[0]);
if (ret < 0 || (size_t)ret >= len) {
free(mkfs);
return -1;
}
TRACE("executing \"%s %s\"", mkfs, data[1]);
execlp(mkfs, mkfs, data[1], (char *)NULL);
SYSERROR("failed to run \"%s %s \"", mkfs, data[1]);
return -1;
}
/*
* This will return 1 for physical disks, qemu-nbd, loop, etc right now only lvm
* is a block device.
*/
int is_blktype(struct bdev *b)
{
if (strcmp(b->type, "lvm") == 0)
return 1;
return 0;
}
int mount_unknown_fs(const char *rootfs, const char *target,
const char *options)
{
size_t i;
int ret;
struct cbarg {
const char *rootfs;
const char *target;
const char *options;
} cbarg = {
.rootfs = rootfs,
.target = target,
.options = options,
};
/*
* find the filesystem type with brute force:
* first we check with /etc/filesystems, in case the modules
* are auto-loaded and fall back to the supported kernel fs
*/
char *fsfile[] = {
"/etc/filesystems",
"/proc/filesystems",
};
for (i = 0; i < sizeof(fsfile) / sizeof(fsfile[0]); i++) {
if (access(fsfile[i], F_OK))
continue;
ret = lxc_file_for_each_line(fsfile[i], find_fstype_cb, &cbarg);
if (ret < 0) {
ERROR("failed to parse '%s'", fsfile[i]);
return -1;
}
if (ret)
return 0;
}
ERROR("failed to determine fs type for '%s'", rootfs);
return -1;
}
bool rootfs_is_blockdev(struct lxc_conf *conf) bool rootfs_is_blockdev(struct lxc_conf *conf)
{ {
const struct bdev_type *q; const struct bdev_type *q;
...@@ -845,204 +609,3 @@ bool rootfs_is_blockdev(struct lxc_conf *conf) ...@@ -845,204 +609,3 @@ bool rootfs_is_blockdev(struct lxc_conf *conf)
return false; return false;
} }
static struct bdev *do_bdev_create(const char *dest, const char *type,
const char *cname, struct bdev_specs *specs)
{
struct bdev *bdev;
bdev = bdev_get(type);
if (!bdev)
return NULL;
if (bdev->ops->create(bdev, dest, cname, specs) < 0) {
bdev_put(bdev);
return NULL;
}
return bdev;
}
static struct bdev *bdev_get(const char *type)
{
size_t i;
struct bdev *bdev;
for (i = 0; i < numbdevs; i++) {
if (strcmp(bdevs[i].name, type) == 0)
break;
}
if (i == numbdevs)
return NULL;
bdev = malloc(sizeof(struct bdev));
if (!bdev)
return NULL;
memset(bdev, 0, sizeof(struct bdev));
bdev->ops = bdevs[i].ops;
bdev->type = bdevs[i].name;
return bdev;
}
static const struct bdev_type *get_bdev_by_name(const char *name)
{
size_t i;
for (i = 0; i < numbdevs; i++) {
if (strcmp(bdevs[i].name, name) == 0)
return &bdevs[i];
}
ERROR("Backing store %s unknown but not caught earlier\n", name);
return NULL;
}
static const struct bdev_type *bdev_query(struct lxc_conf *conf,
const char *src)
{
size_t i;
if (conf->rootfs.bdev_type) {
DEBUG("config file specified rootfs type \"%s\"",
conf->rootfs.bdev_type);
return get_bdev_by_name(conf->rootfs.bdev_type);
}
for (i = 0; i < numbdevs; i++) {
int r;
r = bdevs[i].ops->detect(src);
if (r)
break;
}
if (i == numbdevs)
return NULL;
DEBUG("detected rootfs type \"%s\"", bdevs[i].name);
return &bdevs[i];
}
/*
* These are copied from conf.c. However as conf.c will be moved to using
* the callback system, they can be pulled from there eventually, so we
* don't need to pollute utils.c with these low level functions
*/
static int find_fstype_cb(char *buffer, void *data)
{
struct cbarg {
const char *rootfs;
const char *target;
const char *options;
} *cbarg = data;
unsigned long mntflags;
char *mntdata;
char *fstype;
/* we don't try 'nodev' entries */
if (strstr(buffer, "nodev"))
return 0;
fstype = buffer;
fstype += lxc_char_left_gc(fstype, strlen(fstype));
fstype[lxc_char_right_gc(fstype, strlen(fstype))] = '\0';
DEBUG("trying to mount '%s'->'%s' with fstype '%s'", cbarg->rootfs,
cbarg->target, fstype);
if (parse_mntopts(cbarg->options, &mntflags, &mntdata) < 0) {
free(mntdata);
return 0;
}
if (mount(cbarg->rootfs, cbarg->target, fstype, mntflags, mntdata)) {
DEBUG("mount failed with error: %s", strerror(errno));
free(mntdata);
return 0;
}
free(mntdata);
INFO("mounted '%s' on '%s', with fstype '%s'", cbarg->rootfs,
cbarg->target, fstype);
return 1;
}
static char *linkderef(char *path, char *dest)
{
struct stat sbuf;
ssize_t ret;
ret = stat(path, &sbuf);
if (ret < 0)
return NULL;
if (!S_ISLNK(sbuf.st_mode))
return path;
ret = readlink(path, dest, MAXPATHLEN);
if (ret < 0) {
SYSERROR("error reading link %s", path);
return NULL;
} else if (ret >= MAXPATHLEN) {
ERROR("link in %s too long", path);
return NULL;
}
dest[ret] = '\0';
return dest;
}
/*
* is an unprivileged user allowed to make this kind of snapshot
*/
static bool unpriv_snap_allowed(struct bdev *b, const char *t, bool snap,
bool maybesnap)
{
if (!t) {
// new type will be same as original
// (unless snap && b->type == dir, in which case it will be
// overlayfs -- which is also allowed)
if (strcmp(b->type, "dir") == 0 ||
strcmp(b->type, "aufs") == 0 ||
strcmp(b->type, "overlayfs") == 0 ||
strcmp(b->type, "btrfs") == 0 ||
strcmp(b->type, "loop") == 0)
return true;
return false;
}
// unprivileged users can copy and snapshot dir, overlayfs,
// and loop. In particular, not zfs, btrfs, or lvm.
if (strcmp(t, "dir") == 0 ||
strcmp(t, "aufs") == 0 ||
strcmp(t, "overlayfs") == 0 ||
strcmp(t, "btrfs") == 0 ||
strcmp(t, "loop") == 0)
return true;
return false;
}
bool is_valid_bdev_type(const char *type)
{
if (strcmp(type, "dir") == 0 ||
strcmp(type, "btrfs") == 0 ||
strcmp(type, "aufs") == 0 ||
strcmp(type, "loop") == 0 ||
strcmp(type, "lvm") == 0 ||
strcmp(type, "nbd") == 0 ||
strcmp(type, "overlayfs") == 0 ||
strcmp(type, "rbd") == 0 ||
strcmp(type, "zfs") == 0)
return true;
return false;
}
...@@ -124,26 +124,6 @@ bool bdev_destroy(struct lxc_conf *conf); ...@@ -124,26 +124,6 @@ bool bdev_destroy(struct lxc_conf *conf);
/* callback function to be used with userns_exec_1() */ /* callback function to be used with userns_exec_1() */
int bdev_destroy_wrapper(void *data); int bdev_destroy_wrapper(void *data);
extern bool rootfs_is_blockdev(struct lxc_conf *conf);
/* Some helpers for lvm, rdb, and/or loop:
* Maybe they should move to a separate implementation and header-file
* (bdev_utils.{c,h}) which can be included in bdev.c?
*/
int blk_getsize(struct bdev *bdev, uint64_t *size);
int detect_fs(struct bdev *bdev, char *type, int len);
int do_mkfs_exec_wrapper(void *args);
int is_blktype(struct bdev *b);
int mount_unknown_fs(const char *rootfs, const char *target,
const char *options);
bool rootfs_is_blockdev(struct lxc_conf *conf);
/*
* these are really for qemu-nbd support, as container shutdown
* must explicitly request device detach.
*/
bool attach_block_device(struct lxc_conf *conf);
void detach_block_device(struct lxc_conf *conf);
bool is_valid_bdev_type(const char *type);
#endif // __LXC_BDEV_H #endif // __LXC_BDEV_H
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "bdev.h" #include "bdev.h"
#include "log.h" #include "log.h"
#include "lxcloop.h" #include "lxcloop.h"
#include "storage_utils.h"
#include "utils.h" #include "utils.h"
lxc_log_define(lxcloop, lxc); lxc_log_define(lxcloop, lxc);
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "config.h" #include "config.h"
#include "log.h" #include "log.h"
#include "lxclvm.h" #include "lxclvm.h"
#include "storage_utils.h"
#include "utils.h" #include "utils.h"
/* major()/minor() */ /* major()/minor() */
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "bdev.h" #include "bdev.h"
#include "log.h" #include "log.h"
#include "lxcnbd.h" #include "lxcnbd.h"
#include "storage_utils.h"
#include "utils.h" #include "utils.h"
lxc_log_define(lxcnbd, lxc); lxc_log_define(lxcnbd, lxc);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "bdev.h" #include "bdev.h"
#include "log.h" #include "log.h"
#include "storage_utils.h"
#include "utils.h" #include "utils.h"
lxc_log_define(lxcrbd, lxc); lxc_log_define(lxcrbd, lxc);
......
/*
* lxc: linux Container library
*
* Copyright © 2017 Canonical Ltd.
*
* Authors:
* Christian Brauner <christian.brauner@ubuntu.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <inttypes.h>
#include <libgen.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "bdev.h"
#include "log.h"
#include "lxcnbd.h"
#include "parse.h"
#include "storage_utils.h"
#include "utils.h"
#ifndef BLKGETSIZE64
#define BLKGETSIZE64 _IOR(0x12, 114, size_t)
#endif
lxc_log_define(storage_utils, lxc);
/* the bulk of this needs to become a common helper */
char *dir_new_path(char *src, const char *oldname, const char *name,
const char *oldpath, const char *lxcpath)
{
char *ret, *p, *p2;
int l1, l2, nlen;
nlen = strlen(src) + 1;
l1 = strlen(oldpath);
p = src;
/* if src starts with oldpath, look for oldname only after
* that path */
if (strncmp(src, oldpath, l1) == 0) {
p += l1;
nlen += (strlen(lxcpath) - l1);
}
l2 = strlen(oldname);
while ((p = strstr(p, oldname)) != NULL) {
p += l2;
nlen += strlen(name) - l2;
}
ret = malloc(nlen);
if (!ret)
return NULL;
p = ret;
if (strncmp(src, oldpath, l1) == 0) {
p += sprintf(p, "%s", lxcpath);
src += l1;
}
while ((p2 = strstr(src, oldname)) != NULL) {
strncpy(p, src, p2 - src); // copy text up to oldname
p += p2 - src; // move target pointer (p)
p += sprintf(p, "%s",
name); // print new name in place of oldname
src = p2 + l2; // move src to end of oldname
}
sprintf(p, "%s", src); // copy the rest of src
return ret;
}
/*
* attach_block_device returns true if all went well,
* meaning either a block device was attached or was not
* needed. It returns false if something went wrong and
* container startup should be stopped.
*/
bool attach_block_device(struct lxc_conf *conf)
{
char *path;
if (!conf->rootfs.path)
return true;
path = conf->rootfs.path;
if (!requires_nbd(path))
return true;
path = strchr(path, ':');
if (!path)
return false;
path++;
if (!attach_nbd(path, conf))
return false;
return true;
}
/*
* return block size of dev->src in units of bytes
*/
int blk_getsize(struct bdev *bdev, uint64_t *size)
{
int fd, ret;
char *path = bdev->src;
if (strcmp(bdev->type, "loop") == 0)
path = bdev->src + 5;
fd = open(path, O_RDONLY);
if (fd < 0)
return -1;
ret = ioctl(fd, BLKGETSIZE64, size); // size of device in bytes
close(fd);
return ret;
}
void detach_block_device(struct lxc_conf *conf)
{
if (conf->nbd_idx != -1)
detach_nbd_idx(conf->nbd_idx);
}
/*
* Given a bdev (presumably blockdev-based), detect the fstype
* by trying mounting (in a private mntns) it.
* @bdev: bdev to investigate
* @type: preallocated char* in which to write the fstype
* @len: length of passed in char*
* Returns length of fstype, of -1 on error
*/
int detect_fs(struct bdev *bdev, char *type, int len)
{
int p[2], ret;
size_t linelen;
pid_t pid;
FILE *f;
char *sp1, *sp2, *sp3, *line = NULL;
char *srcdev;
if (!bdev || !bdev->src || !bdev->dest)
return -1;
srcdev = bdev->src;
if (strcmp(bdev->type, "loop") == 0)
srcdev = bdev->src + 5;
ret = pipe(p);
if (ret < 0)
return -1;
if ((pid = fork()) < 0)
return -1;
if (pid > 0) {
int status;
close(p[1]);
memset(type, 0, len);
ret = read(p[0], type, len - 1);
close(p[0]);
if (ret < 0) {
SYSERROR("error reading from pipe");
wait(&status);
return -1;
} else if (ret == 0) {
ERROR("child exited early - fstype not found");
wait(&status);
return -1;
}
wait(&status);
type[len - 1] = '\0';
INFO("detected fstype %s for %s", type, srcdev);
return ret;
}
if (unshare(CLONE_NEWNS) < 0)
exit(1);
if (detect_shared_rootfs()) {
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) {
SYSERROR("Failed to make / rslave");
ERROR("Continuing...");
}
}
ret = mount_unknown_fs(srcdev, bdev->dest, bdev->mntopts);
if (ret < 0) {
ERROR("failed mounting %s onto %s to detect fstype", srcdev,
bdev->dest);
exit(1);
}
// if symlink, get the real dev name
char devpath[MAXPATHLEN];
char *l = linkderef(srcdev, devpath);
if (!l)
exit(1);
f = fopen("/proc/self/mounts", "r");
if (!f)
exit(1);
while (getline(&line, &linelen, f) != -1) {
sp1 = strchr(line, ' ');
if (!sp1)
exit(1);
*sp1 = '\0';
if (strcmp(line, l))
continue;
sp2 = strchr(sp1 + 1, ' ');
if (!sp2)
exit(1);
*sp2 = '\0';
sp3 = strchr(sp2 + 1, ' ');
if (!sp3)
exit(1);
*sp3 = '\0';
sp2++;
if (write(p[1], sp2, strlen(sp2)) != strlen(sp2))
exit(1);
exit(0);
}
exit(1);
}
int do_mkfs_exec_wrapper(void *args)
{
int ret;
char *mkfs;
char **data = args;
/* strlen("mkfs.")
* +
* strlen(data[0])
* +
* \0
*/
size_t len = 5 + strlen(data[0]) + 1;
mkfs = malloc(len);
if (!mkfs)
return -1;
ret = snprintf(mkfs, len, "mkfs.%s", data[0]);
if (ret < 0 || (size_t)ret >= len) {
free(mkfs);
return -1;
}
TRACE("executing \"%s %s\"", mkfs, data[1]);
execlp(mkfs, mkfs, data[1], (char *)NULL);
SYSERROR("failed to run \"%s %s \"", mkfs, data[1]);
return -1;
}
/*
* This will return 1 for physical disks, qemu-nbd, loop, etc right now only lvm
* is a block device.
*/
int is_blktype(struct bdev *b)
{
if (strcmp(b->type, "lvm") == 0)
return 1;
return 0;
}
int mount_unknown_fs(const char *rootfs, const char *target,
const char *options)
{
size_t i;
int ret;
struct cbarg {
const char *rootfs;
const char *target;
const char *options;
} cbarg = {
.rootfs = rootfs,
.target = target,
.options = options,
};
/*
* find the filesystem type with brute force:
* first we check with /etc/filesystems, in case the modules
* are auto-loaded and fall back to the supported kernel fs
*/
char *fsfile[] = {
"/etc/filesystems",
"/proc/filesystems",
};
for (i = 0; i < sizeof(fsfile) / sizeof(fsfile[0]); i++) {
if (access(fsfile[i], F_OK))
continue;
ret = lxc_file_for_each_line(fsfile[i], find_fstype_cb, &cbarg);
if (ret < 0) {
ERROR("failed to parse '%s'", fsfile[i]);
return -1;
}
if (ret)
return 0;
}
ERROR("failed to determine fs type for '%s'", rootfs);
return -1;
}
/*
* These are copied from conf.c. However as conf.c will be moved to using
* the callback system, they can be pulled from there eventually, so we
* don't need to pollute utils.c with these low level functions
*/
int find_fstype_cb(char *buffer, void *data)
{
struct cbarg {
const char *rootfs;
const char *target;
const char *options;
} *cbarg = data;
unsigned long mntflags;
char *mntdata;
char *fstype;
/* we don't try 'nodev' entries */
if (strstr(buffer, "nodev"))
return 0;
fstype = buffer;
fstype += lxc_char_left_gc(fstype, strlen(fstype));
fstype[lxc_char_right_gc(fstype, strlen(fstype))] = '\0';
DEBUG("trying to mount '%s'->'%s' with fstype '%s'", cbarg->rootfs,
cbarg->target, fstype);
if (parse_mntopts(cbarg->options, &mntflags, &mntdata) < 0) {
free(mntdata);
return 0;
}
if (mount(cbarg->rootfs, cbarg->target, fstype, mntflags, mntdata)) {
DEBUG("mount failed with error: %s", strerror(errno));
free(mntdata);
return 0;
}
free(mntdata);
INFO("mounted '%s' on '%s', with fstype '%s'", cbarg->rootfs,
cbarg->target, fstype);
return 1;
}
char *linkderef(char *path, char *dest)
{
struct stat sbuf;
ssize_t ret;
ret = stat(path, &sbuf);
if (ret < 0)
return NULL;
if (!S_ISLNK(sbuf.st_mode))
return path;
ret = readlink(path, dest, MAXPATHLEN);
if (ret < 0) {
SYSERROR("error reading link %s", path);
return NULL;
} else if (ret >= MAXPATHLEN) {
ERROR("link in %s too long", path);
return NULL;
}
dest[ret] = '\0';
return dest;
}
/*
* is an unprivileged user allowed to make this kind of snapshot
*/
bool unpriv_snap_allowed(struct bdev *b, const char *t, bool snap,
bool maybesnap)
{
if (!t) {
// new type will be same as original
// (unless snap && b->type == dir, in which case it will be
// overlayfs -- which is also allowed)
if (strcmp(b->type, "dir") == 0 ||
strcmp(b->type, "aufs") == 0 ||
strcmp(b->type, "overlayfs") == 0 ||
strcmp(b->type, "btrfs") == 0 ||
strcmp(b->type, "loop") == 0)
return true;
return false;
}
// unprivileged users can copy and snapshot dir, overlayfs,
// and loop. In particular, not zfs, btrfs, or lvm.
if (strcmp(t, "dir") == 0 ||
strcmp(t, "aufs") == 0 ||
strcmp(t, "overlayfs") == 0 ||
strcmp(t, "btrfs") == 0 ||
strcmp(t, "loop") == 0)
return true;
return false;
}
bool is_valid_bdev_type(const char *type)
{
if (strcmp(type, "dir") == 0 ||
strcmp(type, "btrfs") == 0 ||
strcmp(type, "aufs") == 0 ||
strcmp(type, "loop") == 0 ||
strcmp(type, "lvm") == 0 ||
strcmp(type, "nbd") == 0 ||
strcmp(type, "overlayfs") == 0 ||
strcmp(type, "rbd") == 0 ||
strcmp(type, "zfs") == 0)
return true;
return false;
}
int bdev_destroy_wrapper(void *data)
{
struct lxc_conf *conf = data;
if (setgid(0) < 0) {
ERROR("Failed to setgid to 0");
return -1;
}
if (setgroups(0, NULL) < 0)
WARN("Failed to clear groups");
if (setuid(0) < 0) {
ERROR("Failed to setuid to 0");
return -1;
}
if (!bdev_destroy(conf))
return -1;
return 0;
}
/*
* lxc: linux Container library
*
* Copyright © 2017 Canonical Ltd.
*
* Authors:
* Christian Brauner <christian.brauner@ubuntu.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LXC_STORAGE_UTILS_H
#define __LXC_STORAGE_UTILS_H
#include "config.h"
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include "conf.h"
struct bdev;
struct lxc_conf;
extern char *dir_new_path(char *src, const char *oldname, const char *name,
const char *oldpath, const char *lxcpath);
extern bool attach_block_device(struct lxc_conf *conf);
extern void detach_block_device(struct lxc_conf *conf);
extern int blk_getsize(struct bdev *bdev, uint64_t *size);
extern int detect_fs(struct bdev *bdev, char *type, int len);
extern int do_mkfs_exec_wrapper(void *args);
extern int is_blktype(struct bdev *b);
extern int mount_unknown_fs(const char *rootfs, const char *target,
const char *options);
extern int find_fstype_cb(char *buffer, void *data);
extern char *linkderef(char *path, char *dest);
extern bool unpriv_snap_allowed(struct bdev *b, const char *t, bool snap,
bool maybesnap);
extern bool is_valid_bdev_type(const char *type);
extern int bdev_destroy_wrapper(void *data);
#endif // __LXC_STORAGE_UTILS_H
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <dirent.h> #include <dirent.h>
#include "bdev.h" #include "bdev.h"
#include "bdev/storage_utils.h"
#include "parse.h" #include "parse.h"
#include "config.h" #include "config.h"
#include "confile.h" #include "confile.h"
......
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
#include "console.h" #include "console.h"
#include "error.h" #include "error.h"
#include "log.h" #include "log.h"
#include "lxccontainer.h"
#include "lxclock.h" #include "lxclock.h"
#include "lxcseccomp.h" #include "lxcseccomp.h"
#include "lxcutmp.h" #include "lxcutmp.h"
...@@ -71,6 +72,7 @@ ...@@ -71,6 +72,7 @@
#include "monitor.h" #include "monitor.h"
#include "namespace.h" #include "namespace.h"
#include "start.h" #include "start.h"
#include "storage_utils.h"
#include "sync.h" #include "sync.h"
#include "utils.h" #include "utils.h"
#include "lsm/lsm.h" #include "lsm/lsm.h"
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "bdev.h" #include "bdev.h"
#include "log.h" #include "log.h"
#include "lxc.h" #include "lxc.h"
#include "storage_utils.h"
#include "utils.h" #include "utils.h"
lxc_log_define(lxc_create_ui, lxc); lxc_log_define(lxc_create_ui, lxc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment