從Ubuntu-base構建ubuntu rootfs系統(以x86_64和arm爲例)

版權聲明:本文爲本文爲博主原創文章,轉載請註明出處,博客地址:https://www.cnblogs.com/wsg1100/。若有錯誤,歡迎指正。
前端

1.介紹

ubuntu-base 是Ubuntu官方構建的ubuntu最小文件系統,包含debain軟件包管理器,基礎包大小一般只有幾十兆,其背後有整個ubuntu軟件源支持,ubuntu軟件通常穩定性比較好,基於ubuntu-base按需安裝Linux軟件,深度可定製......,經常使用於嵌入式rootfs構建。node

嵌入式常見的幾種文件系統構建方法:busybox、yocto、builroot,我以爲它們都不如Ubuntu方便,強大的包管系統,有強大的社區支持,能夠直接apt-get install來安裝新軟件包。本文介紹瞭如何基於Ubuntu-base構建完整的ubuntu 系統。對於安裝過archlinux或者構建過LFS的朋友,對該方式再熟悉不過了。linux

ubuntu支持不少架構,arm、X8六、powerpc、ppc等,本文主要基於X86_64爲例,arm、arm64也給出了簡單的製做步驟,其餘架構操做相似。sql

2.目的

從ubuntu最小文件系統ubuntu-base開始,構建一個具備X-windows的完整系統。shell

2.準備宿主系統

2.1 宿主系統需求

  • 宿主系統是一臺linux系統電腦,能夠是虛擬機,爲避免Linux不一樣發行版間的差別影響系統構建,推薦使用ubuntu,須要可以鏈接Ubuntu源(有網絡),構建時軟件需從源下載安裝。另外宿主系統硬件架構與目標系統一致(本文認爲二者屬同一架構,若是不一致須要使用使用qemu等模擬目標架構)。
  • ubuntu-base包,下載鏈接,裏面包含arm、X86等架構的Ubuntu全部版本的base包,base包是ubuntu的最小文件系統,大小約幾十M。這裏使用的是X86-64架構,Ubuntu16.04最新base包:ubuntu-base-16.04.6-base-amd64.tar.gz
  • 一個磁盤分區≥5GB(也可建立一個環回設備,分區後進行操做,方法自尋搜索),目標系統將構建到該分區上,根據目的自行預估所需磁盤大小,本文構建一個僅運行QT的文件系統,5GB足夠了。因爲直接下載安裝,因此對宿主系統存儲大小無要求。;
    本文構建一個X86-64架構,基於Ubuntu 16.04的自定義系統, 本文磁盤分區狀況以下:
/dev/sda1    #UEFI分區 500MB
/dev/sda2    #宿主系統根分區 20GB
/dev/sda3    #用來構建ubuntu-base 5GB

附:建立迴環文件並分區

環回(loopback)文件系統是Linux類系統中很是有趣的部分。咱們一般是在設備上(例如磁盤分區)建立文件系統。這些存儲設備可以以設備文件的形式來使用,好比 /dev/device_name。爲了使用存儲設備上的文件系統,咱們須要將其掛載到一些被稱爲掛載點(mount point)的目錄上。環迴文件系統是指那些在文件中而非物理設備中建立的文件系統。咱們能夠將這些文件做爲文件系統掛載到掛載點上。這實際上可讓咱們在物理磁盤上的文件中建立邏輯磁盤。ubuntu

咱們的目的:建立一個迴環文件模擬一個大小10GB的物理磁盤,並在該回環鏡像中分兩個區:vim

  • UEFI分區,大小500M,FAT32格式。
  • 根分區(/),大小剩餘大小,EXT4格式。
1. 建立鏡像文件

也就是建立一個大小10G的文件。windows

$dd if=/dev/zero of=ubuntu_base.img bs=1G count=10
記錄了10+0 的讀入
記錄了10+0 的寫出
10737418240 bytes (11 GB, 10 GiB) copied, 84.1916 s, 128 MB/s

