#!/bin/bash # Create a TRX image from up to three source files (kernel, initramfs, rootfs), # and output it to stdout. (This is the format you flash linksys routers with.) # The TRX format is a simple (28 byte) header followed by the concatenation of # the files with each file zero padded to a multiple of 4 bytes, and then the # file as a whole padded up to 4k. Padding is done with zero bytes. # The tricky part is calculating the lengths and CRC for the header before # outputting the header, without screwing up the ability to pipe the output # somewhere. if test ! -f $1 || shell { test ! -z $2 && test ! -f $2 } || shell { test ! -z $3 && test ! -f $3 } { echo "Usage: trximg.sh file1 [file2 [file3]]" > !2 exit 1 } # Output $1 bytes of decimal number $2 as little endian binary data proc leout { global X := '0' global DATA := $2 # Loop through bytes, smallest first while [ $X -lt $1 ] { # Grab next byte global BYTE := $($DATA%) global DATA := $($DATA/) # Convert to octal (because that's what echo needs) global OCTAL := ''"" for i in [1 2 3] { global OCTAL := "$($BYTE%)"$OCTAL"" global BYTE := $($BYTE/) } # Emit byte and loop echo -ne "\0$OCTAL" global X := $($X+) global BYTE := $x } } # Print number of bytes required to round $2 up to a multiple of $1 proc padlen { echo $(($1-($2%$1))%) } # Print number $2 rounded up to $1 proc roundlen { echo $($2+$(padlen $1 $2) } # Return length of file $1 in bytes proc filelen { wc -c $1 | awk '{print $1}' } # Output $1 zero bytes proc zpad { test $1 -ne 0 && dd if=/dev/zero bs=$1 count=1 !2 >/dev/null } # Output file $2, followed by enough zero bytes to pad length up to $1 bytes proc zpad_file { test -z $2 && return cat $2 zpad $[padlen $1 $[filelen $2]] } # Output header. (Optionally just the part included in the CRC32). proc emit_header { if test -z $1 { echo -n "HDR0" # File ID magic leout 4 $LENGTH # Length of file (including this header) leout 4 $CRC32 # crc32 of all file data after this crc field } leout 2 0 # flags leout 2 1 # version leout 4 28 # Start of first file leout 4 $OFFSET2 # Start of second file leout 4 $OFFSET3 # Start of third file } # Calculate file offsets for the three arguments global TOTAL := $(28+$(roundlen 4 $(filelen "$1")) if test -z $2 { global OFFSET2 := '0' } else { global OFFSET2 := $TOTAL global TOTAL := $($TOTAL+$(roundlen 4 $(filelen "$2")) } if test -z $3 { global OFFSET3 := '0' } else { global OFFSET3 := $TOTAL global TOTAL := $($TOTAL+$(roundlen 4 $(filelen "$3")) } global LENGTH := $[roundlen 4096 $TOTAL] # Calculate the CRC value for the header global CRC32 := $[ shell { emit_header skip zpad_file 4 $1 zpad_file 4 $2 zpad_file 4 $3 zpad $[padlen 4096 $TOTAL] } | cksum -NILP] # Output the image to stdout emit_header zpad_file 4 $1 zpad_file 4 $2 zpad_file 4 $3 zpad $[padlen 4096 $TOTAL] (CommandList children: [ (If arms: [ (if_arm cond: [ (AndOr children: [ (C {(Lit_Other "[")} {(KW_Bang "!")} {(-f)} {(DQ ($ VSub_Number "$1"))} {(Lit_Other "]")}) (AndOr children: [ (Subshell child: (AndOr children: [ (C {(Lit_Other "[")} {(KW_Bang "!")} {(-z)} {(DQ ($ VSub_Number "$2"))} {(Lit_Other "]")} ) (C {(Lit_Other "[")} {(KW_Bang "!")} {(-f)} {(DQ ($ VSub_Number "$2"))} {(Lit_Other "]")} ) ] op_id: Op_DAmp ) spids: [48 76] ) (Subshell child: (AndOr children: [ (C {(Lit_Other "[")} {(KW_Bang "!")} {(-z)} {(DQ ($ VSub_Number "$3"))} {(Lit_Other "]")} ) (C {(Lit_Other "[")} {(KW_Bang "!")} {(-f)} {(DQ ($ VSub_Number "$3"))} {(Lit_Other "]")} ) ] op_id: Op_DAmp ) spids: [81 109] ) ] op_id: Op_DPipe ) ] op_id: Op_DPipe ) ] action: [ (SimpleCommand words: [{(echo)} {(DQ ("Usage: trximg.sh file1 [file2 [file3]]"))}] redirects: [(Redir op_id:Redir_GreatAnd fd:-1 arg_word:{(2)} spids:[120])] ) (C {(exit)} {(1)}) ] spids: [-1 111] ) ] spids: [-1 128] ) (FuncDef name: leout body: (BraceGroup children: [ (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:X) op:Equal rhs:{(0)} spids:[144])] spids: [144] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:DATA) op: Equal rhs: {($ VSub_Number "$2")} spids: [148] ) ] spids: [148] ) (While cond: [ (C {(Lit_Other "[")} {($ VSub_Name "$X")} {(-lt)} {($ VSub_Number "$1")} {(Lit_Other "]")}) ] body: (DoGroup children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:BYTE) op: Equal rhs: { (ArithSubPart anode: (ArithBinary op_id: Arith_Percent left: (ArithWord w:{($ VSub_Name "$DATA")}) right: (ArithWord w:{(Lit_Digits 256)}) ) spids: [180 184] ) } spids: [179] ) ] spids: [179] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:DATA) op: Equal rhs: { (ArithSubPart anode: (ArithBinary op_id: Arith_Slash left: (ArithWord w:{($ VSub_Name "$DATA")}) right: (ArithWord w:{(Lit_Digits 256)}) ) spids: [188 192] ) } spids: [187] ) ] spids: [187] ) (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:OCTAL) op:Equal rhs:{(DQ )} spids:[201])] spids: [201] ) (ForEach iter_name: i iter_words: [{(1)} {(2)} {(3)}] do_arg_iter: False body: (DoGroup children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:OCTAL) op: Equal rhs: { (ArithSubPart anode: (ArithBinary op_id: Arith_Percent left: (ArithWord w:{($ VSub_Name "$BYTE")}) right: (ArithWord w:{(Lit_Digits 8)}) ) spids: [223 227] ) (DQ ($ VSub_Name "$OCTAL")) } spids: [222] ) ] spids: [222] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:BYTE) op: Equal rhs: { (ArithSubPart anode: (ArithBinary op_id: Arith_Slash left: (ArithWord w:{($ VSub_Name "$BYTE")}) right: (ArithWord w:{(Lit_Digits 8)}) ) spids: [234 238] ) } spids: [233] ) ] spids: [233] ) ] spids: [219 241] ) spids: [211 -1] ) (C {(echo)} {(-ne)} { (DQ (EscapedLiteralPart token:) ($ VSub_Name "$OCTAL")) } ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:X) op: Equal rhs: { (ArithSubPart anode: (ArithBinary op_id: Arith_Plus left: (ArithWord w:{($ VSub_Name "$X")}) right: (ArithWord w:{(Lit_Digits 1)}) ) spids: [262 266] ) } spids: [261] ) ] spids: [261] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:BYTE) op: Equal rhs: {($ VSub_Name "$x")} spids: [269] ) ] spids: [269] ) ] spids: [171 273] ) ) ] spids: [141] ) spids: [135 140] ) (FuncDef name: padlen body: (BraceGroup children: [ (C {(echo)} { (ArithSubPart anode: (ArithBinary op_id: Arith_Percent left: (ArithBinary op_id: Arith_Minus left: (ArithWord w:{($ VSub_Number "$1")}) right: (ArithBinary op_id: Arith_Percent left: (ArithWord w:{($ VSub_Number "$2")}) right: (ArithWord w:{($ VSub_Number "$1")}) ) ) right: (ArithWord w:{($ VSub_Number "$1")}) ) spids: [293 305] ) } ) ] spids: [288] ) spids: [282 287] ) (FuncDef name: roundlen body: (BraceGroup children: [ (C {(echo)} { (ArithSubPart anode: (ArithBinary op_id: Arith_Plus left: (ArithWord w:{($ VSub_Number "$2")}) right: (ArithWord w: { (CommandSubPart command_list: (CommandList children: [ (C {(padlen)} {($ VSub_Number "$1")} {($ VSub_Number "$2")}) ] ) left_token: spids: [328 334] ) } ) ) spids: [325 335] ) } ) ] spids: [320] ) spids: [314 319] ) (FuncDef name: filelen body: (BraceGroup children: [ (Pipeline children: [(C {(wc)} {(-c)} {(DQ ($ VSub_Number "$1"))}) (C {(awk)} {(SQ <"{print $1}">)})] negated: False ) ] spids: [350] ) spids: [344 349] ) (FuncDef name: zpad body: (BraceGroup children: [ (AndOr children: [ (C {(Lit_Other "[")} {($ VSub_Number "$1")} {(-ne)} {(0)} {(Lit_Other "]")}) (SimpleCommand words: [ {(dd)} {(Lit_VarLike "if=") (/dev/zero)} {(Lit_VarLike "bs=") ($ VSub_Number "$1")} {(Lit_VarLike "count=") (1)} ] redirects: [(Redir op_id:Redir_Great fd:2 arg_word:{(/dev/null)} spids:[408])] ) ] op_id: Op_DAmp ) ] spids: [382] ) spids: [376 381] ) (FuncDef name: zpad_file body: (BraceGroup children: [ (AndOr children: [ (C {(Lit_Other "[")} {(-z)} {(DQ ($ VSub_Number "$2"))} {(Lit_Other "]")}) (ControlFlow token:) ] op_id: Op_DAmp ) (C {(cat)} {($ VSub_Number "$2")}) (C {(zpad)} { (CommandSubPart command_list: (CommandList children: [ (C {(padlen)} {($ VSub_Number "$1")} { (CommandSubPart command_list: (CommandList children: [(C {(filelen)} {(DQ ($ VSub_Number "$2"))})] ) left_token: spids: [454 460] ) } ) ] ) left_token: spids: [449 461] ) } ) ] spids: [424] ) spids: [418 423] ) (FuncDef name: emit_header body: (BraceGroup children: [ (If arms: [ (if_arm cond: [(C {(Lit_Other "[")} {(-z)} {(DQ ($ VSub_Number "$1"))} {(Lit_Other "]")})] action: [ (C {(echo)} {(-n)} {(DQ (HDR0))}) (C {(leout)} {(4)} {($ VSub_Name "$LENGTH")}) (C {(leout)} {(4)} {($ VSub_Name "$CRC32")}) ] spids: [-1 490] ) ] spids: [-1 525] ) (C {(leout)} {(2)} {(0)}) (C {(leout)} {(2)} {(1)}) (C {(leout)} {(4)} {(28)}) (C {(leout)} {(4)} {($ VSub_Name "$OFFSET2")}) (C {(leout)} {(4)} {($ VSub_Name "$OFFSET3")}) ] spids: [474] ) spids: [470 473] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:TOTAL) op: Equal rhs: { (ArithSubPart anode: (ArithBinary op_id: Arith_Plus left: (ArithWord w:{(Lit_Digits 28)}) right: (ArithWord w: { (CommandSubPart command_list: (CommandList children: [ (C {(roundlen)} {(4)} { (CommandSubPart command_list: (CommandList children: [(C {(filelen)} {(DQ ($ VSub_Number "$1"))})] ) left_token: spids: [594 600] ) } ) ] ) left_token: spids: [589 601] ) } ) ) spids: [586 602] ) } spids: [585] ) ] spids: [585] ) (If arms: [ (if_arm cond: [(C {(Lit_Other "[")} {(-z)} {(DQ ($ VSub_Number "$2"))} {(Lit_Other "]")})] action: [ (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:OFFSET2) op:Equal rhs:{(0)} spids:[619])] spids: [619] ) ] spids: [-1 616] ) ] else_action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:OFFSET2) op: Equal rhs: {($ VSub_Name "$TOTAL")} spids: [625] ) ] spids: [625] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:TOTAL) op: Equal rhs: { (ArithSubPart anode: (ArithBinary op_id: Arith_Plus left: (ArithWord w:{($ VSub_Name "$TOTAL")}) right: (ArithWord w: { (CommandSubPart command_list: (CommandList children: [ (C {(roundlen)} {(4)} { (CommandSubPart command_list: (CommandList children: [(C {(filelen)} {(DQ ($ VSub_Number "$2"))})] ) left_token: spids: [638 644] ) } ) ] ) left_token: spids: [633 645] ) } ) ) spids: [630 646] ) } spids: [629] ) ] spids: [629] ) ] spids: [622 648] ) (If arms: [ (if_arm cond: [(C {(Lit_Other "[")} {(-z)} {(DQ ($ VSub_Number "$3"))} {(Lit_Other "]")})] action: [ (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:OFFSET3) op:Equal rhs:{(0)} spids:[665])] spids: [665] ) ] spids: [-1 662] ) ] else_action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:OFFSET3) op: Equal rhs: {($ VSub_Name "$TOTAL")} spids: [671] ) ] spids: [671] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:TOTAL) op: Equal rhs: { (ArithSubPart anode: (ArithBinary op_id: Arith_Plus left: (ArithWord w:{($ VSub_Name "$TOTAL")}) right: (ArithWord w: { (CommandSubPart command_list: (CommandList children: [ (C {(roundlen)} {(4)} { (CommandSubPart command_list: (CommandList children: [(C {(filelen)} {(DQ ($ VSub_Number "$3"))})] ) left_token: spids: [684 690] ) } ) ] ) left_token: spids: [679 691] ) } ) ) spids: [676 692] ) } spids: [675] ) ] spids: [675] ) ] spids: [668 694] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:LENGTH) op: Equal rhs: { (CommandSubPart command_list: (CommandList children: [(C {(roundlen)} {(4096)} {($ VSub_Name "$TOTAL")})] ) left_token: spids: [697 703] ) } spids: [696] ) ] spids: [696] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CRC32) op: Equal rhs: { (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (Subshell child: (CommandList children: [ (C {(emit_header)} {(skip)}) (C {(zpad_file)} {(4)} {(DQ ($ VSub_Number "$1"))}) (C {(zpad_file)} {(4)} {(DQ ($ VSub_Number "$2"))}) (C {(zpad_file)} {(4)} {(DQ ($ VSub_Number "$3"))}) (C {(zpad)} { (CommandSubPart command_list: (CommandList children: [(C {(padlen)} {(4096)} {($ VSub_Name "$TOTAL")})] ) left_token: spids: [751 757] ) } ) ] ) spids: [714 760] ) (C {(cksum)} {(-NILP)}) ] negated: False ) ] ) left_token: spids: [711 768] ) } spids: [710] ) ] spids: [710] ) (C {(emit_header)}) (C {(zpad_file)} {(4)} {(DQ ($ VSub_Number "$1"))}) (C {(zpad_file)} {(4)} {(DQ ($ VSub_Number "$2"))}) (C {(zpad_file)} {(4)} {(DQ ($ VSub_Number "$3"))}) (C {(zpad)} { (CommandSubPart command_list: (CommandList children:[(C {(padlen)} {(4096)} {($ VSub_Name "$TOTAL")})]) left_token: spids: [803 809] ) } ) ] )