summaryrefslogtreecommitdiff
path: root/forkroot
blob: 1ab42440ddb6ae92bfce3caa2b923e5d01698ae7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/bin/bash
die() { printf 'Error: %s\n' "$*"; exit 1; }
if [ $UID != 0 ]
then
    exec sudo -- "$0" "$@" || die 'You are not root.'
fi

help()
{
    printf '%s\n' "Usage: $0 [-h|--help] [--] <target directory>"
}

unset force delete verbose
while [ $# -gt 0 ]
do
    case "$1" in
        -h|--help) help; exit;;
        --force) force=y ;;
        --delete-after) delete=y ;;
        -v) verbose=y ;;
        --) shift; break ;;
        -*) die unknown option ;;
        *) break;
    esac
    shift
done

[ $# = 1 ] || die 'You must specify a target directory to create.'

target=$1
root=$target/root
mkdir "$target" || die 'creating target directory'

btrfs subvolume snapshot / "$root"

verbosely()
{
    (
        if [ "$verbose" ]
        then set -x
        fi
        "$@"
    )
}

bind_it()
{
    verbosely mount --bind "$mountpoint" "$root"/"$mountpoint"
}

copy_mountpoint()
{
    [ "$1" -a "$2" ] || return

    [ -d "$2" ] && return
    [ -d "${2%/*}" ] || copy_mountpoint "${1%/*}" "${2%/*}"
    mkdir -p "$2"
    chown --reference "$1" "$2"
    chmod --reference "$1" "$2"
}

mounts=$(tempfile)
cat /proc/mounts > "$mounts"

while read mountdev mountpoint fstype fsoptions _
do
    case "$mountpoint" in
        /sys  |/proc  |/dev   |/usr | /usr/local) bind_it; continue ;;
        /sys/*|/proc/*|/dev/* ) continue ;;
    esac
    case "$PWD" in
        "$mountpoint"/*) bind_it; continue ;;
    esac

    case "$fstype" in
        tmpfs)
            case "$mountpoint" in
                /run/*) copy_mountpoint "$mountpoint" "$root"/"$mountpoint" ;;
            esac
            mount "$mountdev" -t "$fstype" -o "$fsoptions" "$root"/"$mountpoint" ;;
    esac
done < "$mounts"

tac "$mounts" |
while read mountdev mountpoint fstype fsoptions _
do
    case "$mountpoint" in "$root"/*) verbosely umount -l "$mountpoint";; esac
done

chroot "$root" /bin/bash

confirm()
{
    cat <<EOF
The following command will execute:



    $*



The data will be permanently lost.

EOF
    read -p "Continue?  [y/N]> " line
    case "$line" in
        [yY]) "$@"; return ;;
    esac
}

if [ "$delete" -a "$force" ]
then
    btrfs subvolume delete "$target"/root
    rmdir "$target"
elif [ "$delete" ]
then
    confirm btrfs subvolume delete "$target"/root
    rmdir "$target"
fi