你會發現建立好的文件大小超過了1GB。這是由於硬盤做爲塊設備,其分配存儲空間時是按照塊大小的整數倍來進行的。此時能夠直接對這個文件做爲一整個分區格式化並使用,操做以下。bash

mkfs命令將1GB的文件格式化成ext4文件系統:服務器

$mkfs.ext4 ubuntu_base.img

可以使用下面的命令就可看到已是文件系統了:

$ file ubuntu_base.img
loobackfile.img: Linux rev 1.0 ext4 filesystem data, UUID=3be1775c-8976-445d-9134-8daabb2bade7 (extents) (64bit) (large files) (huge files)

如今就能夠掛載環迴文件了:

$sudo  mkdir /mnt/loopback
$sudo mount -o loop ubuntu_base.img /mnt/loopback

-o loop用來掛載環迴文件系統。

這其實是一種快捷的掛載方法,咱們無需手動鏈接任何設備。可是在內部,這個環迴文件鏈接到了一個名爲/dev/loop1loop2的設備上。

咱們也能夠手動來操做:

$sudo losetup /dev/loop1 ubuntu_base.img
$sudo mount /dev/loop1 /mnt/loopback

使用下面的方法進行卸載(umount):

$sudo umount /mnt/loopback

也能夠用設備文件的路徑做爲umount命令的參數:

$sudo umount /dev/loop1

以上是將整個文件做爲一個分區使用的方法,但咱們的目的是在該文件內分多個區,繼續。

2. 創建分區

創建分區可使用fdiskparted工具,本人比較喜歡用parted,對於設置啓動分區很是方便。

$ sudo parted ubuntu_base.img
GNU Parted 3.2
使用 /home/work/loobackfile.img
歡迎使用 GNU Parted! 輸入 'help'可得到命令列表.
(parted)

新建UEFI分區:

對於UEFI啓動方式,首先要設置建立label爲msdos;

(parted) mklabel msdos

建立分區大小500M:

(parted)mkpart primary fat32 0 500MB

查看新建立的分區,該分區編號爲1:

(parted) p
Model:  (file)
磁盤 /home/work/ubuntu_base.img: 10.7GB
Sector size (logical/physical): 512B/512B
分區表:msdos
Disk Flags:

數字  開始:  End    大小   類型     文件系統  標誌
 1    512B    500MB  500MB  primary  fat32     lba

(parted)

將該分區設置爲boot(UEFI啓動)分區:

(parted) set 1 boot on
(parted) p
Model:  (file)
磁盤 /home/work/ubuntu_base.img: 10.7GB
Sector size (logical/physical): 512B/512B
分區表:msdos
Disk Flags:

數字  開始:  End    大小   類型     文件系統  標誌
 1    512B    500MB  500MB  primary  fat32     啓動, lba

(parted)

設置後經過p命令能夠看到,標誌處多了啓動(boot)標誌。

將剩餘空間建立根分區:

(parted) mkpart primary ext4  500MB 100%

到此兩個分區建立完畢,使用p查看,q保存退出:

(parted) p
Model:  (file)
磁盤 /home/work/ubuntu_base.img: 10.7GB
Sector size (logical/physical): 512B/512B
分區表:msdos
Disk Flags:

數字  開始:  End     大小    類型     文件系統  標誌
 1    512B    500MB   500MB   primary  fat32     啓動, lba
 2    500MB   10.7GB  10.2GB  primary  ext4      lba
3. 映射分區設備並格式化

鏡像文件已經被分爲兩個區,可是尚未格式化,要對內部的兩個分區進行格式化就須要先將兩分區掛載到設備。

有一個更快的方法能夠掛載鏡像中的全部分區——kpartx。它並無安裝默認在系統中,你得使用軟件包管理器進行安裝:

$sudo apt-get install kpartx

執行如下命令自動掛載鏡像文件中的分區:

$sudo kpartx -v -a ubuntu_base.img
add map loop0p1 (253:0): 0 976562 linear 7:0 1
add map loop0p2 (253:1): 0 19994624 linear 7:0 976896

