Components/IFS/src/include/IFS/FWFS/Object.h
Go to the documentation of this file.
1 /****
2  * Object.h
3  * Basic definitions for FW file system structure.
4  *
5  * Created on: 7 Aug 2018
6  *
7  * Copyright 2019 mikee47 <mike@sillyhouse.net>
8  *
9  * This file is part of the IFS Library
10  *
11  * This library is free software: you can redistribute it and/or modify it under the terms of the
12  * GNU General Public License as published by the Free Software Foundation, version 3 or later.
13  *
14  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along with this library.
19  * If not, see <https://www.gnu.org/licenses/>.
20  *
21  *
22  * A filesystem image is basically:
23  *
24  * uint32_t START_MARKER;
25  * Object Objects[];
26  * Object EndObject;
27  * uint32_t END_MARKER;
28  *
29  * An FWFS Object is a variable-length structure which can be read as either 1 or 2 words,
30  * depending on the type.
31  * Objects always start on a word boundary.
32  * File and directory objects are both emitted as 'named' objects which contain a list of
33  * zero or more child objects. When the image is built the child objects are emitted first,
34  * followed by the parent. This puts the root directory at the end of the image.
35  * This arrangement means an image can be generated without having to back-track and rewrite
36  * headers.
37  *
38  * Child objects can be of any type. A directory object will mostly contain other file and
39  * directory objects. File data is stored in a data object, not in the file object itself.
40  * This is usually found following the file object, but it doesn't have to be.
41  * Any object may be referenced by zero or more named object(s). For example, file links/aliases
42  * can contain references to the same file data.
43  * A file object may contain multiple data objects. These are treated as a contiguous block for file operations.
44  * This would potentially allow a file system builder to place common file blocks into shared data objects.
45  *
46  * Object names are from 0 to 255 characters, inclusive. The root directory has a zero-length name.
47  * Paths lengths are unlimited.
48  * '/' is used as the path separator. It informs the filesystem of the parent/child relationship
49  * between one directory object and a sub-ordinate.
50  * ':' is used as the file stream separator. It performs the equivalent of the path separator for
51  * non-directory named objects. For exaample, file object may contain named objects accessible thus:
52  * index.html:info opens a handle to a named object called 'info' belonging to index.html.
53  *
54  * OK, I admit it; this is pinched from NTFS; but it's such a cool idea. Applications can use it
55  * to attach custom data to their files without limitation.
56  *
57  * As a side note, of course FWFS is read-only. More precisely, it only supports random reading
58  * of files, not random writing. Serial writing is supported in the form of image creation.
59  *
60  * For SPIFFS, IFS is a wrapper. The metadata features are supported using SPIFFS metadata.
61  * An alternative approach is to implement every named object as a SPIFFS file. We'd then get
62  * all the features of FWFS in a rewritable system, with all the benefits of SPIFFS wear-levelling.
63  *
64  * Objects are identified by their index, but it's not stored in the image. Instead, it's
65  * tracked via internal object descriptor.
66  *
67  * To optimise lookups, an object table can be stored at the end of the image. This is
68  * just an array of 32-bit image offsets so that an object can be located instantly on
69  * large volumes. This will be optional as it can consume significant space.
70  *
71  ****/
72 
73 #pragma once
74 
75 #include "../TimeStamp.h"
76 #include "../FileAttributes.h"
77 #include "../Compression.h"
78 #include "../UserRole.h"
79 
80 namespace IFS::FWFS
81 {
82 /*
83  * Helper template function to get the next object pointer at (current + offset)
84  * casting as required for the given return type.
85  */
86 template <typename T> static T at_offset(const void* current, int offset)
87 {
88  auto p = reinterpret_cast<const uint8_t*>(current) + offset;
89  return reinterpret_cast<T>(p);
90 }
91 
92 template <typename T> static T at_offset(void* current, int offset)
93 {
94  auto p = reinterpret_cast<uint8_t*>(current) + offset;
95  return reinterpret_cast<T>(p);
96 }
97 
98 // First object located immediately after start marker in image
99 constexpr size_t FWFS_BASE_OFFSET{sizeof(uint32_t)};
100 
101 // Images start with a single word to identify this as a Firmware Filesystem image
102 constexpr uint32_t FWFILESYS_START_MARKER{0x53465746}; // "FWFS"
103 
104 // Image end marker (reverse of magic)
105 constexpr uint32_t FWFILESYS_END_MARKER{0x46574653}; // "SFWF"
106 
107 // Everything in this header must be portable (at least, with other little-endian systems) so byte-align and pack it
108 #pragma pack(1)
109 
117 #define FWFS_OBJTYPE_MAP(XX) \
118  XX(0, End, "The system image footer") \
119  XX(1, Data8, "Data, max 255 bytes") \
120  XX(2, ID32, "32-bit identifier") \
121  XX(3, ObjAttr, "Object attributes") \
122  XX(4, Compression, "Compression descriptor") \
123  XX(5, ReadACE, "minimum UserRole for read access") \
124  XX(6, WriteACE, "minimum UserRole for write access") \
125  XX(7, VolumeIndex, "Volume index number") \
126  XX(8, Md5Hash, "MD5 Hash Value") \
127  XX(9, Comment, "Comment") \
128  XX(10, UserAttribute, "User Attribute") \
129  XX(32, Data16, "Data, max 64K - 1") \
130  XX(33, Volume, "Volume, top-level container object") \
131  XX(34, MountPoint, "Root for another filesystem") \
132  XX(35, Directory, "Directory entry") \
133  XX(36, File, "File entry") \
134  XX(64, Data24, "Data, max 16M - 1")
135 
142 struct Object {
143  uint8_t typeData;
144 
148  using ID = uint32_t;
149 
150  enum class Type {
151 #define XX(value, tag, text) tag = value,
153 #undef XX
154  };
155 
156  // Top bit of object type set to indicate a reference
157  static constexpr uint8_t FWOBT_REF{0x80};
158 
163  enum class Attribute {
167  ReadOnly,
171  Archive,
178  Encrypted,
179  //
180  MAX
181  };
182 
183  using Attributes = BitSet<uint8_t, Attribute, size_t(Attribute::MAX)>;
184 
185  Type type() const
186  {
187  return static_cast<Type>(typeData & 0x7f);
188  }
189 
190  void setType(Type type, bool isRef = false)
191  {
193  if(isRef) {
194  typeData |= FWOBT_REF;
195  }
196  }
197 
198  bool isRef() const
199  {
200  return (typeData & FWOBT_REF) != 0;
201  }
202 
203  uint32_t getRef() const
204  {
205  if(!isRef()) {
206  return 0;
207  }
208  uint32_t mask = 0xffffffff >> ((4 - data8.contentSize()) * 8);
209  return data8.ref.packedOffset & mask;
210  }
211 
212  bool isNamed() const
213  {
214  return type() >= Type::Volume && type() < Type::Data24;
215  }
216 
217  bool isData() const
218  {
219  return type() == Type::Data8 || type() == Type::Data16 || type() == Type::Data24;
220  }
221 
222  bool isDir() const
223  {
224  return type() == Type::Directory;
225  }
226 
227  bool isMountPoint() const
228  {
229  return type() == Type::MountPoint;
230  }
231 
232  union {
233  // Data8 - 8-bit size, max. 255 bytes
234  struct {
235  uint8_t _contentSize;
236  // uint8_t data[];
237 
238  unsigned contentOffset() const
239  {
240  return offsetof(Object, data8) + 1;
241  }
242 
243  unsigned contentSize() const
244  {
245  return _contentSize;
246  }
247 
248  void setContentSize(uint32_t value)
249  {
251  }
252 
253  union {
254  // An object reference: the contents are stored externally (on the same volume)
255  struct {
256  uint32_t packedOffset; // 1-4 byte offset
257  } ref;
258 
259  // ID32
260  struct {
261  uint32_t value;
262  } id32;
263 
264  // Object attributes
265  struct {
266  uint8_t attr; // Attributes
268 
269  // Compression descriptor
271 
272  // ReadACE, WriteACE
273  struct {
275  } ace;
276 
277  // Identifies a volume index, contained in a mount point
278  struct {
279  uint8_t index;
281 
282  // User attribute
283  struct {
284  uint8_t tagValue;
285  // uint8_t[] data;
287 
288  // END - immediately followed by end marker
289  struct {
290  uint32_t checksum;
291  } end;
292  };
293  } data8;
294 
295  // Data16 - 16-bit size, max. (64KB - 1)
296  struct {
297  uint16_t _contentSize;
298  // uint8_t data[];
299 
300  unsigned contentOffset() const
301  {
302  return offsetof(Object, data16) + 2;
303  }
304 
305  uint32_t contentSize() const
306  {
307  return _contentSize;
308  }
309 
310  void setContentSize(uint32_t value)
311  {
313  }
314 
315  uint32_t size() const
316  {
317  return contentOffset() + contentSize();
318  }
319 
320  union {
321  /*
322  * named object
323  *
324  * child objects can be contained or referenced (flag in _id)
325  * object attributes are optional so can be another object/attribute
326  */
327  struct {
328  uint8_t namelen;
329  TimeStamp mtime; // Object modification time
330  // char name[namelen];
331  // Object children[];
332 
333  // Offset to object name relative to content start
334  unsigned nameOffset() const
335  {
336  return sizeof(Object::data16.named);
337  }
338 
339  // Offset to start of child object table
340  unsigned childTableOffset() const
341  {
342  return nameOffset() + namelen;
343  }
344 
345  } named;
346  };
348 
349  // Data24 - 24-bit size, max. (16MB - 1)
350  struct {
351  uint16_t _contentSize;
353  // uint8_t data[];
354 
355  size_t contentOffset() const
356  {
357  return offsetof(Object, data24) + 3;
358  }
359 
360  uint32_t contentSize() const
361  {
362  return (_contentSizeHigh << 16) | _contentSize;
363  }
364 
365  void setContentSize(uint32_t value)
366  {
367  _contentSize = value & 0xffff;
368  _contentSizeHigh = value >> 16;
369  }
370 
371  uint32_t size() const
372  {
373  return contentOffset() + contentSize();
374  }
376  };
377 
380  size_t contentOffset() const
381  {
382  if(isRef() || (type() < Type::Data16)) {
383  return data8.contentOffset();
384  } else if(type() < Type::Data24) {
385  return data16.contentOffset();
386  } else {
387  return data24.contentOffset();
388  }
389  }
390 
395  uint32_t contentSize() const
396  {
397  if(isRef() || (type() < Type::Data16)) {
398  return data8.contentSize();
399  } else if(type() < Type::Data24) {
400  return data16.contentSize();
401  } else {
402  return data24.contentSize();
403  }
404  }
405 
406  void setContentSize(size_t size)
407  {
408  if(isRef() || type() < Type::Data16) {
409  data8.setContentSize(size);
410  } else if(type() < Type::Data24) {
411  data16.setContentSize(size);
412  } else {
413  data24.setContentSize(size);
414  }
415  }
416 
417  uint32_t childTableOffset() const
418  {
419  assert(isNamed());
420  return data16.contentOffset() + data16.named.childTableOffset();
421  }
422 
423  uint32_t childTableSize() const
424  {
425  assert(isNamed());
426  int size = data16.contentSize() - data16.named.childTableOffset();
427  return (size <= 0) ? 0 : size;
428  }
429 
433  uint32_t size() const
434  {
435  return contentOffset() + contentSize();
436  }
437 };
438 
439 static_assert(sizeof(Object) == 8, "Object alignment wrong!");
440 
441 #pragma pack()
442 
446 struct FWObjDesc {
449 
450  FWObjDesc(Object::ID objId = 0) : id(objId)
451  {
452  }
453 
454  uint32_t offset() const
455  {
456  return id;
457  }
458 
459  uint32_t nextOffset() const
460  {
461  return offset() + obj.size();
462  }
463 
464  // Move to next object location
465  void next()
466  {
467  id = nextOffset();
468  }
469 
470  uint32_t contentOffset() const
471  {
472  return offset() + obj.contentOffset();
473  }
474 
475  uint32_t childTableOffset() const
476  {
477  return offset() + obj.childTableOffset();
478  }
479 };
480 
483 
484 } // namespace IFS::FWFS
485 
String toString(IFS::FWFS::Object::Type obt)
Get descriptive String for an object type.
#define FWFS_OBJTYPE_MAP(XX)
Object type identifiers.
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:117
The String class.
Definition: WString.h:133
IFS::Directory Directory
Definition: Core/FileSystem.h:33
Definition: Components/IFS/src/include/IFS/FWFS/ArchiveStream.h:28
Object::Attributes getObjectAttributes(FileAttributes fileAttr)
constexpr uint32_t FWFILESYS_END_MARKER
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:105
FileAttributes getFileAttributes(Object::Attributes objattr)
static T at_offset(const void *current, int offset)
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:86
constexpr size_t FWFS_BASE_OFFSET
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:99
constexpr uint32_t FWFILESYS_START_MARKER
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:102
UserRole
Definition: UserRole.h:36
A compression descriptor.
Definition: Compression.h:38
FWFS Object Descriptor.
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:446
void next()
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:465
uint32_t childTableOffset() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:475
Object obj
The object structure.
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:448
uint32_t contentOffset() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:470
uint32_t offset() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:454
Object::ID id
location
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:447
uint32_t nextOffset() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:459
FWObjDesc(Object::ID objId=0)
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:450
Object structure.
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:142
struct IFS::FWFS::Object::@26::@28::@31::@34 id32
Compression compression
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:270
bool isData() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:217
bool isNamed() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:212
void setContentSize(size_t size)
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:406
struct IFS::FWFS::Object::@26::@28::@31::@36 ace
Attribute
Object attributes.
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:163
@ ReadOnly
Object should not be modified or deleted.
@ Encrypted
Object data is encrypted.
@ Archive
Object modified flag.
struct IFS::FWFS::Object::@26::@28::@31::@33 ref
uint16_t _contentSize
Object size (excluding this header)
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:297
UserRole role
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:274
Type type() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:185
struct IFS::FWFS::Object::@26::@28::@31::@39 end
uint32_t value
32-bit identifier, e.g. volume ID
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:261
struct IFS::FWFS::Object::@26::@28 data8
bool isRef() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:198
bool isDir() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:222
struct IFS::FWFS::Object::@26::@29 data16
uint32_t size() const
total size this object occupies in the image
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:433
uint32_t packedOffset
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:256
struct IFS::FWFS::Object::@26::@28::@31::@38 userAttribute
uint8_t _contentSizeHigh
Allows data up to 16MByte.
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:352
struct IFS::FWFS::Object::@26::@30 data24
uint32_t childTableOffset() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:417
bool isMountPoint() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:227
uint32_t childTableSize() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:423
uint32_t checksum
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:290
struct IFS::FWFS::Object::@26::@28::@31::@35 objectAttributes
Type
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:150
XX(value, tag, text)
uint8_t index
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:279
uint8_t typeData
Stored type plus flag.
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:143
uint32_t contentSize() const
return size of object content, excluding header and size fields
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:395
uint8_t tagValue
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:284
uint8_t namelen
Length of object name.
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:328
struct IFS::FWFS::Object::@26::@28::@31::@37 volumeIndex
void setType(Type type, bool isRef=false)
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:190
TimeStamp mtime
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:329
uint32_t ID
Object identifier (offset from start of image)
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:148
uint8_t _contentSize
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:235
static constexpr uint8_t FWOBT_REF
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:157
uint32_t getRef() const
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:203
size_t contentOffset() const
return offset to start of object content
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:380
uint8_t attr
Definition: Components/IFS/src/include/IFS/FWFS/Object.h:266
struct IFS::FWFS::Object::@26::@29::@40::@42 named
Manage IFS timestamps stored as an unsigned 32-bit value.
Definition: TimeStamp.h:37