Skip to content

Commit

Permalink
Merge pull request #15 from rhvgoyal/unmount-submounts
Browse files Browse the repository at this point in the history
Add capability to unmount submounts of a mount point on host
  • Loading branch information
rhatdan committed Aug 14, 2017
2 parents feadef4 + 6f0317a commit 299e781
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 14 deletions.
10 changes: 8 additions & 2 deletions oci-umount.conf
@@ -1,11 +1,17 @@
# This contains a list of paths on host which will be unmounted inside
# container. (If they are mounted inside container).

# If there is a "/*" at the end, that means only mounts underneath that
# mounts (submounts) will be unmounted but top level mount will remain
# in place.
/var/lib/docker/overlay2
/var/lib/docker/overlay
/var/lib/docker/devicemapper
/var/lib/docker/containers
/var/lib/docker/containers/*
/var/lib/docker-latest/overlay2
/var/lib/docker-latest/overlay
/var/lib/docker-latest/devicemapper
/var/lib/docker-latest/containers
/var/lib/docker-latest/containers/*
/var/lib/container/storage/lvm
/var/lib/container/storage/devicemapper
/var/lib/container/storage/overlay
133 changes: 121 additions & 12 deletions src/oci-umount.c
Expand Up @@ -18,6 +18,7 @@
#include <linux/limits.h>
#include <selinux/selinux.h>
#include <yajl/yajl_tree.h>
#include <ctype.h>

#include "config.h"

Expand All @@ -31,6 +32,8 @@
/* Basic mount info. For now we need only destination */
struct mount_info {
char *destination;
unsigned mntid;
unsigned parent_mntid;
};

/* Basic config mount info */
Expand All @@ -39,6 +42,11 @@ struct config_mount_info {
char *destination;
};

struct host_mount_info {
char *path;
bool submounts_only;
};

static inline void freep(void *p) {
free(*(void**) p);
}
Expand Down Expand Up @@ -68,6 +76,19 @@ static inline void free_mnt_info(struct mount_info **p) {
free(mi);
}

static inline void free_host_mounts(struct host_mount_info **p) {
unsigned i;
struct host_mount_info *hmi = *p;

if (hmi == NULL)
return;

for (i = 0; hmi[i].path; i++) {
free(hmi[i].path);
}
free(hmi);
}