這條命令在磁盤鏡像中的分區與/dev/mapper中的設備之間創建了映射,隨後即可以格式化/掛載這些設備。

格式化兩個分區:

$ sudo mkfs.fat -F 32 /dev/mapper/loop0p1  #loop0p1 迴環設備的分區1
$ sudo mkfs.ext4 /dev/mapper/loop0p2	#loop0p1 迴環設備的分區2
4. 掛載分區

格式換完成後就能夠掛載兩個分區:

$ sudo mkdir /mnt/loopback
$ sudo mount /dev/mapper/loop0p2  /mnt/loopback  #掛載根分區
$ sudo mkdir -p /mnt/loopback/boot/efi
$ sudo mount /dev/mapper/loop0p1  /mnt/loopback/boot/efi  #掛載uefi啓動分區

2.2 掛載新分區

將目標分區格式化後掛載到/mnt目錄,若是掛載的是其餘目錄後面的全部命令均需更改:

sudo mkfs.ext4 /dev/sda3
sudo mount /dev/sda3 /mnt

ubuntu-base-16.04.6-base-amd64.tar.gz解壓到/mnt目錄:

sudo tar -xpvf ubuntu-base-16.04.6-base-amd64.tar.gz -C /mnt

注意:須要保留ubuntu-base中的文件權限及全部者,解壓時須要root權限或者sudo操做,且使用-p參數保留權限。

2.3 配置目標Ubuntu源

ubuntu-base中有默認ubuntu官方源,若是鏈接上互聯網且訪問官方源速度不受限制的話不須要更換。配置文件/etc/apt/sources.list更換源,本文宿主系統與要構建的系統硬件架構及版本一致,因此直接拷貝便可

sudo cp /etc/apt/sources.list /mnt/etc/apt/

2.3 配置DNS

進入目標環境須要聯網,須要先配置DNS,拷貝宿主系統文件/etc/resolv.conf/mnt/etc/目錄下:

sudo cp /etc/resolv.conf /mnt/etc/

2.3 配置用戶建立默認配置文件

ubuntu-base默認只有root用戶,若是須要像普通ubuntu那樣可隨意建立普通用戶,須要向Ubuntu-bae裏添加用戶默認配置文件夾/etc/skel,該文件夾內包含用戶建立時的默認配置文件如.bashrc、.profile等,若沒有該文件夾,在構建出的文件系統中執行adduser添加的用戶會有各類問題,因此將宿主系統/etc/skel拷貝至ubuntu-base:

sudo cp  -R /etc/skel /mnt/etc/

2.3 進入chroot環境

方法1:使用原始的方法,來進入chroot環境
掛載和激活 /dev:一般激活 /dev 目錄下設備的方式是在 /dev 目錄掛載一個虛擬文件系統(好比 tmpfs),而後容許在檢測 到設備或打開設備時在這個虛擬文件系統裏動態建立設備節點。這個一般是在啓動過程當中由 udev 完成。因爲咱們的ubuntu-base新系統尚未 udev,也沒有被引導,有必要手動掛載和激活 /dev 這能夠經過綁定掛載宿主機系統的/dev 目錄來實現。綁定掛載是一種特殊的掛載模式,它容許在另外的位置建立某個目錄或掛載點的鏡像。運行下面的命令來實現:

sudo mount -v --bind /dev /mnt/dev

掛載虛擬文件系統:

sudo mount -vt devpts devpts /mnt/dev/pts -o gid=5,mode=620
sudo mount -vt proc proc /mnt/proc
sudo mount -vt sysfs sysfs /mnt/sys
sudo mount -vt tmpfs tmpfs /mnt/run

進入chroot環境:

chroot /mnt

方法2:使用arch-chroot

linux發行版archlinux提供了一個自動化chroot的腳本arch-chroot,包含自動配置DNS文件、自動掛載虛擬文件系統等操做,用來維護linux系統很是方便,chroot時無需掛載等操做直接執行:

sudo arch-chroot /mnt

arch-chroot是方法1的封裝,除此以外有會對目標系統進行檢測並預先配置,其源碼見附錄。

3.安裝軟件

3.1 安裝內核

chroot 後爲root用戶,直接執行操做:
更新軟件包列表並升級:

apt-get update
apt-get upgrade
apt-get locales
apt-get install linux-headers-4.4.0-164-generic  linux-image-4.4.0-164-generic linux-modules-4.4.0-164-generic
echo "LANG=en_US.UTF-8" > /etc/locale.conf

自動補全工具bash-completion.

apt-get install bash-completion
apt-get install grub-efi-amd64-bin

3.2 安裝X-window

apt-get   x-window-system-core
apt-get   qt5-default libsqlite3-dev

到此該系統可運行基本的圖形程序。

4.安裝各類桌面(可選)

按需ubuntu各類桌面環境,任選其一,也可同時安裝多個,經過systemd選擇開機啓動的登陸管理器來登陸對應的桌面。

GDM-GNOME登陸管理器;

SDDM - 基於QML的顯示管理器和KDM的後繼者; 推薦用於 Plasma和 LXQt;

XDM - X顯示管理器,支持XDMCP;

LightDM - 跨桌面顯示管理器,可使用任何工具包中編寫的各類前端,Ubuntu16.04默認使用該管理器。

4.1 安裝Lubuntu的定製LXDE

sudo apt-get install lubuntu-desktop

4.2 安裝gnome桌面

其登陸管理器爲gdm,須要先安裝xdn。

sudo apt-get install  gdm
sudo -y --no-install-recommends ubuntu-gnome-desktop

4.3 安裝xfce桌面環境

xfce 是一款輕量級桌面.其登陸管理器爲xdm,須要先安裝xdn。

apt-get install -y xdm

安裝桌面環境

apt-get install -y --no-install-recommends xubuntu-desktop

開機啓動桌面管理器。

systemctl enable xdm

4.4 安裝mate桌面

sudo  apt-get install  ubuntu-mate-core
sudo apt-get install  ubuntu-mate-desktop

.......

5.arm構建簡要

arm下構建流程與上面相似,能夠不使用換回鏡像,直接使用一個空文件夾rootfs,在該文件夾內進行Ubuntu rootfs構建,構建完成後再將該文件夾下的全部文件拷貝到SD卡已格式格式化後的rootfs分區內便可,也可直接掛載SD卡內的rooyfs分區操做。

目前通常arm的板子都支持從SD卡啓動,同時SD卡內有兩個分區,一個Fat32的啓動分區內存放u-boot,另外一個ext4分區爲根文件系統。對於nand flash啓動的板子,將roofs按文件系統類型製做燒寫鏡像,燒寫nandflash便可。

