diff --git a/README.md b/README.md index 36ad4a6..529ade8 100644 --- a/README.md +++ b/README.md @@ -348,9 +348,12 @@ processes inside a user namespace. If they don't start with a slash, **`source`** and **`target`** are interpreted as paths relative to ccon's [current working -directory][getcwd.3]. If **`target`** does not exist, ccon will -attempt to create it by calling [`mkdir`][mkdir.3p], making multiple -calls if necessary. +directory][getcwd.3]. + +If **`target`** does not exist, ccon will attempt to create it by +calling [`mkdir`][mkdir.3p], making multiple calls if necessary. If +**`source`** is set to a non-directory and **`target`** does not exit, +ccon will create an empty file at **`target`** to mount over. In addition to the usual types supported by [`mount`][mount.2], ccon supports a `pivot-root` **`type`** that invokes the diff --git a/ccon.c b/ccon.c index a04383d..e90fb4f 100644 --- a/ccon.c +++ b/ccon.c @@ -123,6 +123,7 @@ static char **json_array_of_strings_value(json_t * array); static int close_pipe(int pipe_fd[]); static int splice_pseudoterminal_master(int *master, int *slave); static int mkdir_all(const char *path, mode_t mode); +static int mkfile_all(const char *path, mode_t dir_mode, mode_t file_mode); int main(int argc, char **argv) { @@ -2137,12 +2138,13 @@ static int get_mount_flag(const char *name, unsigned long *flag) static int handle_mounts(json_t * config) { + struct stat buf; json_t *namespaces, *mt_ns, *mounts, *mt, *v1, *v2; const char *source, *target, *type, *data, *flag; char cwd[MAX_PATH], full_source[MAX_PATH], full_target[MAX_PATH]; unsigned long flags, f; size_t i, j; - int size; + int size, mkdir; namespaces = json_object_get(config, "namespaces"); if (!namespaces) { @@ -2253,7 +2255,19 @@ static int handle_mounts(json_t * config) return 1; } } else { - if (mkdir_all(target, 0777) == -1) { + mkdir = 1; + if (source) { + if (stat(source, &buf) == -1) { + PERROR("stat"); + return 1; + } + mkdir = S_ISDIR(buf.st_mode); + } + if (mkdir) { + if (mkdir_all(target, 0777) == -1) { + return 1; + } + } else if (mkfile_all(target, 0777, 0666) == -1) { return 1; } @@ -2620,7 +2634,7 @@ static int mkdir_all(const char *path, mode_t mode) err = -1; goto cleanup; } - LOG("create directory %s\n", path);; + LOG("create directory %s\n", path); if (mkdir(path, mode) == -1) { PERROR("mkdir"); err = -1; @@ -2639,3 +2653,37 @@ static int mkdir_all(const char *path, mode_t mode) } return err; } + +static int mkfile_all(const char *path, mode_t dir_mode, mode_t file_mode) +{ + char *path_copy = NULL, *dir = NULL; + int fd = -1, err = 0; + + path_copy = strdup(path); + if (path_copy == NULL) { + PERROR("strdup"); + err = -1; + goto cleanup; + } + dir = dirname(path_copy); + if (mkdir_all(dir, dir_mode) == -1) { + err = -1; + goto cleanup; + } + LOG("create file %s\n", path); + fd = open(path, O_CREAT | O_RDONLY, file_mode); + if (fd == -1) { + PERROR("mkdir_all open"); + } + + cleanup: + if (fd >= 0) { + if (close(fd) == -1) { + PERROR("close mkdir_all descriptor"); + } + } + if (path_copy != NULL) { + free(path_copy); + } + return err; +} diff --git a/test/t2002-mount-namespace.t b/test/t2002-mount-namespace.t index 64ad2c0..2b9665d 100755 --- a/test/t2002-mount-namespace.t +++ b/test/t2002-mount-namespace.t @@ -156,4 +156,51 @@ test_expect_success BUSYBOX,ID 'Test mount namespace creates destination directo test_cmp expected actual " +test_expect_success BUSYBOX,ID 'Test mount namespace creates destination files' " + mkdir -p rootfs && + ccon --verbose --config-string '{ + \"version\": \"0.5.0\", + \"namespaces\": { + \"user\": { + \"setgroups\": false, + \"uidMappings\": [ + { + \"containerID\": 0, + \"hostID\": $(id -u), + \"size\": 1 + } + ], + \"gidMappings\": [ + { + \"containerID\": 0, + \"hostID\": $(id -u), + \"size\": 1 + } + ] + }, + \"mount\": { + \"mounts\": [ + { + \"target\": \"/tmp/\", + \"type\": \"tmpfs\" + }, + { + \"source\": \"/bin/busybox\", + \"target\": \"/tmp/a/b/c\", + \"flags\": [ + \"MS_BIND\" + ] + } + ] + } + }, + \"process\": { + \"args\": [\"/bin/busybox\", \"ls\", \"/tmp/a/b\"], + \"host\": true + } + }' >actual && + echo c >expected && + test_cmp expected actual +" + test_done