/* Free an array of char pointers */
static inline void free_cptr_array(char ***p) {
unsigned i;
Expand Down Expand Up @@ -102,6 +123,7 @@ static inline void free_config_mounts(struct config_mount_info **p) {
#define _cleanup_close_ _cleanup_(closep)
#define _cleanup_fclose_ _cleanup_(fclosep)
#define _cleanup_mnt_info_ _cleanup_(free_mnt_info)
#define _cleanup_host_mounts_ _cleanup_(free_host_mounts)
#define _cleanup_cptr_array_ _cleanup_(free_cptr_array)
#define _cleanup_config_mounts_ _cleanup_(free_config_mounts)

Expand All @@ -121,6 +143,25 @@ DEFINE_CLEANUP_FUNC(yajl_val, yajl_tree_free)
#define BUFLEN 1024
#define CHUNKSIZE 4096

int iscomment(const char *line) {
int len = strlen(line);

for (int i = 0; i < len; i++) {
if (isspace(line[i]))
continue;

switch (line[i]) {
case '#':
return 1;
default:
return 0;
}
}

// treat blank lines as comments
return 1;
}

static void *grow_mountinfo_table(void *curr_table, size_t curr_sz, size_t new_sz) {
void *table;

Expand Down Expand Up @@ -170,10 +211,16 @@ static int parse_mountinfo(struct mount_info **info, size_t *sz)
while ((getline(&line, &len, fp)) != -1) {
char *token, *str = line, *dest;
int token_idx = 0;
unsigned mntid, parent_mntid;

mntid = parent_mntid = 0;
while ((token = strtok(str, " ")) != NULL) {
str = NULL;
token_idx++;
if (token_idx == 1)
mntid = atoi(token);
if (token_idx == 2)
parent_mntid = atoi(token);
if (token_idx != 5)
continue;

Expand All @@ -183,7 +230,10 @@ static int parse_mountinfo(struct mount_info **info, size_t *sz)
return -1;
}

mnt_table[table_idx++].destination = dest;
mnt_table[table_idx].destination = dest;
mnt_table[table_idx].mntid = mntid;
mnt_table[table_idx].parent_mntid = parent_mntid;
table_idx++;
if (table_idx == nr_elem) {
int new_sz_bytes = table_sz_bytes + elem_sz * 64;
mnt_table_temp = grow_mountinfo_table(mnt_table, table_sz_bytes, new_sz_bytes);
Expand Down Expand Up @@ -305,6 +355,50 @@ static int map_mount_host_to_container(const struct config_mount_info *config_mo
return nr_mapped;
}

/* Returns 0 on success, negative error otherwise */
static int unmount(char *umount_path, bool submounts_only, const struct mount_info *mnt_table, size_t table_sz)
{
int ret;
unsigned i;
unsigned mntid = 0;
bool found_mnt = false;

if (!submounts_only) {
ret = umount2(umount_path, MNT_DETACH);
if (!ret)
pr_pinfo("Unmounted: [%s]\n", umount_path);
else
pr_perror("Failed to unmount: [%s]\n", umount_path);
return ret;
}

/* Unmount submounts only */
for (i = 0; i < table_sz; i++) {
if (!strcmp(umount_path, mnt_table[i].destination)) {
found_mnt = true;
mntid = mnt_table[i].mntid;
break;
}
}

if (!found_mnt) {
pr_perror("Could not determine mount id of mountpoint: [%s]\n", umount_path);
return -1;
}

/* lazy unmount all direct submounts */
for (i = 0; i < table_sz; i++) {
if (mnt_table[i].parent_mntid != mntid)
continue;
ret = umount2(mnt_table[i].destination, MNT_DETACH);
if (!ret)
pr_pinfo("Unmounted submount: [%s]\n", mnt_table[i].destination);
else
pr_perror("Failed to unmount submount: [%s]. Skipping.\n", mnt_table[i].destination);
}
return 0;
}

static int prestart(const char *rootfs,
int pid,
const struct config_mount_info *config_mounts,
Expand All @@ -320,7 +414,7 @@ static int prestart(const char *rootfs,
char process_mnt_ns_fd[PATH_MAX];
char umount_path[PATH_MAX];
_cleanup_fclose_ FILE *fp = NULL;
_cleanup_cptr_array_ char **mounts_on_host = NULL;
_cleanup_host_mounts_ struct host_mount_info *mounts_on_host = NULL;
_cleanup_cptr_array_ char **mapped_paths = NULL;
int nr_umounts = 0;
_cleanup_free_ char *line = NULL;
Expand All @@ -330,12 +424,12 @@ static int prestart(const char *rootfs,
int i, ret, nr_mapped;

/* Allocate one extra element and keep it zero for cleanup function */
mounts_on_host = malloc((MAX_UMOUNTS + 1) * sizeof(char *));
mounts_on_host = malloc((MAX_UMOUNTS + 1) * sizeof(struct host_mount_info));
if (!mounts_on_host) {
pr_perror("Failed to malloc memory for mounts_on_host table\n");
return EXIT_FAILURE;
}
memset((void *)mounts_on_host, 0, (MAX_UMOUNTS + 1) * sizeof(char *));
memset((void *)mounts_on_host, 0, (MAX_UMOUNTS + 1) * sizeof(struct host_mount_info));

/* Allocate one extra element and keep it zero for cleanup function */
mapped_paths = malloc((MAX_MAPS + 1) * sizeof(char *));
Expand All @@ -358,21 +452,37 @@ static int prestart(const char *rootfs,
}

while ((read = getline(&line, &len, fp)) != -1) {
bool submounts_only = false;

/* Get rid of newline character at the end */
line[read - 1] ='\0';
read--;

if (iscomment(line))
continue;

if (nr_umounts == MAX_UMOUNTS) {
pr_perror("Exceeded maximum number of supported unmounts is %d\n", MAX_UMOUNTS);
return EXIT_FAILURE;
}

// If there is a "/*" at the end, only unmount submounts
if (read >= 2) {
if (line[read - 1] == '*' && line[read - 2] == '/') {
submounts_only = true;
line[read - 1] = '\0';
}
}

real_path = realpath(line, NULL);
if (!real_path) {
pr_pinfo("Failed to canonicalize path [%s]: %m. Skipping.", line);
continue;
}

mounts_on_host[nr_umounts++] = real_path;
mounts_on_host[nr_umounts].path = real_path;
mounts_on_host[nr_umounts].submounts_only = submounts_only;
nr_umounts++;
}

if (!nr_umounts)
Expand Down Expand Up @@ -406,14 +516,14 @@ static int prestart(const char *rootfs,
}

for (i = 0; i < nr_umounts; i++) {
nr_mapped = map_mount_host_to_container(config_mounts, config_mounts_len, mounts_on_host[i], mapped_paths, MAX_MAPS);
nr_mapped = map_mount_host_to_container(config_mounts, config_mounts_len, mounts_on_host[i].path, mapped_paths, MAX_MAPS);
if (nr_mapped < 0) {
pr_perror("Error while trying to map mount [%s] from host to conatiner. Skipping.\n", mounts_on_host[i]);
pr_perror("Error while trying to map mount [%s] from host to conatiner. Skipping.\n", mounts_on_host[i].path);
continue;
}

if (!nr_mapped) {
pr_pinfo("Could not find mapping for mount [%s] from host to conatiner. Skipping.\n", mounts_on_host[i]);
pr_pinfo("Could not find mapping for mount [%s] from host to conatiner. Skipping.\n", mounts_on_host[i].path);
continue;
}

Expand All @@ -425,12 +535,11 @@ static int prestart(const char *rootfs,
continue;
}

ret = umount2(umount_path, MNT_DETACH);
ret = unmount(umount_path, mounts_on_host[i].submounts_only, mnt_table, mnt_table_sz);
if (ret < 0) {
pr_perror("Failed to umount: [%s]", umount_path);
return EXIT_FAILURE;
pr_perror("Skipping unmount path: [%s]\n", umount_path);
continue;
}
pr_pinfo("Unmounted %s \n", umount_path);
}
free_char_ptr_array_entries(mapped_paths, nr_mapped);
}
Expand Down

0 comments on commit 299e781

Please sign in to comment.