因爲arm板子的開放性,板子外設、接口等硬件資源不盡相同,不一樣的廠家區別很大,不像X86由硬件廠商經過BIOS(UEFI)屏蔽底層差別,兼容性強(不過如今arm這方面已改善,在arm服務器領域已與X86同樣使用UEFI標準了,樹莓派安裝win10就是UEFI應用的一個例子,此外設備樹也是借鑑了UFEI ACPI,關於UEFI,可經過https://www.zhihu.com/topic/19573354/top-answers瞭解)。因此完成rootfs構建後,還須要移植好Linux內核和u-boot、設備樹等。

5.1下載ubuntu-base

以ubuntu16爲例,下載arm架構的ubuntu-base壓縮包,能夠看到有幾類之後綴armXXX結尾的,它們的含義以下:

  • ubuntu-base-xx.xx-core--arm64.tar.gz 適用於64位arm架構,幾乎全部ARMv8-A都是64位處理器,例如ARM Cortex-A5三、 ARM Cortex-A5七、ARM Cortex-A7二、ARM Cortex-A7三、RM_Cortex-A76等。
  • ubuntu-base-xx.xx-core-armhf.tar.gz 適用於32位帶硬浮點arm處理器,hf(hard float),即帶有浮點單元 (FPU),主要用於ARMv7-A,例如[ARM Cortex-A五、ARM Cortex-A七、ARM Cortex-A八、ARM Cortex-A九、ARM Cortex-A十二、ARM Cortex-A1五、ARM Cortex-A17。

下面以armhf爲例。

5.3 安裝qemu

sudo apt-get install multistrap qemu qemu-user-static binfmt-support dpkg-cross

5.4 將ubuntu-base解壓

將ubuntu-base包解壓到準備的rootfs文件夾,這裏爲/mnt,下面命令根據實際狀況更換。

$sudo tar -xpvf ubuntu-base-16.04.4-base-armhf.tar.gz -C /mnt

拷貝qemu-arm-static到剛剛解壓出來的目錄/mnt/usr/bin/

$sudo cp /usr/bin/qemu-arm-static /mnt/usr/bin

如果arm64則拷貝qemu-aarch64-static

$sudo cp /usr/bin/qemu-arm64-static /mnt/usr/bin

5.4 chroot操做

接下來的一切都和2.3 進入chroot環境,一致了。更新源並安裝須要的軟件。

apt-get update
apt-get install net-tools vim bash-completion ...

也可經過chroot直接執行某個命令,例如修改root密碼,,其中/mnt是咱們的rootfs目錄:

$sudo chroot  /mnt  passwd

直接安裝軟件:

$sudo LC_ALL=C LANGUAGE=C LANG=C chroot /mnt apt-get install packagename

5.5 最後

安裝內核,將內核和設備樹保存到rootfs中的boot目錄,即/mnt/boot/下。nand flash除外。

與普通文件系統燒寫一致,製做燒寫鏡像,燒寫SD卡或nandflash。

附錄

arch-chroot源碼

請遵照相關開源協議。

#!/bin/bash

shopt -s extglob

# generated from util-linux source: libmount/src/utils.c
declare -A pseudofs_types=([anon_inodefs]=1
                           [autofs]=1
                           [bdev]=1
                           [binfmt_misc]=1
                           [cgroup]=1
                           [cgroup2]=1
                           [configfs]=1
                           [cpuset]=1
                           [debugfs]=1
                           [devfs]=1
                           [devpts]=1
                           [devtmpfs]=1
                           [dlmfs]=1
                           [fuse.gvfs-fuse-daemon]=1
                           [fusectl]=1
                           [hugetlbfs]=1
                           [mqueue]=1
                           [nfsd]=1
                           [none]=1
                           [pipefs]=1
                           [proc]=1
                           [pstore]=1
                           [ramfs]=1
                           [rootfs]=1
                           [rpc_pipefs]=1
                           [securityfs]=1
                           [sockfs]=1
                           [spufs]=1
                           [sysfs]=1
                           [tmpfs]=1)

# generated from: pkgfile -vbr '/fsck\..+' | awk -F. '{ print $NF }' | sort
declare -A fsck_types=([cramfs]=1
                       [exfat]=1
                       [ext2]=1
                       [ext3]=1
                       [ext4]=1
                       [ext4dev]=1
                       [jfs]=1
                       [minix]=1
                       [msdos]=1
                       [reiserfs]=1
                       [vfat]=1
                       [xfs]=1)

out() { printf "$1 $2\n" "${@:3}"; }
error() { out "==> ERROR:" "$@"; } >&2
warning() { out "==> WARNING:" "$@"; } >&2
msg() { out "==>" "$@"; }
msg2() { out "  ->" "$@";}
die() { error "$@"; exit 1; }

ignore_error() {
  "$@" 2>/dev/null
  return 0
}

in_array() {
  local i
  for i in "${@:2}"; do
    [[ $1 = "$i" ]] && return 0
  done
  return 1
}

chroot_add_mount() {
  mount "$@" && CHROOT_ACTIVE_MOUNTS=("$2" "${CHROOT_ACTIVE_MOUNTS[@]}")
}

chroot_maybe_add_mount() {
  local cond=$1; shift
  if eval "$cond"; then
    chroot_add_mount "$@"
  fi
}

chroot_setup() {
  CHROOT_ACTIVE_MOUNTS=()
  [[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap'
  trap 'chroot_teardown' EXIT

  chroot_add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev &&
  chroot_add_mount sys "$1/sys" -t sysfs -o nosuid,noexec,nodev,ro &&
  ignore_error chroot_maybe_add_mount "[[ -d '$1/sys/firmware/efi/efivars' ]]" \
      efivarfs "$1/sys/firmware/efi/efivars" -t efivarfs -o nosuid,noexec,nodev &&
  chroot_add_mount udev "$1/dev" -t devtmpfs -o mode=0755,nosuid &&
  chroot_add_mount devpts "$1/dev/pts" -t devpts -o mode=0620,gid=5,nosuid,noexec &&
  chroot_add_mount shm "$1/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev &&
  chroot_add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755 &&
  chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,strictatime,nodev,nosuid
}

chroot_teardown() {
  if (( ${#CHROOT_ACTIVE_MOUNTS[@]} )); then
    umount "${CHROOT_ACTIVE_MOUNTS[@]}"
  fi
  unset CHROOT_ACTIVE_MOUNTS
}

try_cast() (
  _=$(( $1#$2 ))
) 2>/dev/null

valid_number_of_base() {
  local base=$1 len=${#2} i=

  for (( i = 0; i < len; i++ )); do
    try_cast "$base" "${2:i:1}" || return 1
  done

  return 0
}

mangle() {
  local i= chr= out=
  local {a..f}= {A..F}=

  for (( i = 0; i < ${#1}; i++ )); do
    chr=${1:i:1}
    case $chr in
      [[:space:]\\])
        printf -v chr '%03o' "'$chr"
        out+=\\
        ;;
    esac
    out+=$chr
  done

  printf '%s' "$out"
}

unmangle() {
  local i= chr= out= len=$(( ${#1} - 4 ))
  local {a..f}= {A..F}=

  for (( i = 0; i < len; i++ )); do
    chr=${1:i:1}
    case $chr in
      \\)
        if valid_number_of_base 8 "${1:i+1:3}" ||
            valid_number_of_base 16 "${1:i+1:3}"; then
          printf -v chr '%b' "${1:i:4}"
          (( i += 3 ))
        fi
        ;;
    esac
    out+=$chr
  done

  printf '%s' "$out${1:i}"
}

optstring_match_option() {
  local candidate pat patterns

  IFS=, read -ra patterns <<<"$1"
  for pat in "${patterns[@]}"; do
    if [[ $pat = *=* ]]; then
      # "key=val" will only ever match "key=val"
      candidate=$2
    else
      # "key" will match "key", but also "key=anyval"
      candidate=${2%%=*}
    fi

    [[ $pat = "$candidate" ]] && return 0
  done

  return 1
}

optstring_remove_option() {
  local o options_ remove=$2 IFS=,

  read -ra options_ <<<"${!1}"

  for o in "${!options_[@]}"; do
    optstring_match_option "$remove" "${options_[o]}" && unset 'options_[o]'
  done

  declare -g "$1=${options_[*]}"
}

optstring_normalize() {
  local o options_ norm IFS=,

  read -ra options_ <<<"${!1}"

  # remove empty fields
  for o in "${options_[@]}"; do
    [[ $o ]] && norm+=("$o")
  done

  # avoid empty strings, reset to "defaults"
  declare -g "$1=${norm[*]:-defaults}"
}

optstring_append_option() {
  if ! optstring_has_option "$1" "$2"; then
    declare -g "$1=${!1},$2"
  fi

  optstring_normalize "$1"
}

optstring_prepend_option() {
  local options_=$1

  if ! optstring_has_option "$1" "$2"; then
    declare -g "$1=$2,${!1}"
  fi

  optstring_normalize "$1"
}

optstring_get_option() {
  local opts o

  IFS=, read -ra opts <<<"${!1}"
  for o in "${opts[@]}"; do
    if optstring_match_option "$2" "$o"; then
      declare -g "$o"
      return 0
    fi
  done

  return 1
}

optstring_has_option() {
  local "${2%%=*}"

  optstring_get_option "$1" "$2"
}

dm_name_for_devnode() {
  read dm_name <"/sys/class/block/${1#/dev/}/dm/name"
  if [[ $dm_name ]]; then
    printf '/dev/mapper/%s' "$dm_name"
  else
    # don't leave the caller hanging, just print the original name
    # along with the failure.
    print '%s' "$1"
    error 'Failed to resolve device mapper name for: %s' "$1"
  fi
}

fstype_is_pseudofs() {
  (( pseudofs_types["$1"] ))
}

fstype_has_fsck() {
  (( fsck_types["$1"] ))
}


usage() {
  cat <<EOF
usage: ${0##*/} chroot-dir [command]

    -h                  Print this help message
    -u <user>[:group]   Specify non-root user and optional group to use

If 'command' is unspecified, ${0##*/} will launch /bin/bash.

Note that when using arch-chroot, the target chroot directory *should* be a
mountpoint. This ensures that tools such as pacman(8) or findmnt(8) have an
accurate hierarchy of the mounted filesystems within the chroot.

If your chroot target is not a mountpoint, you can bind mount the directory on
itself to make it a mountpoint, i.e. 'mount --bind /your/chroot /your/chroot'.

EOF
}

chroot_add_resolv_conf() {
  local chrootdir=$1 resolv_conf=$1/etc/resolv.conf

  [[ -e /etc/resolv.conf ]] || return 0

  # Handle resolv.conf as a symlink to somewhere else.
  if [[ -L $chrootdir/etc/resolv.conf ]]; then
    # readlink(1) should always give us *something* since we know at this point
    # it's a symlink. For simplicity, ignore the case of nested symlinks.
    resolv_conf=$(readlink "$chrootdir/etc/resolv.conf")
    if [[ $resolv_conf = /* ]]; then
      resolv_conf=$chrootdir$resolv_conf
    else
      resolv_conf=$chrootdir/etc/$resolv_conf
    fi

    # ensure file exists to bind mount over
    if [[ ! -f $resolv_conf ]]; then
      install -Dm644 /dev/null "$resolv_conf" || return 1
    fi
  elif [[ ! -e $chrootdir/etc/resolv.conf ]]; then
    # The chroot might not have a resolv.conf.
    return 0
  fi

  chroot_add_mount /etc/resolv.conf "$resolv_conf" --bind
}

while getopts ':hu:' flag; do
  case $flag in
    h)
      usage
      exit 0
      ;;
    u)
      userspec=$OPTARG
      ;;
    :)
      die '%s: option requires an argument -- '\''%s'\' "${0##*/}" "$OPTARG"
      ;;
    ?)
      die '%s: invalid option -- '\''%s'\' "${0##*/}" "$OPTARG"
      ;;
  esac
done
shift $(( OPTIND - 1 ))

(( EUID == 0 )) || die 'This script must be run with root privileges'
(( $# )) || die 'No chroot directory specified'
chrootdir=$1
shift

[[ -d $chrootdir ]] || die "Can't create chroot on non-directory %s" "$chrootdir"

if ! mountpoint -q "$chrootdir"; then
  warning "$chrootdir is not a mountpoint. This may have undesirable side effects."
fi

chroot_setup "$chrootdir" || die "failed to setup chroot %s" "$chrootdir"
chroot_add_resolv_conf "$chrootdir" || die "failed to setup resolv.conf"

chroot_args=()
[[ $userspec ]] && chroot_args+=(--userspec "$userspec")
echo "${chroot_args[@]}"
echo  "$chrootdir"
echo  "$@"
SHELL=/bin/bash unshare --fork --pid chroot "${chroot_args[@]}" -- "$chrootdir" "$@"
相關文章
相關標籤/搜索