aboutsummaryrefslogtreecommitdiff
path: root/package/mke2img/mke2img
blob: e93d8695ee0cec481ca555e858ff413d385f2e69 (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#!/bin/bash

# Buildroot wrapper to the collection of ext2/3/4 filesystem tools:
# - genext2fs, to generate ext2 filesystem images
# - tune2fs, to modify an ext2/3/4 filesystem (possibly in an image file)
# - e2fsck, to check and fix an ext2/3/4 filesystem (possibly in an image file)

set -e

main() {
    local OPT OPTARG
    local nb_blocks nb_inodes nb_res_blocks root_dir image gen rev label uuid
    local -a genext2fs_opts
    local -a tune2fs_opts
    local tune2fs_O_opts

    # Default values
    gen=2
    rev=1

    while getopts :hb:i:r:d:o:G:R:l:u: OPT; do
        case "${OPT}" in
        h)  help; exit 0;;
        b)  nb_blocks=${OPTARG};;
        i)  nb_inodes=${OPTARG};;
        r)  nb_res_blocks=${OPTARG};;
        d)  root_dir="${OPTARG}";;
        o)  image="${OPTARG}";;
        G)  gen=${OPTARG};;
        R)  rev=${OPTARG};;
        l)  label="${OPTARG}";;
        u)  uuid="${OPTARG}";;
        :)  error "option '%s' expects a mandatory argument\n" "${OPTARG}";;
        \?) error "unknown option '%s'\n" "${OPTARG}";;
        esac
    done

    # Sanity checks
    if [ -z "${root_dir}" ]; then
        error "you must specify a root directory with '-d'\n"
    fi
    if [ -z "${image}" ]; then
        error "you must specify an output image file with '-o'\n"
    fi
    case "${gen}:${rev}" in
    2:0|2:1|3:1|4:1)
        ;;
    3:0|4:0)
        error "revision 0 is invalid for ext3 and ext4\n"
        ;;
    *)  error "unknown ext generation '%s' and/or revision '%s'\n" \
               "${gen}" "${rev}"
        ;;
    esac

    # calculate needed inodes
    if [ -z "${nb_inodes}" ]; then
        nb_inodes=$(find "${root_dir}" | wc -l)
        nb_inodes=$((nb_inodes+400))
    fi

    # calculate needed blocks
    if [ -z "${nb_blocks}" ]; then
        # size ~= superblock, block+inode bitmaps, inodes (8 per block),
        # blocks; we scale inodes / blocks with 10% to compensate for
        # bitmaps size + slack
        nb_blocks=$(du -s -k "${root_dir}" |sed -r -e 's/[[:space:]]+.*$//')
        nb_blocks=$((500+(nb_blocks+nb_inodes/8)*11/10))
        if [ ${gen} -ge 3 ]; then
            # we add 1300 blocks (a bit more than 1 MiB, assuming 1KiB blocks)
            # for the journal
            # Note: I came to 1300 blocks after trial-and-error checks. YMMV.
            nb_blocks=$((nb_blocks+1300))
        fi
    fi

    # Upgrade to rev1 if needed
    if [ ${rev} -ge 1 ]; then
        tune2fs_O_opts+=",filetype"
    fi

    # Add a journal for ext3 and above
    if [ ${gen} -ge 3 ]; then
        tune2fs_opts+=( -j -J size=1 )
    fi

    # Add ext4 specific features
    if [ ${gen} -ge 4 ]; then
        tune2fs_O_opts+=",extents,uninit_bg,dir_index"
    fi

    # Add our -O options (there will be at most one leading comma, remove it)
    if [ -n "${tune2fs_O_opts}" ]; then
        tune2fs_opts+=( -O "${tune2fs_O_opts#,}" )
    fi

    # Add the label if specified
    if [ -n "${label}" ]; then
        tune2fs_opts+=( -L "${label}" )
    fi

    # Generate the filesystem
    genext2fs_opts=( -b ${nb_blocks} -N ${nb_inodes} -d "${root_dir}" )
    if [ -n "${nb_res_blocks}" ]; then
        genext2fs_opts+=( -m ${nb_res_blocks} )
    fi
    genext2fs "${genext2fs_opts[@]}" "${image}"

    # genext2fs does not generate a UUID, but fsck will whine if one
    # is missing, so we need to add a UUID.
    # Of course, this has to happen _before_ we run fsck.
    # Also, some ext4 metadata are based on the UUID, so we must
    # set it before we can convert the filesystem to ext4.
    # If the user did not specify a UUID, we generate a random one.
    # Although a random UUID may seem bad for reproducibility, there
    # already are so many things that are not reproducible in a
    # filesystem: file dates, file ordering, content of the files...
    tune2fs -U "${uuid:-random}" "${image}"

    # Upgrade the filesystem
    if [ ${#tune2fs_opts[@]} -ne 0 ]; then
        tune2fs "${tune2fs_opts[@]}" "${image}"
    fi

    # After changing filesystem options, running fsck is required
    # (see: man tune2fs). Running e2fsck in other cases will ensure
    # coherency of the filesystem, although it is not required.
    # 'e2fsck -pDf' means:
    #  - automatically repair
    #  - optimise and check for duplicate entries
    #  - force checking
    # Sending output to oblivion, as e2fsck can be *very* verbose,
    # especially with filesystems generated by genext2fs.
    # Exit codes 1 & 2 are OK, it means fs errors were successfully
    # corrected, hence our little trick with $ret.
    ret=0
    e2fsck -pDf "${image}" >/dev/null || ret=$?
    case ${ret} in
       0|1|2) ;;
       *)   errorN ${ret} "failed to run e2fsck on '%s' (ext%d)\n" \
                   "${image}" ${gen}
    esac
    printf "\n"
    trace "e2fsck was successfully run on '%s' (ext%d)\n" "${image}" ${gen}
    printf "\n"

    # Remove count- and time-based checks, they are not welcome
    # on embedded devices, where they can cause serious boot-time
    # issues by tremendously slowing down the boot.
    tune2fs -c 0 -i 0 "${image}"
}

