summaryrefslogtreecommitdiff
path: root/debootstrap.sh
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2017-08-28 16:04:22 -0400
committerAndrew Cady <d@jerkface.net>2017-08-28 16:04:22 -0400
commit965e80da28ff4075eeb4c13c5a6b9c17ab195bb0 (patch)
treec30b6a7805ed3f56cbb321b0177b2144912943f7 /debootstrap.sh
parent9b10ec82e7ba97df6d8ecc62bd7d50b930cd67a2 (diff)
debootstrap.sh: debootstrap wrapper
Diffstat (limited to 'debootstrap.sh')
-rwxr-xr-xdebootstrap.sh207
1 files changed, 207 insertions, 0 deletions
diff --git a/debootstrap.sh b/debootstrap.sh
new file mode 100755
index 0000000..e0bbc4b
--- /dev/null
+++ b/debootstrap.sh
@@ -0,0 +1,207 @@
1#!/bin/sh
2imgdir=./debootstrap
3warn() { printf 'Warning: %s\n' "$*" >&2; }
4die() { printf 'Error: %s\n' "$*" >&2; exit 1; }
5[ -d "$imgdir" ] || die "directory does not exist: $imgdir"
6
7usage()
8{
9 cat <<EOF >&2
10Usage:
11 $0 init <suite>
12 $0 new <suite> <new-name>
13 $0 clone <suite> <source-name> <new-name>
14 $0 chroot <suite> <name>
15 $0 list
16
17EOF
18 list
19 exit 1
20}
21
22list()
23{
24 local f suite name header_printed=
25 for f in $imgdir/*.btrfs; do
26 [ -e "$f" ] || continue
27 f=${f##*/}
28 f=${f%.btrfs}
29 case "$f" in *.*) continue ;; esac
30 suite=${f%%-*}
31 [ "$header_printed" ] || echo 'Existing initialized suites:'
32 printf ' %s\n' "$suite"
33 header_printed=y
34 done
35 header_printed=
36 for f in $imgdir/*.*.btrfs; do
37 [ -e "$f" ] || continue
38 f=${f##*/}
39 f=${f%.btrfs}
40 suite=${f%%-*}
41 name=${f##*.}
42 [ "$header_printed" ] || echo 'Existing images:'
43 printf ' %s %s\n' "$suite" "$name"
44 header_printed=y
45 done
46}
47
48validate_suite()
49{
50 case "$1" in
51 jessie|stretch|sid) return 0 ;;
52 *) return 1 ;;
53 esac
54}
55
56i_am_root() { [ "$(id -u)" = 0 ]; }
57
58btrfs_show_subvolume_id()
59{
60 local result path="$1"
61 result=$(btrfs subvolume show "$path" | sed -n -e 's/^[ \t]*Subvolume ID:[ \t]*//p; s/.*is toplevel subvolume/5/p')
62 if [ "$result" ]
63 then printf '%s\n' "$result"
64 else false
65 fi
66}
67
68mkfs_btrfs()
69{
70 local target="$1"
71 mountpoint="$2" # can't be local because of trap
72
73 mkfs.btrfs "$target" || die "mkfs.btrfs failed"
74 mount -t btrfs "$target" "$mountpoint" || die "mount failed"
75 trap 'umount "$mountpoint"' EXIT
76
77 btrfs subvolume create "$mountpoint"/root || die "command 'btrfs subvolume create' failed"
78 mkdir -p "$mountpoint"/root/var/cache/apt
79 btrfs subvolume create "$mountpoint"/root/var/cache/apt/archives || die "command 'btrfs subvolume create' failed"
80 subvol_id=$(btrfs_show_subvolume_id "$mountpoint"/root) || die "could not find btrfs subvolume name"
81 btrfs subvolume set-default "$subvol_id" "$mountpoint" || die "command 'btrfs subvolume set-default' failed"
82
83 trap - EXIT
84 umount "$mountpoint"
85}
86
87suite_to_basename()
88{
89 suite=$1
90 variant=minbase
91 arch=$(dpkg-architecture -q DEB_HOST_ARCH) || die "command 'dpkg-architecture' failed"
92
93 printf '%s\n' "$suite-$variant-$arch.btrfs"
94}
95
96suite_name_to_imagename()
97{
98 suite=$1
99 name=$2
100 variant=minbase
101 arch=$(dpkg-architecture -q DEB_HOST_ARCH) || die "command 'dpkg-architecture' failed"
102
103 printf '%s/%s-%s-%s.%s.btrfs\n' "$imgdir" "$suite" "$variant" "$arch" "$name"
104}
105
106chroot_image()
107{
108 suite=$1
109 name=$2
110 [ "$suite" -a "$name" ] || usage
111 imagename=$(suite_name_to_imagename "$suite" "$name")
112 [ -e "$imagename" ] || die "no such file: $imagename"
113 [ -d "$imagename".mnt ] || mkdir "$imagename".mnt || die "mkdir"
114 mountpoint -q "$imagename".mnt || mount "$imagename" "$imagename".mnt || die "mount"
115
116 unshare -f -m -p \
117 chroot "$imagename".mnt \
118 /bin/sh -c 'mount -t proc proc /proc; mount -t devpts devpts /dev/pts; exec /bin/bash'
119
120 r=$?
121 umount "$imagename".mnt
122 rmdir "$imagename".mnt
123 return $r
124}
125
126clone()
127{
128 suite=$1
129 source_name=$2
130 name=$3
131 [ "$suite" -a "$name" -a "$source_name" ] || usage
132 image_basename=$imgdir/$(suite_to_basename "$suite") || die 'suite_to_basename'
133
134 source=${image_basename%.btrfs}.$source_name.btrfs
135 dest=${image_basename%.btrfs}.$name.btrfs
136
137 [ -e "$source" ] || die "source image not found -- run '$0 new $suite $source_name' to create it"
138 [ ! -e "$dest" ] || die "file exists: $dest"
139 cp --reflink=always "$source" "$dest"
140}
141
142new()
143{
144 suite=$1
145 name=$2
146 [ "$suite" -a "$name" ] || usage
147 image_basename=$imgdir/$(suite_to_basename "$suite") || die 'suite_to_basename'
148
149 source=$image_basename
150 dest=${source%.btrfs}.$name.btrfs
151
152 [ -e "$source" ] || die "source image not found -- run '$0 init $suite' to create it"
153 [ ! -e "$dest" ] || die "file exists: $dest"
154 cp --reflink=always "$source" "$dest"
155}
156
157init()
158{
159 suite=$1
160 variant=minbase
161 arch=$(dpkg-architecture -q DEB_HOST_ARCH) || die "command 'dpkg-architecture' failed"
162
163 size=1G # truncate(1) format
164
165 i_am_root || die 'you are not root'
166
167 validate_suite "$suite" || die "invalid suite: '$suite'"
168
169 target=$imgdir/$suite-$variant-$arch.btrfs
170 [ -e "$target" ] && return
171 [ -e "$target".tmp ] && die "refusing to overwrite existing temporary file: $target.tmp"
172
173 [ -d "$target".mnt ] || mkdir "$target".mnt || die "could not create directory $target.mnt"
174
175 truncate -s "$size" "$target".tmp || die "truncate failed"
176 mkfs_btrfs "$target".tmp "$target".mnt || die "btrfs filesystem creation failed"
177 mount -t btrfs "$target".tmp "$target".mnt || die "mount failed"
178 trap 'umount "$target.mnt"' EXIT
179
180 debootstrap_efficiently "$arch" "$variant" "$suite" \
181 "$target".mnt "${target%.btrfs}".debs.txt || die "debootstrap failed"
182
183 if umount "$target".mnt
184 then trap - EXIT
185 else warn "umount failed"
186 fi
187
188 mv "$target".tmp "$target"
189}
190
191debootstrap_efficiently()
192{
193 local arch="$1" variant="$2" suite="$3" target="$4" savedebs="$5"
194 set -- --arch "$arch" --variant "$variant" "$suite" "$target"
195 debs=$(debootstrap --print-debs --keep-debootstrap-dir "$@" | tee "$savedebs") || die "debootstrap failed"
196 for deb in $debs; do
197 cp /var/cache/apt/archives/${deb}_* $target/var/cache/apt/archives/
198 done
199
200 debootstrap "$@" || die "debootstrap failed"
201}
202
203case "$1" in
204 init|new|clone|list) cmd=$1; shift; $cmd "$@" ;;
205 chroot) cmd=chroot_image; shift; $cmd "$@" ;;
206 *) usage ;;
207esac