1.3: added -d option
This commit is contained in:
parent
3d39aed74c
commit
9ca3a2b640
15
README.md
15
README.md
@ -80,6 +80,7 @@ Options:
|
||||
VOIDNSRUN_DIR environment variable is used.
|
||||
-m <path>: Add bind mount. You can add up to 50 paths.
|
||||
-u <path>: Add undo bind mount. You can add up to 50 paths.
|
||||
-d <path>: Add /usr subdirectory bind mount.
|
||||
-U <path>: Path to voidnsundo. When this option is not present,
|
||||
VOIDNSUNDO_BIN environment variable is used.
|
||||
-i: Don't treat missing source or target for added mounts as error.
|
||||
@ -99,6 +100,12 @@ launching `xbps-install`, `xbps-remove` or `xbps-reconfigure`and using
|
||||
To bind something else, use the `-m` option. You can add up to 50 binds as of
|
||||
version 1.2.
|
||||
|
||||
To bind a subdirectory from the host `/usr`, use the `-d` option (available
|
||||
since version 1.3). For example, instead of installing fonts into the container
|
||||
and therefore duplicating them and wasting your disk space, you can bind-mount
|
||||
`/usr/share/fonts` from the host. The rest of `/usr/` will be from the glibc
|
||||
container.
|
||||
|
||||
There's also the `-u` option. It adds bind mounts of the **voidnsundo** binary
|
||||
inside the namespace. See more about this below in the **voidnsundo** bind mode
|
||||
section. Just like with the `-m` option, you can add up to 50 binds as of version
|
||||
@ -251,8 +258,8 @@ A: If you installed fonts on your main system, applications that run in the moun
|
||||
namespace can't see them because of custom `/usr` directory. You need to install
|
||||
them again into the container directory.
|
||||
|
||||
Some workaround to bind-mount `/usr/share/fonts` from the root system to the
|
||||
namespace may be introduced in future, if Linux will allow such hacks.
|
||||
Since 1.3, it's possible to bind-mount `/usr/share/fonts` or other directorires
|
||||
from the host to the mount namespace. Use the `-d` option for that.
|
||||
|
||||
## Security
|
||||
|
||||
@ -269,6 +276,10 @@ promise to pay $25 in Bitcoin. Contact me if you find something.
|
||||
|
||||
## Changelog
|
||||
|
||||
#### 1.3
|
||||
|
||||
- Added the `-d` option to bind mount subdirectories from the host `/usr`.
|
||||
|
||||
#### 1.2.1
|
||||
|
||||
- Minor code fixes, nothing serious.
|
||||
|
3
config.h
3
config.h
@ -1,13 +1,14 @@
|
||||
#ifndef VOIDNSRUN_CONFIG_H
|
||||
#define VOIDNSRUN_CONFIG_H
|
||||
|
||||
#define PROG_VERSION "1.2.1"
|
||||
#define PROG_VERSION "1.3"
|
||||
|
||||
#define USER_LISTS_MAX 50
|
||||
|
||||
#define CONTAINER_DIR_VAR "VOIDNSRUN_DIR"
|
||||
#define UNDO_BIN_VAR "VOIDNSUNDO_BIN"
|
||||
#define VOIDNSUNDO_NAME "voidnsundo"
|
||||
#define OLDROOT "/oldroot"
|
||||
|
||||
/* This path has not been made configurable and is hardcoded
|
||||
* here for security purposes. If you want to change it, change it
|
||||
|
13
utils.c
13
utils.c
@ -43,6 +43,19 @@ bool mkfile(const char *s)
|
||||
return true;
|
||||
}
|
||||
|
||||
mode_t getmode(const char *s)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(s, &st) == -1)
|
||||
return 0;
|
||||
return st.st_mode;
|
||||
}
|
||||
|
||||
bool startswith(const char *haystack, const char *needle)
|
||||
{
|
||||
return strncmp(haystack, needle, strlen(needle)) == 0;
|
||||
}
|
||||
|
||||
int send_fd(int sock, int fd)
|
||||
{
|
||||
struct msghdr msg = {0};
|
||||
|
2
utils.h
2
utils.h
@ -20,6 +20,8 @@ bool isdir(const char *s);
|
||||
bool isexe(const char *s);
|
||||
bool exists(const char *s);
|
||||
bool mkfile(const char *s);
|
||||
bool startswith(const char *haystack, const char *needle);
|
||||
mode_t getmode(const char *s);
|
||||
|
||||
int send_fd(int sock, int fd);
|
||||
int recv_fd(int sock);
|
||||
|
143
voidnsrun.c
143
voidnsrun.c
@ -35,6 +35,7 @@ void usage(const char *progname)
|
||||
" " CONTAINER_DIR_VAR " environment variable is used.\n"
|
||||
" -m <path>: Add bind mount. You can add up to %d paths.\n"
|
||||
" -u <path>: Add undo bind mount. You can add up to %d paths.\n"
|
||||
" -d <path>: Add /usr subdirectory bind mount.\n"
|
||||
" -U <path>: Path to " VOIDNSUNDO_NAME ". When this option is not present,\n"
|
||||
" " UNDO_BIN_VAR " environment variable is used.\n"
|
||||
" -i: Don't treat missing source or target for added mounts as error.\n"
|
||||
@ -44,10 +45,14 @@ void usage(const char *progname)
|
||||
USER_LISTS_MAX, USER_LISTS_MAX);
|
||||
}
|
||||
|
||||
size_t mount_dirs(const char *source_prefix, size_t source_prefix_len, struct strarray *targets)
|
||||
size_t mount_dirs(const char *source_prefix,
|
||||
size_t source_prefix_len,
|
||||
struct strarray *targets,
|
||||
struct intarray *created)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
int successful = 0;
|
||||
mode_t mode;
|
||||
for (size_t i = 0; i < targets->end; i++) {
|
||||
/* Check if it's safe to proceed. */
|
||||
if (source_prefix_len + strlen(targets->list[i]) >= PATH_MAX) {
|
||||
@ -65,8 +70,28 @@ size_t mount_dirs(const char *source_prefix, size_t source_prefix_len, struct st
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!exists(targets->list[i])) {
|
||||
if (created != NULL) {
|
||||
mode = getmode(buf);
|
||||
if (mode == 0) {
|
||||
ERROR("error: can't get mode for %s.\n", buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mkdir(targets->list[i], mode) == -1) {
|
||||
ERROR("error: failed to create mountpotint at %s: %s.\n",
|
||||
targets->list[i], strerror(errno));
|
||||
continue;
|
||||
} else
|
||||
intarray_append(created, i);
|
||||
} else {
|
||||
ERROR("error: mount dir %s does not exists.\n", buf);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isdir(targets->list[i])) {
|
||||
ERROR("error: mount point %s does not exists.\n", targets->list[i]);
|
||||
ERROR("error: mount point %s is not a directory.\n", targets->list[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -78,7 +103,9 @@ size_t mount_dirs(const char *source_prefix, size_t source_prefix_len, struct st
|
||||
return successful;
|
||||
}
|
||||
|
||||
size_t mount_undo(const char *source, const struct strarray *targets, struct intarray *created)
|
||||
size_t mount_undo(const char *source,
|
||||
const struct strarray *targets,
|
||||
struct intarray *created)
|
||||
{
|
||||
int successful = 0;
|
||||
for (size_t i = 0; i < targets->end; i++) {
|
||||
@ -135,12 +162,20 @@ int main(int argc, char **argv)
|
||||
struct strarray undo_mounts;
|
||||
strarray_alloc(&undo_mounts, USER_LISTS_MAX);
|
||||
|
||||
/* List of user-specified /usr subdirectories to mount. */
|
||||
struct strarray dir_mounts;
|
||||
strarray_alloc(&dir_mounts, USER_LISTS_MAX);
|
||||
|
||||
/* List of indexes of items in the undo_mounts array. See comments in
|
||||
* mount_undo() function for more info. */
|
||||
struct intarray to_unlink;
|
||||
intarray_alloc(&to_unlink, USER_LISTS_MAX);
|
||||
struct intarray created_undos;
|
||||
intarray_alloc(&created_undos, USER_LISTS_MAX);
|
||||
|
||||
while ((c = getopt(argc, argv, "vhm:r:u:U:iV")) != -1) {
|
||||
/* List of indexes of items in the dir_mounts array. */
|
||||
struct intarray created_dirs;
|
||||
intarray_alloc(&created_dirs, USER_LISTS_MAX);
|
||||
|
||||
while ((c = getopt(argc, argv, "vhm:r:u:U:iVd:")) != -1) {
|
||||
switch (c) {
|
||||
case 'v':
|
||||
printf("%s\n", PROG_VERSION);
|
||||
@ -170,6 +205,13 @@ int main(int argc, char **argv)
|
||||
ERROR_EXIT("error: only up to %lu user mounts allowed.\n",
|
||||
undo_mounts.size);
|
||||
break;
|
||||
case 'd':
|
||||
if (!startswith(optarg, "/usr/"))
|
||||
ERROR_EXIT("only subdirectories of /usr are allowed for bind mounting this way.\n");
|
||||
if (!strarray_append(&dir_mounts, optarg))
|
||||
ERROR_EXIT("error: only up to %lu dir mounts allowed.\n",
|
||||
dir_mounts.size);
|
||||
break;
|
||||
case '?':
|
||||
return 1;
|
||||
}
|
||||
@ -261,9 +303,28 @@ int main(int argc, char **argv)
|
||||
|
||||
/* Mount stuff from the container to the namespace. */
|
||||
/* First, mount what user asked us to mount. */
|
||||
if (mount_dirs(dir, dirlen, &user_mounts) < user_mounts.end && !ignore_missing)
|
||||
if (mount_dirs(dir, dirlen, &user_mounts, NULL) < user_mounts.end && !ignore_missing)
|
||||
ERROR_EXIT("error: some mounts failed.\n");
|
||||
|
||||
/* Then preserve original /usr at /oldroot if needed. */
|
||||
if (dir_mounts.end > 0) {
|
||||
mode_t mode = getmode("/usr");
|
||||
if (mode == 0)
|
||||
ERROR_EXIT("error: failed to get mode of /usr.\n");
|
||||
|
||||
if (mount("tmpfs", OLDROOT, "tmpfs", 0, "size=4k,mode=0700,uid=0,gid=0") == -1)
|
||||
ERROR_EXIT("mount: error mounting tmpfs in %s.\n", OLDROOT);
|
||||
|
||||
strcpy(buf, OLDROOT);
|
||||
strcat(buf, "/usr");
|
||||
if (mkdir(buf, mode) == -1)
|
||||
ERROR_EXIT("error: failed to mkdir %s: %s.\n", buf, strerror(errno));
|
||||
|
||||
if (mount("/usr", buf, NULL, MS_BIND|MS_REC, NULL) == -1)
|
||||
ERROR_EXIT("error: failed to mount /usr at %s: %s.",
|
||||
buf, strerror(errno));
|
||||
}
|
||||
|
||||
/* Then the necessary stuff. */
|
||||
struct strarray default_mounts;
|
||||
strarray_alloc(&default_mounts, 3);
|
||||
@ -272,11 +333,16 @@ int main(int argc, char **argv)
|
||||
strarray_append(&default_mounts, "/var");
|
||||
strarray_append(&default_mounts, "/etc");
|
||||
}
|
||||
if (mount_dirs(dir, dirlen, &default_mounts) < default_mounts.end)
|
||||
if (mount_dirs(dir, dirlen, &default_mounts, NULL) < default_mounts.end)
|
||||
ERROR_EXIT("error: some necessary mounts failed.\n");
|
||||
|
||||
/* Mount /usr subdirectories if needed. */
|
||||
if (dir_mounts.end > 0
|
||||
&& mount_dirs(OLDROOT, strlen(OLDROOT), &dir_mounts, &created_dirs) < dir_mounts.end)
|
||||
ERROR_EXIT("error: some dir mounts failed.\n");
|
||||
|
||||
/* Now lets do bind mounts of voidnsundo (if needed). */
|
||||
if (mount_undo(undo_bin, &undo_mounts, &to_unlink) < undo_mounts.end
|
||||
if (mount_undo(undo_bin, &undo_mounts, &created_undos) < undo_mounts.end
|
||||
&& !ignore_missing)
|
||||
ERROR_EXIT("error: some undo mounts failed.\n");
|
||||
|
||||
@ -393,17 +459,54 @@ end:
|
||||
if (dirptr != NULL)
|
||||
closedir(dirptr);
|
||||
|
||||
/* If we created some empty files to bind the voidnsundo utility,
|
||||
* delete them here. */
|
||||
if (to_unlink.end > 0 && (!forked || pid == 0)) {
|
||||
for (size_t i = 0; i < to_unlink.end; i++) {
|
||||
char *path = undo_mounts.list[to_unlink.list[i]];
|
||||
if (umount(path) == -1)
|
||||
DEBUG("umount(%s): %s\n", path, strerror(errno));
|
||||
if (unlink(path) == -1)
|
||||
ERROR("unlink(%s): %s\n", path, strerror(errno));
|
||||
else
|
||||
DEBUG("unlink(%s)\n", path);
|
||||
if (!forked || pid == 0) {
|
||||
/* If we created some empty files to bind the voidnsundo utility,
|
||||
* delete them here. */
|
||||
if (created_undos.end > 0) {
|
||||
for (size_t i = 0; i < created_undos.end; i++) {
|
||||
char *path = undo_mounts.list[created_undos.list[i]];
|
||||
if (umount(path) == -1)
|
||||
DEBUG("umount(%s): %s\n", path, strerror(errno));
|
||||
if (unlink(path) == -1)
|
||||
ERROR("unlink(%s): %s\n", path, strerror(errno));
|
||||
else
|
||||
DEBUG("unlink(%s)\n", path);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we had to create mount tmpfs to /oldroot and do other
|
||||
* dirty hacks related to /usr subdirs bind mounting, clean up here. */
|
||||
if (dir_mounts.end > 0) {
|
||||
for (size_t i = 0; i < dir_mounts.end; i++) {
|
||||
char *path = dir_mounts.list[i];
|
||||
if (umount(path) == -1)
|
||||
ERROR("umount(%s): %s\n", path, strerror(errno));
|
||||
}
|
||||
|
||||
/* If we created some empty dirs to use them as mountpoints for
|
||||
* bind mounts, delete them here. */
|
||||
if (created_dirs.end > 0) {
|
||||
for (size_t i = 0; i < created_dirs.end; i++) {
|
||||
char *path = dir_mounts.list[created_dirs.list[i]];
|
||||
if (rmdir(path) == -1)
|
||||
ERROR("rmdir(%s): %s\n", path, strerror(errno));
|
||||
else
|
||||
DEBUG("rmdir(%s)\n", path);
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(buf, OLDROOT);
|
||||
strcat(buf, "/usr");
|
||||
if (umount(buf) == -1)
|
||||
ERROR("umount(%s): %s\n", buf, strerror(errno));
|
||||
|
||||
/* This call always fails with EBUSY and I don't know why.
|
||||
* We can safely ignore any errors here (I hope) because
|
||||
* the mount namespace will be destroyed as soon as there
|
||||
* will be no more processes attached to it. */
|
||||
umount(OLDROOT);
|
||||
/*if (umount(OLDROOT) == -1)
|
||||
ERROR("umount(%s): %s\n", OLDROOT, strerror(errno));*/
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user