| #!/usr/bin/env 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 |
| nb_extra_blocks=0 |
| nb_extra_inodes=0 |
| |
| while getopts :hb:B:i:I:r:d:o:G:R:l:u: OPT; do |
| case "${OPT}" in |
| h) help; exit 0;; |
| b) nb_blocks=${OPTARG};; |
| B) nb_extra_blocks=${OPTARG};; |
| i) nb_inodes=${OPTARG};; |
| I) nb_extra_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 |
| nb_inodes=$((nb_inodes+nb_extra_inodes)) |
| |
| # 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 |
| nb_blocks=$((nb_blocks+nb_extra_blocks)) |
| |
| # Upgrade to rev1 if needed |
| if [ ${rev} -ge 1 ]; then |
| tune2fs_O_opts+=",filetype,sparse_super" |
| 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=( -z -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 "$@" |