diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index c6bdc8cfdfeb835f2f534d7bc2f32896adc948be..f08dbc2b1cef8668b70fa913b032224f97c37535 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -744,7 +744,7 @@ static const virLXCBasicMountInfo lxcBasicMounts[] = { }; -static bool lxcIsBasicMountLocation(const char *path) +bool lxcIsBasicMountLocation(const char *path) { size_t i; diff --git a/src/lxc/lxc_container.h b/src/lxc/lxc_container.h index e74a7d7bffd0dfb6f88f1abebebbb046720c9586..67292ab2ab25289a61c96fbba6efc3b1cc43fdc7 100644 --- a/src/lxc/lxc_container.h +++ b/src/lxc/lxc_container.h @@ -71,4 +71,6 @@ virArch lxcContainerGetAlt32bitArch(virArch arch); int lxcContainerChown(virDomainDefPtr def, const char *path); +bool lxcIsBasicMountLocation(const char *path); + #endif /* LXC_CONTAINER_H */ diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c index af9d34e24883ce049235322ecf45586a9671c623..66be361f3fd83727247adfbf9bbccad73afb34e6 100644 --- a/src/lxc/lxc_native.c +++ b/src/lxc/lxc_native.c @@ -21,10 +21,13 @@ */ #include +#include #include "internal.h" +#include "lxc_container.h" #include "lxc_native.h" #include "util/viralloc.h" +#include "util/virfile.h" #include "util/virlog.h" #include "util/virstring.h" #include "util/virconf.h" @@ -35,7 +38,9 @@ static virDomainFSDefPtr lxcCreateFSDef(int type, const char *src, - const char* dst) + const char* dst, + bool readonly, + unsigned long long usage) { virDomainFSDefPtr def; @@ -48,6 +53,8 @@ lxcCreateFSDef(int type, goto error; if (VIR_STRDUP(def->dst, dst) < 0) goto error; + def->readonly = readonly; + def->usage = usage; return def; @@ -56,15 +63,121 @@ lxcCreateFSDef(int type, return NULL; } +typedef struct _lxcFstab lxcFstab; +typedef lxcFstab *lxcFstabPtr; +struct _lxcFstab { + lxcFstabPtr next; + char *src; + char *dst; + char *type; + char *options; +}; + +static void +lxcFstabFree(lxcFstabPtr fstab) +{ + while (fstab) { + lxcFstabPtr next = NULL; + next = fstab->next; + + VIR_FREE(fstab->src); + VIR_FREE(fstab->dst); + VIR_FREE(fstab->type); + VIR_FREE(fstab->options); + VIR_FREE(fstab); + + fstab = next; + } +} + +static char ** lxcStringSplit(const char *string) +{ + char *tmp; + size_t i; + size_t ntokens = 0; + char **parts; + char **result = NULL; + + if (VIR_STRDUP(tmp, string) < 0) + return NULL; + + /* Replace potential \t by a space */ + for (i = 0; tmp[i]; i++) { + if (tmp[i] == '\t') + tmp[i] = ' '; + } + + if (!(parts = virStringSplit(tmp, " ", 0))) + goto error; + + /* Append NULL element */ + if (VIR_EXPAND_N(result, ntokens, 1) < 0) + goto error; + + for (i = 0; parts[i]; i++) { + if (STREQ(parts[i], "")) + continue; + + if (VIR_EXPAND_N(result, ntokens, 1) < 0) + goto error; + + if (VIR_STRDUP(result[ntokens-2], parts[i]) < 0) + goto error; + } + + VIR_FREE(tmp); + virStringFreeList(parts); + return result; + +error: + VIR_FREE(tmp); + virStringFreeList(parts); + virStringFreeList(result); + return NULL; +} + +static lxcFstabPtr +lxcParseFstabLine(char *fstabLine) +{ + lxcFstabPtr fstab = NULL; + char **parts; + + if (!fstabLine || VIR_ALLOC(fstab) < 0) + return NULL; + + if (!(parts = lxcStringSplit(fstabLine))) + goto error; + + if (!parts[0] || !parts[1] || !parts[2] || !parts[3]) + goto error; + + if (VIR_STRDUP(fstab->src, parts[0]) < 0 || + VIR_STRDUP(fstab->dst, parts[1]) < 0 || + VIR_STRDUP(fstab->type, parts[2]) < 0 || + VIR_STRDUP(fstab->options, parts[3]) < 0) + goto error; + + virStringFreeList(parts); + + return fstab; + +error: + lxcFstabFree(fstab); + virStringFreeList(parts); + return NULL; +} + static int lxcAddFSDef(virDomainDefPtr def, int type, const char *src, - const char *dst) + const char *dst, + bool readonly, + unsigned long long usage) { virDomainFSDefPtr fsDef = NULL; - if (!(fsDef = lxcCreateFSDef(type, src, dst))) + if (!(fsDef = lxcCreateFSDef(type, src, dst, readonly, usage))) goto error; if (VIR_EXPAND_N(def->fss, def->nfss, 1) < 0) @@ -95,12 +208,126 @@ lxcSetRootfs(virDomainDefPtr def, if (STRPREFIX(value->str, "/dev/")) type = VIR_DOMAIN_FS_TYPE_BLOCK; - if (lxcAddFSDef(def, type, value->str, "/") < 0) + if (lxcAddFSDef(def, type, value->str, "/", false, 0) < 0) return -1; return 0; } +static int +lxcConvertSize(const char *size, unsigned long long *value) +{ + char *unit = NULL; + + /* Split the string into value and unit */ + if (virStrToLong_ull(size, &unit, 10, value) < 0) + goto error; + + if (STREQ(unit, "%")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("can't convert relative size: '%s'"), + size); + return -1; + } else { + if (virScaleInteger(value, unit, 1, ULLONG_MAX) < 0) + goto error; + } + + return 0; + +error: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to convert size: '%s'"), + size); + return -1; +} + +static int +lxcAddFstabLine(virDomainDefPtr def, lxcFstabPtr fstab) +{ + const char *src = NULL; + char *dst = NULL; + char **options = virStringSplit(fstab->options, ",", 0); + bool readonly; + int type = VIR_DOMAIN_FS_TYPE_MOUNT; + unsigned long long usage = 0; + int ret = -1; + + if (!options) + return -1; + + if (fstab->dst[0] != '/') { + if (virAsprintf(&dst, "/%s", fstab->dst) < 0) + goto cleanup; + } else { + if (VIR_STRDUP(dst, fstab->dst) < 0) + goto cleanup; + } + + /* Check that we don't add basic mounts */ + if (lxcIsBasicMountLocation(dst)) { + ret = 0; + goto cleanup; + } + + if (STREQ(fstab->type, "tmpfs")) { + char *sizeStr = NULL; + size_t i; + type = VIR_DOMAIN_FS_TYPE_RAM; + + for (i = 0; options[i]; i++) { + if ((sizeStr = STRSKIP(options[i], "size="))) { + if (lxcConvertSize(sizeStr, &usage) < 0) + goto cleanup; + break; + } + } + if (!sizeStr) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing tmpfs size, set the size option")); + goto cleanup; + } + } else { + src = fstab->src; + } + + /* Do we have ro in options? */ + readonly = virStringArrayHasString(options, "ro"); + + if (lxcAddFSDef(def, type, src, dst, readonly, usage) < 0) + goto cleanup; + + ret = 1; + + cleanup: + VIR_FREE(dst); + virStringFreeList(options); + return ret; +} + +static int +lxcFstabWalkCallback(const char* name, virConfValuePtr value, void * data) +{ + int ret = 0; + lxcFstabPtr fstabLine; + virDomainDefPtr def = data; + + /* We only care about lxc.mount.entry lines */ + if (STRNEQ(name, "lxc.mount.entry")) + return 0; + + fstabLine = lxcParseFstabLine(value->str); + + if (!fstabLine) + return -1; + + if (lxcAddFstabLine(def, fstabLine) < 0) + ret = -1; + + lxcFstabFree(fstabLine); + return ret; +} + virDomainDefPtr lxcParseConfigString(const char *config) { @@ -119,6 +346,7 @@ lxcParseConfigString(const char *config) _("failed to generate uuid")); goto error; } + vmdef->id = -1; vmdef->mem.max_balloon = 64 * 1024; @@ -131,6 +359,8 @@ lxcParseConfigString(const char *config) * minimum required to make XML parsing pass */ vmdef->maxvcpus = 1; + vmdef->nfss = 0; + if (VIR_STRDUP(vmdef->os.type, "exe") < 0) goto error; @@ -146,6 +376,18 @@ lxcParseConfigString(const char *config) if (lxcSetRootfs(vmdef, properties) < 0) goto error; + /* Look for fstab: we shouldn't have it */ + if (virConfGetValue(properties, "lxc.mount")) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("lxc.mount found, use lxc.mount.entry lines instead")); + goto error; + } + + /* Loop over lxc.mount.entry to add filesystem devices for them */ + value = virConfGetValue(properties, "lxc.mount.entry"); + if (virConfWalk(properties, lxcFstabWalkCallback, vmdef) < 0) + goto error; + goto cleanup; error: diff --git a/tests/lxcconf2xmldata/lxcconf2xml-fstab.config b/tests/lxcconf2xmldata/lxcconf2xml-fstab.config new file mode 100644 index 0000000000000000000000000000000000000000..8c03c22c91bf4f558bbe9ab502fa66eb41b879df --- /dev/null +++ b/tests/lxcconf2xmldata/lxcconf2xml-fstab.config @@ -0,0 +1,37 @@ +# Template used to create this container: opensuse +# Template script checksum (SHA-1): 27307e0a95bd81b2c0bd82d6f87fdbe83be075ef + +lxc.network.type = veth +lxc.network.flags = up +lxc.network.link = virbr0 +lxc.network.hwaddr = 02:00:15:8f:05:c1 +lxc.network.name = eth0 + +#remove next line if host DNS configuration should not be available to container +lxc.mount = /var/lib/lxc/migrate_test/fstab +lxc.rootfs = /var/lib/lxc/migrate_test/rootfs +lxc.utsname = migrate_test +lxc.autodev=1 +lxc.tty = 2 +lxc.pts = 1024 +lxc.cap.drop = sys_module mac_admin mac_override mknod + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.aa_profile = unconfined + +lxc.cgroup.devices.deny = a +# /dev/null and zero +lxc.cgroup.devices.allow = c 1:3 rwm +lxc.cgroup.devices.allow = c 1:5 rwm +# consoles +lxc.cgroup.devices.allow = c 5:1 rwm +lxc.cgroup.devices.allow = c 5:0 rwm +lxc.cgroup.devices.allow = c 4:0 rwm +lxc.cgroup.devices.allow = c 4:1 rwm +# /dev/{,u}random +lxc.cgroup.devices.allow = c 1:9 rwm +lxc.cgroup.devices.allow = c 1:8 rwm +lxc.cgroup.devices.allow = c 136:* rwm +lxc.cgroup.devices.allow = c 5:2 rwm +# rtc +lxc.cgroup.devices.allow = c 254:0 rwm diff --git a/tests/lxcconf2xmldata/lxcconf2xml-simple.config b/tests/lxcconf2xmldata/lxcconf2xml-simple.config index 12428bb50080877cdd1c29af12555766c928950b..f75e8fc3d83a804c4e999691de1efa0bfdd1beac 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-simple.config +++ b/tests/lxcconf2xmldata/lxcconf2xml-simple.config @@ -8,13 +8,15 @@ lxc.network.hwaddr = 02:00:15:8f:05:c1 lxc.network.name = eth0 #remove next line if host DNS configuration should not be available to container +lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 +lxc.mount.entry = sysfs sys sysfs defaults 0 0 +lxc.mount.entry = tmpfs run tmpfs size=8m,mode=0755,nodev,nosuid 0 0 lxc.mount.entry = /etc/resolv.conf etc/resolv.conf none bind,ro 0 0 lxc.rootfs = /var/lib/lxc/migrate_test/rootfs lxc.utsname = migrate_test lxc.autodev=1 lxc.tty = 2 lxc.pts = 1024 -lxc.mount = /var/lib/lxc/migrate_test/fstab lxc.cap.drop = sys_module mac_admin mac_override mknod # When using LXC with apparmor, uncomment the next line to run unconfined: diff --git a/tests/lxcconf2xmldata/lxcconf2xml-simple.xml b/tests/lxcconf2xmldata/lxcconf2xml-simple.xml index eebcb4ed5825833cdc33456143c3e051d4fb284a..fadbdcad1bc27ea2e7b26918f00467b7697c602b 100644 --- a/tests/lxcconf2xmldata/lxcconf2xml-simple.xml +++ b/tests/lxcconf2xmldata/lxcconf2xml-simple.xml @@ -17,5 +17,14 @@ + + + + + + + + + diff --git a/tests/lxcconf2xmltest.c b/tests/lxcconf2xmltest.c index 9a840f9c4eea7ca761ada81a4deac8a59b9285f7..c4b21ecb04e465622648a68339775af7d8797015 100644 --- a/tests/lxcconf2xmltest.c +++ b/tests/lxcconf2xmltest.c @@ -18,7 +18,8 @@ blankProblemElements(char *data) static int testCompareXMLToConfigFiles(const char *xml, - const char *configfile) + const char *configfile, + bool expectError) { int ret = -1; char *config = NULL; @@ -28,22 +29,26 @@ testCompareXMLToConfigFiles(const char *xml, if (virtTestLoadFile(configfile, &config) < 0) goto fail; - if (virtTestLoadFile(xml, &expectxml) < 0) - goto fail; - if (!(vmdef = lxcParseConfigString(config))) + vmdef = lxcParseConfigString(config); + if ((vmdef && expectError) || (!vmdef && !expectError)) goto fail; - if (!(actualxml = virDomainDefFormat(vmdef, 0))) - goto fail; + if (vmdef) { + if (!(actualxml = virDomainDefFormat(vmdef, 0))) + goto fail; - if (blankProblemElements(expectxml) < 0 || - blankProblemElements(actualxml) < 0) - goto fail; + if (virtTestLoadFile(xml, &expectxml) < 0) + goto fail; - if (STRNEQ(expectxml, actualxml)) { - virtTestDifference(stderr, expectxml, actualxml); - goto fail; + if (blankProblemElements(expectxml) < 0 || + blankProblemElements(actualxml) < 0) + goto fail; + + if (STRNEQ(expectxml, actualxml)) { + virtTestDifference(stderr, expectxml, actualxml); + goto fail; + } } ret = 0; @@ -56,21 +61,26 @@ fail: return ret; } +struct testInfo { + const char *name; + bool expectError; +}; + static int testCompareXMLToConfigHelper(const void *data) { int result = -1; - const char *name = data; + const struct testInfo *info = data; char *xml = NULL; char *config = NULL; if (virAsprintf(&xml, "%s/lxcconf2xmldata/lxcconf2xml-%s.xml", - abs_srcdir, name) < 0 || + abs_srcdir, info->name) < 0 || virAsprintf(&config, "%s/lxcconf2xmldata/lxcconf2xml-%s.config", - abs_srcdir, name) < 0) + abs_srcdir, info->name) < 0) goto cleanup; - result = testCompareXMLToConfigFiles(xml, config); + result = testCompareXMLToConfigFiles(xml, config, info->expectError); cleanup: VIR_FREE(xml); @@ -83,13 +93,17 @@ mymain(void) { int ret = EXIT_SUCCESS; -# define DO_TEST(name) \ - if (virtTestRun("LXC Native-2-XML " name, \ - testCompareXMLToConfigHelper, \ - name) < 0) \ - ret = EXIT_FAILURE - - DO_TEST("simple"); +# define DO_TEST(name, expectError) \ + do { \ + const struct testInfo info = { name, expectError }; \ + if (virtTestRun("LXC Native-2-XML " name, \ + testCompareXMLToConfigHelper, \ + &info) < 0) \ + ret = EXIT_FAILURE; \ + } while (0) + + DO_TEST("simple", false); + DO_TEST("fstab", true); return ret; }