1 FreeWRT Configuration Filesystem
2 ════════════════════════════════
6 Version 1.04 - 2 July 2007
10 Thorsten Glaser <tg@mirbsd.de>
12 Provided that these terms and disclaimer and all copyright notices
13 are retained or reproduced in an accompanying document, permission
14 is granted to deal in this work without restriction, including un-
15 limited rights to use, publicly perform, distribute, sell, modify,
16 merge, give away, or sublicence.
18 Advertising materials mentioning features or use of this work must
19 display the following acknowledgement:
20 This product includes material provided by Thorsten Glaser
21 for the FreeWRT Project.
23 This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
24 the utmost extent permitted by applicable law, neither express nor
25 implied; without malicious intent or gross negligence. In no event
26 may a licensor, author or contributor be held liable for indirect,
27 direct, other damage, loss, or other issues arising in any way out
28 of dealing in the work, even if advised of the possibility of such
29 damage or existence of a defect, except proven that it results out
30 of said person's immediate fault when using the work as intended.
36 FreeWRT is an operating system for embedded devices. At the moment,
37 it provides a uClibc/GNU/Linux-based operating environment for mips-
38 based hardware routers, e.g. from Linksys or Asus.
40 FreeWRT operates on flash memory and as such is under constraints to
41 reduce the amount of write operations to the root filesystem, because
42 flash memory has limited lifetime. Changing the file-based configura-
43 tion in /etc, however, often requires a fair amount of write opera-
44 tions; furthermore, usual reconfiguration operations change more than
45 only one file, possibly erasing and re-writing the same flash memory
46 block several times. In addition, in between these changes, the sy-
47 stem is in an inconsistent state, and, if the configuration changes
48 render the system unusable, a simple reboot will not be able to fix
49 it, a full reflash and reconfiguration is required.
51 My proposed implementation will present /etc as a memory filesystem,
52 loaded at boot with the content of the underlying /etc from the de-
53 fault root filesystem (usually on squashfs or jffs2), then populated
54 with additional files read from a custom flash partition in the be-
55 low documented format. Changes to /etc will never be reflected in the
56 underlying root filesystem, and the fwcf partition is only updated by
57 a userland programme to be run manually.
60 2. Implementation details
61 ―――――――――――――――――――――――――
63 The size of the flash partition has been set by the FreeWRT project
64 to 128 KiB (usually two flash blocks). A custom flash map driver has
65 been added to the FreeWRT kernel before the import of fwcf.
67 The command-line utility will support three operations:
68 • fwcf setup to be run by the rc bootup script early
69 • fwcf commit similar to Cisco ‘write’
70 • fwcf erase similar to Cisco ‘erase startup-config’
71 • fwcf status NEW IN 1.03: check if commit is needed
72 • fwcf dump NEW IN 1.03: make a backup of the fwcf filesystem
73 • fwcf restore NEW IN 1.03: restore a previously made backup
75 • poweroff > NEW IN 1.04: wrapper around busybox
78 This utility is implemented as rapid prototype as a shell script in
79 ash, using one C helper programme. Later versions will be pure C.
82 2.1. Operation of ‘fwcf setup’
83 ――――――――――――――――――――――――――――――
85 This command will first remap the existing /etc (via ‘mount --bind’)
86 to /tmp/.fwcf/root. Then, it will create a memory filesystem (tmpfs)
87 at /tmp/.fwcf/temp and populate it with all files from /tmp/.fwcf/root.
88 Now, the fwcf flash partition will be read, the format and checksum
89 verified and data extracted to /tmp/.fwcf/temp, possibly overwriting
90 pre-existing files†. Then, the /tmp/.fwcf/temp filesystem will be re-
91 bound to /etc and, finally, the mountpoint at /tmp/.fwcf/temp unloaded.
93 NEW IN 1.03: If /etc/.fwcf_deleted exists, the files listed in it,
94 newline-separated, will be removed from and relative to /etc, then
95 the file itself will be removed.
97 Data from the end of the fwcf data in the flash partition to the
98 end of the 64 KiB block the end of data resides in will be written
101 NEW IN 1.03: Afterwards, a sorted list of all files is given to the
102 busybox md5sum applet, the output is stored as /tmp/.fwcf/status.asz
104 If the “fwcf” mtd partition does not start with the four letters
105 FWCF on invoking ‘fwcf setup’, it is erased (i.e. populated with
106 an empty FWCF filesystem).
108 NEW IN 1.03: If run with ‘-N’, it will not read out the data from
109 flash and force an “unclean startup”, as described below.
111 †) NEW IN 1.03: If this fails, but the “fwcf” mtd partition starts
112 with FWCF, i.e. we cannot read the flash filesystem, possibly because
113 it's from an incompatible format or unknown compressor, a flag file
114 is created as /etc/.fwcf_unclean to prevent a following commit which
115 would lead to data loss. The user must remove this file to override.
118 2.2. Operation of ‘fwcf commit’
119 ―――――――――――――――――――――――――――――――
121 A new memory filesystem (tmpfs) will be createt at /tmp/.fwcf/temp
122 and populated with the data currently in /etc. Now, NEW IN 1.03,
123 the /tmp/.fwcf/status.asz file is recreated. Then, ALSO NEW IN 1.03,
124 files in /tmp/.fwcf/root but not in /tmp/.fwcf/temp will be listed
125 in /tmp/.fwcf/temp/.fwcf_deleted, newline-separated. Now, all files
126 with exactly the same content in /tmp/.fwcf/root will be removed
127 from /tmp/.fwcf/temp. Any remaining files will be packed into the
128 fwcf format documented below and written to the flash partition,
129 padded to a multiple of 64 KiB with data read from /dev/urandom.
131 Unclean setups, NEW IN 1.03, will prevent a commit, unless the
132 file /etc/.fwcf_unclean is removed manually, or the ‘-f’ option
135 The first public release does only support directories, files
136 and symbolic links, for simplicity. Stored hard links and other
137 file types will be skipped, because their storage format is al-
138 ready specified (as “reserved for future use”), and ignored. No
139 inode or file-sequential-number information is read or written.
142 2.3. Operation of ‘fwcf erase’
143 ――――――――――――――――――――――――――――――
145 In theory, just writing a NUL byte to the beginning of the flash
146 partition would suffice. However, this requires an mtd erase and
147 flash operation of one entire flash block (usually 64 KiB), so an
148 empty fwcf filesystem padded with random data to the next 64 KiB
149 will be written instead, for the added benefit of improving the
150 quality of the kernel PRNG even over total reconfigurations.
153 2.4. Operation of ‘fwcf status’ (NEW IN 1.03)
154 ――――――――――――――――――――――――――――――― ┄┄┄┄┄┄┄┄┄┄┄┄┄
156 For all files in /etc, the ‘md5sum’ busybox applet is run, output
157 stored in a temporary file and compared against the saved values
158 from ‘fwcf setup’. If the ‘-q’ flag is not given, the differences
159 are shown as “<oldmd5><space><newmd5><space><file>”, where the md5
160 is expressed as shown by the busybox applet, or as padded¹ “<NULL>”
161 if the file does not exist on either side, where “old” is the status
162 at fwcf setup time (or the /etc from the root fs, if ‘-r’ is given),
163 and “new” is the status of the current (tmpfs) /etc. If there are
164 no differences, the exit status is 0, non-0 otherwise.
166 If the ‘-r’ flag is given, operation is done against the data that
167 is stored in the ROM, without considering the contents of the FWCF
170 ¹) Every “MD5” value is padded to be 32 bytes long, at its left
171 side, with spaces (just to clarify).
174 2.5. Operation of ‘fwcf dump’ (NEW IN 1.03)
175 ――――――――――――――――――――――――――――― ┄┄┄┄┄┄┄┄┄┄┄┄┄
177 A dump of the data currently stored in flash (commit first if you
178 have changed anything!) is dumped to the filename argument, or to
179 standard output, if none is present.
181 Note: dumps are a LZO1X compressed tarball of a 256-byte entropy
182 seed (“seed”) and the contents of the “inner filesystem” in “asz”
183 format (“dump”), which is stored as .tar.asz itself. There is no
184 version information, and this is by design.
186 Implementation information: the fwcf helper tool has a new mo-
187 de of operation in which it works as compressor/decompressor –
188 the algorithm used is determined with the -C option on a build
189 system, and at compile time (i.e. the only one compiled in) on
190 the target system. That's a compromise relative to using gzip,
191 because it makes dumps depend on the compression algorithms in
192 use, but it's always LZO1X-1 in FreeWRT 1.03 and up, and no de-
193 pendency on a 50+ KiB gzip binary is added; the .tar.asz enco-
194 ded dump can be recompressed with the tool on the build system.
196 While the dump itself is tar → compressed → asz encoded, “asz”
197 itself is not a compressing format, just a storage container –
198 which stores one octet stream, plus size and checksum informa-
199 tion only. Because many compressors have no idea about the un-
200 compressed size, the actual encoded data is prefixed by a lit-
201 tle endian unsigned 32-bit integer of the uncompressed length;
202 the resulting larger buffer is then passed to the asz encoder.
205 2.6. Operation of ‘fwcf restore’ (NEW IN 1.03)
206 ―――――――――――――――――――――――――――――――― ┄┄┄┄┄┄┄┄┄┄┄┄┄
208 A dump created with “fwcf dump” is expected to be read from the
209 filename argument, or standard input, if none is present, and
210 then written to flash.
213 3. Structure of the fwcf data
214 ―――――――――――――――――――――――――――――
216 All data is written in little-endian format.
218 The fwcf data begins at offset 0 in the flash partition, with the
219 magic bytes “FWCF” (0x46435746).
221 The next doubleword (four bytes) is the “outer length” of the fwcf
222 data, including the header (including the magic bytes and the length
223 information itself) and the trailer (checksum), but not the padding;
224 the length takes up the lower 24 bits of this doubleword. The upper
225 8 bits are the (major) version of the specification adhered to, i.e.
226 0x01 for this document. This information shall be true for all ver-
227 sions of this specification in order to enable the fwcf command-line
228 utility to perform as follows: it is not required to process any non-
229 native versions of fwcf data, but even if reading a different version,
230 the random data used for the padding should be written to /dev/urandom.
232 The following information is dependent on the version of the speci-
235 The next doubleword (starting at offset 8) is the “inner length” of
236 the compressed fwcf data (lower 24 bit), or'd with the identification
237 number of the compression algorithm used (upper 8 bit). Note this ef-
238 fectively limits both the uncompressed and the compressed size of an
239 fwcf filesystem to 2²⁴ bytes = 16 MiB. Since the filesystem is de-
240 signed for /etc, this limitation is not expected to be troublesome.
242 After this, at offset 12, the compressed data starts. It is padded
243 to the next 4-byte boundary with zeroes.
245 The next doubleword is the ADLER-32 checksum (as defined by libz)
246 of all previous data, starting from the magic bytes at offset 0,
247 ending with the zero-padding of the compressed data. Note that this
248 does not check the integrity of the data after decompressing; cur-
249 rently we must trust the decompressor to check integrity and do a
250 length check on the decompressed data returned by the plugin our-
251 selves. The next major version of the specification may change that.
254 4. Compression algorithm allocation
255 ―――――――――――――――――――――――――――――――――――
257 An implementation is only required to be able to use exactly one of
258 the compression algorithms defined below, but it is not required to
259 implement a specific algorithm. Conversion might be achieved by un-
260 and repacking the data, or using an fwcf implementation with multi-
261 ple algorithms. Every implementation, however, is required to offer
262 at least one of the non-private algorithms below.
264 This draft of the specification offers two compression algorithms:
266 0x00 = plain uncompressed data
267 0x01 = zlib deflate compression as per http://www.zlib.net/
268 0x10 = LZO1X as per http://www.oberhumer.com/opensource/lzo/
270 Algorithm codes from 0xE0 to 0xFF are available for private use.
273 5. Structure of the fwcf filesystem
274 ―――――――――――――――――――――――――――――――――――
276 The compressed/inner data consists of a byte stream without padding
277 applied, in the following format:
279 entry ::= file-entry | NUL-byte
281 file-entry ::= pathname NUL-byte attributes NUL-byte data
283 attributes ::= attribute ( attribute )*
285 The pathname is a POSIX pathname, i.e. can contain any character
286 except NUL. Directories are separated with ‘/’ and automatically
287 created by the extraction tool if required. If the first octet
288 of the pathname is a NUL byte (i.e. it is of zero length), the
289 end of the filesystem has been reached. Any data read afterwards
290 MUST be discarded for security reasons.
292 Attributes consist of a one-byte identifier, which is usually a
293 letter, and a zero-to-multiple-bytes payload. If the identifier
294 is a letter, its lowercase and uppercase forms denote the same
295 attribute with a different payload length.
297 The raw file data is not padded or aligned; its length is an at-
298 tribute. Alternate streams / forks are not supported.
301 6. Currently defined attributes
302 ―――――――――――――――――――――――――――――――
304 0x01 this file is a block special device ①
306 reserved for future use
308 0x02 this file is a character special device ①
310 reserved for future use
312 0x03 this file is a symbolic link ②
315 0x04 this file is a hard link to another file ① ④
317 reserved for future use
319 0x05 this file is a directory ④
322 0x0D this file is deleted ①
323 reserved for future use
325 0x10 modification time of the entry
327 ignored for symbolic links
328 payload length: 32 bit
330 g/G group of the file (numeric GID)
332 payload length: lowercase = 8 bit, uppercase = 32 bit
334 i/I “inode” of the file ① ④
335 required if this file is a hard link source or target
336 optional (ignored) otherwise
337 payload length: lowercase = 8 bit, uppercase = 16 bit
338 reserved for future use
340 m/M mode_t / permissions of the file ③
342 ignored for symbolic links
343 payload length: lowercase = 16 bit, uppercase = 32 bit
345 o/O owner of the file (numeric UID)
347 payload length: lowercase = 8 bit, uppercase = 32 bit
350 for files and symbolic links: mandatory
351 for directories, device nodes and hard links: forbidden
352 payload length: lowercase = 8 bit, uppercase = 24 bit
354 ① These identifiers are defined in this specification for future
355 use; implementations do not need to support them at this time.
357 ② The name of the target is the data, thus, size is required.
359 ③ Defaults to 0 if not used (for security reasons), so labelling
360 it as “optional” is probably a farce ☺
362 ④ Implementing hard links and directories is, of course, optional
369 The initial idea for a “configuration filesystem” based upon the
370 Linux FUSE kernel module has been communicated to me by Waldemar
371 Brodkorb, FreeWRT Project Founder. After a discussion with him I
372 decided on the archive/userland tool layout outlined in sections
373 1 and 2 above. For FreeWRT 1.0, it has been realised in shell.
375 For now, nodes other than directories, files, and symbolic links
378 Development of FWCF is hosted in the CVS repository of the MirOS
379 project. Anonymous read-only CVS access is available at the root
380 “:ext:anoncvs@anoncvs.mirbsd.org:/cvs” using password “anoncvs”,
381 SSH on port 22; module “fwcf”. CVSweb is also available, e.g. at
382 http://cvs.mirbsd.de/contrib/hosted/fwcf/ or its mirrors.
384 FWCF code is released under the same licence terms as the speci-
385 fication, but without the advertising clause. The author however
386 would really appreciate users to credit his name and that of the
387 FreeWRT Project in derived works and/or links to the CVS reposi-
388 tory of the original source.
391 8. The “asz” file format (NEW IN 1.03)
392 ―――――――――――――――――――――――― ┄┄┄┄┄┄┄┄┄┄┄┄┄
394 The “asz” format is intended for storing small arbitrary 8-bit
395 data, and used in the “fwcf dump” and “fwcf restore” formats –
396 the dump itself is a raw uncompressed “inner fwcf filesystem”,
397 stored as “asz”, and the storage used by the dump/restore com-
398 mands is a tarball, compressed with lzo1X1 (in the current im-
399 plementation), the result stored, again, as “asz”.
401 It almost looks like the “outer fwcf” format, except the start
402 isn't a header but the ADLER-32 checksum double-word (2 unsig-
403 ned 16-bit integer in LITTLE ENDIAN), i.e. without magic bytes
404 to identify the format; followed by the length double-word – 1
405 unsigned 32-bit integer in LITTLE ENDIAN – and finally the raw
406 binary data. Only the lowest three octets of the length should
407 be used because the current implementation malloc(3)s a buffer
408 containing the whole data.
414 The next major version of the FWCF filesystem specification is
415 likely to contain the following changes:
417 • An additional checksum (probably ADLER32 as well) shall be
418 placed inside the compressed portion, to be checked after
419 decompression. The idea of adding a random IV has not been
420 adopted because we pretty much want the same FWCF blocks,
421 except the random padding at the end, to be generated for
422 the same input data. (This is not guaranteed because fts()
423 may traverse the directory hierarchy differently.)
424 • Revisit the current size limits and file types.
425 • Implement a r̲e̲a̲l̲ file type “deleted”, replacing the hack
426 with the .fwcf_deleted file.
428 These future directions have come up during or after the
429 fwcf 1.00 release process, and from the discussion thereafter.
430 They are provided as hint only and not part of the specifi-
431 cation itself. They may change without notice.
433 ⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼
434 $MirOS: contrib/hosted/fwcf/fwcf.txt,v 1.37 2007/07/02 14:55:44 tg Exp $