diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-10-29 02:12:35 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-10-29 02:12:35 +0800 |
| commit | 298dd9150ec8bc92af1bc1685726e1cd98d16b49 (patch) | |
| tree | dac8b52571821433123fb56ecc32b98fa229d996 | |
| parent | d8bfc59e2f5dc0728326ae7a143f8fdc113932e2 (diff) | |
| download | rutils-298dd9150ec8bc92af1bc1685726e1cd98d16b49.tar.gz rutils-298dd9150ec8bc92af1bc1685726e1cd98d16b49.tar.xz | |
Add configurable host directory bind mounts for container entry and command execution.
Signed-off-by: Richard Qin <richard.qin.x@gmail.com>
| -rw-r--r-- | debian-container/README | 9 | ||||
| -rwxr-xr-x | debian-container/debian-container.sh | 3 | ||||
| -rw-r--r-- | debian-container/lib/binds.sh | 64 | ||||
| -rw-r--r-- | debian-container/lib/common.sh | 3 | ||||
| -rw-r--r-- | debian-container/lib/container.sh | 44 | ||||
| -rw-r--r-- | debian-container/lib/mount.sh | 1 |
6 files changed, 120 insertions, 4 deletions
diff --git a/debian-container/README b/debian-container/README index 15fd95b..b0e11fe 100644 --- a/debian-container/README +++ b/debian-container/README @@ -27,11 +27,14 @@ sudo ./debian-container.sh resize test-3 --size 12G sudo ./debian-container.sh enter test-1 sudo ./debian-container.sh enter test-1 --network host +sudo ./debian-container.sh enter test-1 --mount /home/richard/project:/workspace:rw +sudo ./debian-container.sh enter test-1 --mount /home/richard/data:/data:ro 在容器中执行命令: sudo ./debian-container.sh exec test-1 -- uname -a sudo ./debian-container.sh exec test-1 --network host -- apt-get update +sudo ./debian-container.sh exec test-1 --mount /home/richard/project:/workspace:rw -- make -C /workspace 列出和查看容器: @@ -74,4 +77,10 @@ DEBIAN_INCLUDE 额外软件包,使用逗号分隔 DEBIAN_EXCLUDE 要排除的软件包,使用逗号分隔 DEBIAN_NETWORK 默认网络模式,none 或 host +挂载参数格式: + +HOST:CONTAINER[:ro|rw] + +目前只支持目录挂载,默认模式为 rw。 + 脚本实现按职责拆分在 lib/ 目录中,入口脚本只负责加载模块和分发命令。 diff --git a/debian-container/debian-container.sh b/debian-container/debian-container.sh index 78b0009..d6bfbaf 100755 --- a/debian-container/debian-container.sh +++ b/debian-container/debian-container.sh @@ -13,6 +13,7 @@ source "$DC_DIR/lib/common.sh" source "$DC_DIR/lib/network.sh" source "$DC_DIR/lib/storage.sh" source "$DC_DIR/lib/mount.sh" +source "$DC_DIR/lib/binds.sh" source "$DC_DIR/lib/container.sh" DC_ACTION=${1:-} @@ -52,7 +53,7 @@ case "$DC_ACTION" in init) dc_init "$@" ;; _init) dc_init_rootfs "$@" ;; enter) dc_enter "$@" ;; - _enter) dc_enter_rootfs ;; + _enter) dc_enter_rootfs "$@" ;; exec) dc_exec "$@" ;; _exec) dc_exec_rootfs "$@" ;; resize) dc_resize "$@" ;; diff --git a/debian-container/lib/binds.sh b/debian-container/lib/binds.sh new file mode 100644 index 0000000..7725bd8 --- /dev/null +++ b/debian-container/lib/binds.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (C) 2026 by Richard Qin + +DC_BIND_SPECS=() +DC_BIND_SOURCES=() +DC_BIND_TARGETS=() +DC_BIND_MODES=() + +dc_bind_add() { + local spec=$1 + local source + local target + local mode + local extra + + IFS=: read -r source target mode extra <<< "$spec" + [[ -n "$source" && -n "$target" && -z "$extra" ]] || dc_die "挂载参数格式无效:$spec" + [[ "$source" = /* && "$target" = /* ]] || dc_die "挂载源和目标必须使用绝对路径:$spec" + [[ "$target" != "/" ]] || dc_die '不能把目录挂载到容器根目录。' + + mode=${mode:-rw} + [[ "$mode" == rw || "$mode" == ro ]] || dc_die "挂载模式无效:$mode" + source=$(readlink -f -- "$source") + [[ -d "$source" ]] || dc_die "挂载源不是目录:$source" + target=$(readlink -m -- "$target") + [[ "$target" != "/" ]] || dc_die '不能把目录挂载到容器根目录。' + + DC_BIND_SPECS+=("$source:$target:$mode") + DC_BIND_SOURCES+=("$source") + DC_BIND_TARGETS+=("$target") + DC_BIND_MODES+=("$mode") +} + +dc_bind_load_options() { + while (($# > 0)); do + [[ "$1" == --mount ]] || dc_die '挂载参数格式无效。' + (($# >= 2)) || dc_die '--mount 需要一个挂载参数。' + dc_bind_add "$2" + shift 2 + done +} + +dc_bind_mount_all() { + local index + local target + + for index in "${!DC_BIND_SPECS[@]}"; do + target="$DC_ROOT${DC_BIND_TARGETS[index]}" + mkdir -p "$target" + mount --bind "${DC_BIND_SOURCES[index]}" "$target" + if [[ "${DC_BIND_MODES[index]}" == ro ]]; then + mount -o remount,bind,ro "$target" + fi + done +} + +dc_bind_cleanup() { + local index + + for ((index = ${#DC_BIND_TARGETS[@]} - 1; index >= 0; index--)); do + umount "$DC_ROOT${DC_BIND_TARGETS[index]}" 2>/dev/null || true + done +} diff --git a/debian-container/lib/common.sh b/debian-container/lib/common.sh index 9fd03b4..4102188 100644 --- a/debian-container/lib/common.sh +++ b/debian-container/lib/common.sh @@ -76,6 +76,9 @@ base-create 选项: 网络选项: --network MODE 网络模式:none(默认)或 host +挂载选项: + --mount SPEC 目录挂载:HOST:CONTAINER[:ro|rw] + 环境变量: DEBIAN_INSTANCES 实例目录(默认:当前目录/instances) DEBIAN_SIZE 新实例镜像大小(默认:3G) diff --git a/debian-container/lib/container.sh b/debian-container/lib/container.sh index eee6d76..5f040e3 100644 --- a/debian-container/lib/container.sh +++ b/debian-container/lib/container.sh @@ -181,6 +181,7 @@ dc_base_create() { dc_enter() { local network_mode=$DC_NETWORK_MODE + local -a bind_args=() while (($# > 0)); do case "$1" in @@ -189,6 +190,12 @@ dc_enter() { network_mode=$2 shift 2 ;; + --mount) + (($# >= 2)) || dc_die '--mount 需要一个挂载参数。' + dc_bind_add "$2" + bind_args+=(--mount "${DC_BIND_SPECS[$((${#DC_BIND_SPECS[@]} - 1))]}") + shift 2 + ;; --help|-h) dc_usage return 0 @@ -203,13 +210,15 @@ dc_enter() { dc_network_prepare "$network_mode" unshare --mount --uts --ipc --pid --fork --propagation private \ - "${DC_NETWORK_UNSHARE_ARGS[@]}" -- bash "$DC_SCRIPT" _enter "$DC_INSTANCE" + "${DC_NETWORK_UNSHARE_ARGS[@]}" -- bash "$DC_SCRIPT" _enter "$DC_INSTANCE" "${bind_args[@]}" } dc_enter_rootfs() { + dc_bind_load_options "$@" dc_mount_root trap dc_cleanup_mounts EXIT dc_mount_virtual_filesystems + dc_bind_mount_all hostname debian-container echo '已进入独立的 Debian 实验环境;输入 exit 可退出容器。' @@ -220,6 +229,7 @@ dc_enter_rootfs() { dc_exec() { local network_mode=$DC_NETWORK_MODE + local -a bind_args=() while (($# > 0)); do case "$1" in @@ -228,6 +238,12 @@ dc_exec() { network_mode=$2 shift 2 ;; + --mount) + (($# >= 2)) || dc_die '--mount 需要一个挂载参数。' + dc_bind_add "$2" + bind_args+=(--mount "${DC_BIND_SPECS[$((${#DC_BIND_SPECS[@]} - 1))]}") + shift 2 + ;; --) shift break @@ -248,17 +264,39 @@ dc_exec() { unshare --mount --uts --ipc --pid --fork --propagation private \ "${DC_NETWORK_UNSHARE_ARGS[@]}" -- \ - bash "$DC_SCRIPT" _exec "$DC_INSTANCE" "$@" + bash "$DC_SCRIPT" _exec "$DC_INSTANCE" "${bind_args[@]}" -- "$@" } dc_exec_rootfs() { + local -a command_args=() + + while (($# > 0)); do + case "$1" in + --mount) + (($# >= 2)) || dc_die '--mount 需要一个挂载参数。' + dc_bind_add "$2" + shift 2 + ;; + --) + shift + command_args=("$@") + break + ;; + *) + dc_die 'exec 子进程参数格式无效。' + ;; + esac + done + + (($# > 0)) || dc_die 'exec 需要一个命令。' dc_mount_root trap dc_cleanup_mounts EXIT dc_mount_virtual_filesystems + dc_bind_mount_all hostname debian-container setpriv --bounding-set=-sys_admin,-net_admin,-net_raw,-sys_module,-sys_boot,-sys_time,-sys_rawio,-sys_ptrace,-syslog -- \ - chroot "$DC_ROOT" "$@" + chroot "$DC_ROOT" "${command_args[@]}" } dc_status() { diff --git a/debian-container/lib/mount.sh b/debian-container/lib/mount.sh index 0b623c8..26403c7 100644 --- a/debian-container/lib/mount.sh +++ b/debian-container/lib/mount.sh @@ -3,6 +3,7 @@ # Copyright (C) 2026 by Richard Qin dc_cleanup_mounts() { + dc_bind_cleanup umount "$DC_ROOT/dev/pts" 2>/dev/null || true umount "$DC_ROOT/dev" 2>/dev/null || true umount "$DC_ROOT/proc" 2>/dev/null || true |