help() {
    cat <<_EOF_
NAME
    ${my_name} - Create an ext2/3/4 filesystem image

SYNOPSIS
    ${my_name} [OPTION]...

DESCRIPTION
    Create ext2/3/4 filesystem image from the content of a directory.

    -b BLOCKS
        Create a filesystem of BLOCKS 1024-byte blocs. The default is to
        compute the required number of blocks.

    -i INODES
        Create a filesystem with INODES inodes. The default is to compute
        the required number of inodes.

    -r RES_BLOCKS
        Create a filesystem with RES_BLOCKS reserved blocks. The default
        is to reserve 0 block.

    -d ROOT_DIR
        Create a filesystem, using the content of ROOT_DIR as the content
        of the root of the filesystem. Mandatory.

    -o FILE
        Create the filesystem in FILE. Madatory.

    -G GEN -R REV
        Create a filesystem of generation GEN (2, 3 or 4), and revision
        REV (0 or 1). The default is to generate an ext2 revision 1
        filesystem; revision 0 is invalid for ext3 and ext4.

    -l LABEL
        Create a filesystem with label LABEL. The default is to not set
        a label.

    -u UUID
        Create filesystem with uuid UUID. The default is to set a random
        UUID.

  Exit status:
    0   if OK
    !0  in case of error
_EOF_
}

trace()  { local msg="${1}"; shift; printf "%s: ${msg}" "${my_name}" "${@}"; }
warn()   { trace "${@}" >&2; }
errorN() { local ret="${1}"; shift; warn "${@}"; exit ${ret}; }
error()  { errorN 1 "${@}"; }

my_name="${0##*/}"
main "$@"