summaryrefslogtreecommitdiff
path: root/include/sqfs/inode.h
blob: f2b6ede94506e680ecf34fb67f743b9d991cdd6b (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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/*
 * inode.h - This file is part of libsquashfs
 *
 * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
#ifndef SQFS_INODE_H
#define SQFS_INODE_H

#include "sqfs/predef.h"

/**
 * @file inode.h
 *
 * @brief Contains on-disk data structures used for inodes.
 */

/**
 * @enum E_SQFS_INODE_TYPE
 *
 * @brief Used by @ref sqfs_inode_t to identify the inode type.
 */
typedef enum {
	SQFS_INODE_DIR = 1,
	SQFS_INODE_FILE = 2,
	SQFS_INODE_SLINK = 3,
	SQFS_INODE_BDEV = 4,
	SQFS_INODE_CDEV = 5,
	SQFS_INODE_FIFO = 6,
	SQFS_INODE_SOCKET = 7,
	SQFS_INODE_EXT_DIR = 8,
	SQFS_INODE_EXT_FILE = 9,
	SQFS_INODE_EXT_SLINK = 10,
	SQFS_INODE_EXT_BDEV = 11,
	SQFS_INODE_EXT_CDEV = 12,
	SQFS_INODE_EXT_FIFO = 13,
	SQFS_INODE_EXT_SOCKET = 14,
} E_SQFS_INODE_TYPE;

/**
 * @enum E_SQFS_INODE_MODE
 *
 * @brief Mode bits for the @ref sqfs_inode_t mode field.
 *
 * This is basically the same that mode bits in struct stat store on Unix-like
 * systems. It is duplicated here for portability with non-POSIX platforms. In
 * case you are not familiar with Unix file permissions, a brief description
 * follows.
 *
 * There are 3 fields with permissions:
 * - of the user that owns the file
 * - of the group that the file belongs to
 * - everybody else
 *
 * Each field holds 3 bits: X, W and R meaning execute, write and read access
 * respectively. There are 2 special cases: On a directory, execute means
 * entering the directory and accessing its contents. Read and write refere
 * to reading or changing the list of entries. For symlinks, the permissions
 * are meaningless and have all bits set.
 *
 * Besides the permissions, there are 3 more bits:
 * - sticky
 * - set group id
 * - set user id
 *
 * Nowadays, the later two mean executing a program makes it run as the group
 * or user (respectively) that owns it instead of the user that actually ran
 * the program. On directories, the sticky bit means that its contents can only
 * be deleted by the actual owner, even if others have write permissions. All
 * other uses of those bits are obscure, historic and differ between flavours
 * of Unix.
 *
 * The remaining 4 bits (adding up to a total of 16) specify the type of file:
 * - named pipe, aka fifo
 * - character device
 * - directory
 * - block device
 * - regular file
 * - symlink
 * - socket
 */
typedef enum {
	SQFS_INODE_OTHERS_X = 00001,
	SQFS_INODE_OTHERS_W = 00002,
	SQFS_INODE_OTHERS_R = 00004,
	SQFS_INODE_OTHERS_MASK = 00007,

	SQFS_INODE_GROUP_X = 00010,
	SQFS_INODE_GROUP_W = 00020,
	SQFS_INODE_GROUP_R = 00040,
	SQFS_INODE_GROUP_MASK = 00070,

	SQFS_INODE_OWNER_X = 00100,
	SQFS_INODE_OWNER_W = 00200,
	SQFS_INODE_OWNER_R = 00400,
	SQFS_INODE_OWNER_MASK = 00700,

	SQFS_INODE_STICKY = 01000,
	SQFS_INODE_SET_GID = 02000,
	SQFS_INODE_SET_UID = 04000,

	SQFS_INODE_MODE_FIFO = 0010000,
	SQFS_INODE_MODE_CHR = 0020000,
	SQFS_INODE_MODE_DIR = 0040000,
	SQFS_INODE_MODE_BLK = 0060000,
	SQFS_INODE_MODE_REG = 0100000,
	SQFS_INODE_MODE_LNK = 0120000,
	SQFS_INODE_MODE_SOCK = 0140000,
	SQFS_INODE_MODE_MASK = 0170000,
} E_SQFS_INODE_MODE;

/**
 * @struct sqfs_inode_t
 *
 * @brief Common inode structure
 *
 * This structure holds the fields common for all inodes. Depending on the type
 * field, a specific inode structure follows.
 */
struct sqfs_inode_t {
	/**
	 * @brief An @ref E_SQFS_INODE_TYPE value.
	 */
	sqfs_u16 type;

	/**
	 * @brief Mode filed holding permission bits only. The type is derived
	 *        from the type field.
	 *
	 * This field holds a combination of @ref E_SQFS_INODE_MODE flags.
	 */
	sqfs_u16 mode;

	/**
	 * @brief An index into the ID table where the owner UID is located.
	 */
	sqfs_u16 uid_idx;

	/**
	 * @brief An index into the ID table where the owner GID is located.
	 */
	sqfs_u16 gid_idx;

	/**
	 * @brief Last modifcation time.
	 *
	 * This field counts seconds (not counting leap seconds) since 00:00,
	 * Jan 1 1970 UTC. This field is unsigned, so it expires in the year
	 * 2106 (as opposed to 2038).
	 */
	sqfs_u32 mod_time;

	/**
	 * @brief Unique inode number
	 */
	sqfs_u32 inode_number;
};

/**
 * @struct sqfs_inode_dev_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_BDEV
 *        or @ref SQFS_INODE_CDEV.
 */
struct sqfs_inode_dev_t {
	/**
	 * @brief Number of hard links to this node.
	 */
	sqfs_u32 nlink;

	/**
	 * @brief Device number.
	 */
	sqfs_u32 devno;
};

/**
 * @struct sqfs_inode_dev_ext_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_BDEV
 *        or @ref SQFS_INODE_EXT_CDEV.
 */
struct sqfs_inode_dev_ext_t {
	/**
	 * @brief Number of hard links to this node.
	 */
	sqfs_u32 nlink;

	/**
	 * @brief Device number.
	 */
	sqfs_u32 devno;

	/**
	 * @brief Extended attribute index.
	 */
	sqfs_u32 xattr_idx;
};

/**
 * @struct sqfs_inode_ipc_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_FIFO
 *        or @ref SQFS_INODE_SOCKET.
 */
struct sqfs_inode_ipc_t {
	/**
	 * @brief Number of hard links to this node.
	 */
	sqfs_u32 nlink;
};

/**
 * @struct sqfs_inode_ipc_ext_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_FIFO
 *        or @ref SQFS_INODE_EXT_SOCKET.
 */
struct sqfs_inode_ipc_ext_t {
	/**
	 * @brief Number of hard links to this node.
	 */
	sqfs_u32 nlink;

	/**
	 * @brief Extended attribute index.
	 */
	sqfs_u32 xattr_idx;
};

/**
 * @struct sqfs_inode_slink_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_SLINK.
 *
 * The declaration does not contain the flexible array member of the symlink
 * target because @ref sqfs_inode_generic_t would otherwies be impossible to
 * implement without violating the C standard.
 */
struct sqfs_inode_slink_t {
	/**
	 * @brief Number of hard links to this node.
	 */
	sqfs_u32 nlink;

	/**
	 * @brief Size of the symlink target in bytes
	 */
	sqfs_u32 target_size;

	/*sqfs_u8 target[];*/
};

/**
 * @struct sqfs_inode_slink_ext_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_SLINK.
 *
 * The declaration does not contain the flexible array member of the symlink
 * target because it is wedged right in between the target size and the xattr
 * identifier.
 */
struct sqfs_inode_slink_ext_t {
	/**
	 * @brief Number of hard links to this node.
	 */
	sqfs_u32 nlink;

	/**
	 * @brief Size of the symlink target in bytes
	 */
	sqfs_u32 target_size;

	/*sqfs_u8 target[];*/

	/**
	 * @brief Extended attribute index.
	 */
	sqfs_u32 xattr_idx;
};

/**
 * @struct sqfs_inode_file_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_FILE.
 *
 * The declaration does not contain the flexible array member for the data
 * block sizes because @ref sqfs_inode_generic_t would otherwies be impossible
 * to implement without violating the C standard.
 *
 * For each data block, the inode is followed by a 32 bit integer that holds
 * the on-disk size of the compressed block in bytes and has bit number 24
 * set if the block is stored uncompressed.
 *
 * If a block size is specified as zero, it is assumed to be an entire block
 * filled with zero bytes.
 */
struct sqfs_inode_file_t {
	/**
	 * @brief Absolute position of the first data block.
	 */
	sqfs_u32 blocks_start;

	/**
	 * @brief Index into the fragment table or 0xFFFFFFFF if unused.
	 */
	sqfs_u32 fragment_index;

	/**
	 * @brief Offset into the uncompressed fragment block or 0xFFFFFFFF
	 *        if unused.
	 */
	sqfs_u32 fragment_offset;

	/**
	 * @brief Total, uncompressed size of the file in bytes.
	 */
	sqfs_u32 file_size;

	/*sqfs_u32 block_sizes[];*/
};

/**
 * @struct sqfs_inode_file_ext_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_FILE.
 *
 * @copydoc sqfs_inode_file_t
 */
struct sqfs_inode_file_ext_t {
	/**
	 * @brief Absolute position of the first data block.
	 */
	sqfs_u64 blocks_start;

	/**
	 * @brief Total, uncompressed size of the file in bytes.
	 */
	sqfs_u64 file_size;

	/**
	 * @brief If the file is sparse, holds the number of bytes not written
	 *        to disk because of the omitted sparse blocks.
	 */
	sqfs_u64 sparse;

	/**
	 * @brief Number of hard links to this node.
	 */
	sqfs_u32 nlink;

	/**
	 * @brief Index into the fragment table or 0xFFFFFFFF if unused.
	 */
	sqfs_u32 fragment_idx;

	/**
	 * @brief Offset into the uncompressed fragment block or 0xFFFFFFFF
	 *        if unused.
	 */
	sqfs_u32 fragment_offset;

	/**
	 * @brief Extended attribute index.
	 */
	sqfs_u32 xattr_idx;

	/*sqfs_u32 block_sizes[];*/
};

/**
 * @struct sqfs_inode_dir_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_DIR.
 */
struct sqfs_inode_dir_t {
	/**
	 * @brief Offset from the directory table start to the location of the
	 *        meta data block containing the first directory header.
	 */
	sqfs_u32 start_block;

	/**
	 * @brief Number of hard links to this node.
	 */
	sqfs_u32 nlink;

	/**
	 * @brief Combined size of all directory entries and headers in bytes.
	 */
	sqfs_u16 size;

	/**
	 * @brief Offset into the uncompressed start block where the header can
	 *        be found.
	 */
	sqfs_u16 offset;

	/**
	 * @brief Inode number of the parent directory containing
	 *        this directory inode.
	 */
	sqfs_u32 parent_inode;
};

/**
 * @struct sqfs_inode_dir_ext_t
 *
 * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_DIR.
 */
struct sqfs_inode_dir_ext_t {
	/**
	 * @brief Number of hard links to this node.
	 */
	sqfs_u32 nlink;

	/**
	 * @brief Combined size of all directory entries and headers in bytes.
	 */
	sqfs_u32 size;

	/**
	 * @brief Offset from the directory table start to the location of the
	 *        meta data block containing the first directory header.
	 */
	sqfs_u32 start_block;

	/**
	 * @brief Inode number of the parent directory containing
	 *        this directory inode.
	 */
	sqfs_u32 parent_inode;

	/**
	 * @brief Number of directory index entries following the inode
	 *
	 * This number is stored off-by one and counts the number of
	 * @ref sqfs_dir_index_t entries following the inode.
	 */
	sqfs_u16 inodex_count;

	/**
	 * @brief Offset into the uncompressed start block where the header can
	 *        be found.
	 */
	sqfs_u16 offset;

	/**
	 * @brief Extended attribute index.
	 */
	sqfs_u32 xattr_idx;
};

/**
 * @struct sqfs_inode_generic_t
 *
 * @brief A generic inode structure that combines all others and provides
 *        additional information.
 *
 * A few helper functions exist for working with this. For instance,
 * @ref sqfs_meta_reader_read_inode can read an inode from disk and assemble it
 * into an instance of this structure. Similarly, the
 * @ref sqfs_meta_writer_write_inode function can break it down into encoded,
 * on-disk structures and write them to disk.
 */
struct sqfs_inode_generic_t {
	/**
	 * @brief The common fields for all inodes.
	 */
	sqfs_inode_t base;

	/**
	 * @brief A pointer into the extra field holding the symlink target.
	 *
	 * @param This string is not null terminated. The helper functions rely
	 *        entirely on the length stored in the symlink inode.
	 */
	char *slink_target;

	/**
	 * @brief A pointer into the extra field holding file blocks sizes.
	 *
	 * For file inodes, holds the consecutive block sizes. Bit number 24 is
	 * set if the block is stored uncompressed. If it the size is zero,
	 * the block is sparse.
	 */
	sqfs_u32 *block_sizes;

	/**
	 * @brief For file inodes, stores the number of blocks used.
	 */
	size_t num_file_blocks;

	/**
	 * @brief For extended directory inodes, stores the number of payload
	 *        bytes following for the directory index.
	 */
	size_t num_dir_idx_bytes;

	/**
	 * @brief Type specific inode data.
	 */
	union {
		sqfs_inode_dev_t dev;
		sqfs_inode_dev_ext_t dev_ext;
		sqfs_inode_ipc_t ipc;
		sqfs_inode_ipc_ext_t ipc_ext;
		sqfs_inode_slink_t slink;
		sqfs_inode_slink_ext_t slink_ext;
		sqfs_inode_file_t file;
		sqfs_inode_file_ext_t file_ext;
		sqfs_inode_dir_t dir;
		sqfs_inode_dir_ext_t dir_ext;
	} data;

	/**
	 * @brief Holds type specific extra data, such as symlink target.
	 */
	sqfs_u8 extra[];
};

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief Create a deep copy of a generic inode.
 *
 * The @ref sqfs_inode_generic_t structure contains inlined fields that have a
 * size depending on the inode data and pointers to the inlined fields. This
 * helper function calculates the actual size of the structure in memory, makes
 * a copy and propperly sets up the pointers.
 *
 * @param src The inode to copy.
 * @param copy Returns a pointer to the copy on success. Can be released with a
 *             single free call.
 *
 * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has
 *         an unknown type set.
 */
SQFS_API int sqfs_inode_copy(const sqfs_inode_generic_t *src,
			     sqfs_inode_generic_t **copy);

/**
 * @brief Get the extended attribute index of an inode
 *
 * For basic inodes, this returns the inode index 0xFFFFFFFF, i.e. the
 * sentinel value indicating that there are no xattrs.
 *
 * @param inode A pointer to an inode.
 * @param out Returns the extended attribute index on success.
 *
 * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has
 *         an unknown type set.
 */
SQFS_API int sqfs_inode_get_xattr_index(const sqfs_inode_generic_t *inode,
					sqfs_u32 *out);

/**
 * @brief Set the extended attribute index of an inode.
 *
 * For basic inodes, this function promes the inodes to extended inodes if the
 * index is not 0xFFFFFFFF. If the index is 0xFFFFFFFF, the function tries to
 * demote extended inode to a basic inode after setting the index.
 *
 * @param inode A pointer to an inode.
 * @param index The extended attribute index.
 *
 * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has
 *         an unknown type set.
 */
SQFS_API int sqfs_inode_set_xattr_index(sqfs_inode_generic_t *inode,
					sqfs_u32 index);

/**
 * @brief Convert a basic inode to an extended inode.
 *
 * For inodes that already have an extended type, this is a no-op.
 *
 * @param inode A pointer to an inode.
 *
 * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has
 *         an unknown type set.
 */
SQFS_API int sqfs_inode_make_extended(sqfs_inode_generic_t *inode);

/**
 * @brief Convert an extended inode to a basic inode if possible.
 *
 * For inodes that already have a basic type, this is a no-op. If the inode
 * has values set that the coresponding basic type doesn't support (e.g. it
 * has an xattr index set or a regular file which requires 64 bit size
 * counter), it is left as an extended type and success state is returned.
 *
 * @param inode A pointer to an inode.
 *
 * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has
 *         an unknown type set.
 */
SQFS_API int sqfs_inode_make_basic(sqfs_inode_generic_t *inode);

/**
 * @brief Update the file size of a regular file inode.
 *
 * If the new size is wider than 32 bit, a basic file inode is transparently
 * promoted to an extended file inode. For extended inodes, if the new size
 * is small enough and was the only requirement for the extended type, the
 * node is transparently demoted to a basic file inode.
 *
 * @param inode A pointer to an inode.
 * @param size The new size to set.
 *
 * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is
 *         not a regular file.
 */
SQFS_API int sqfs_inode_set_file_size(sqfs_inode_generic_t *inode,
				      sqfs_u64 size);

/**
 * @brief Update the location of the first data block of a regular file inode.
 *
 * If the new location is wider than 32 bit, a basic file inode is
 * transparently promoted to an extended file inode. For extended inodes,
 * if the new size is small enough and was the only requirement for the
 * extended type, the node is transparently demoted to a basic file inode.
 *
 * @param inode A pointer to an inode.
 * @param location The new location to set.
 *
 * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is
 *         not a regular file.
 */
SQFS_API int sqfs_inode_set_file_block_start(sqfs_inode_generic_t *inode,
					     sqfs_u64 location);

/**
 * @brief Update the file fragment location of a regular file inode.
 *
 * @param inode A pointer to an inode.
 * @param index The new fragment index to set.
 * @param offset The new fragment offset to set.
 *
 * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is
 *         not a regular file.
 */
SQFS_API int sqfs_inode_set_frag_location(sqfs_inode_generic_t *inode,
					  sqfs_u32 index, sqfs_u32 offset);

/**
 * @brief Get the file size of a regular file inode.
 *
 * @param inode A pointer to an inode.
 * @param size Returns the file size.
 *
 * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is
 *         not a regular file.
 */
SQFS_API int sqfs_inode_get_file_size(const sqfs_inode_generic_t *inode,
				      sqfs_u64 *size);

/**
 * @brief Get the file fragment location of a regular file inode.
 *
 * @param inode A pointer to an inode.
 * @param index Returns the fragment index.
 * @param offset Returns the fragment offset.
 *
 * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is
 *         not a regular file.
 */
SQFS_API int sqfs_inode_get_frag_location(const sqfs_inode_generic_t *inode,
					  sqfs_u32 *index, sqfs_u32 *offset);

/**
 * @brief Get the location of the first data block of a regular file inode.
 *
 * @param inode A pointer to an inode.
 * @param location Returns the location.
 *
 * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is
 *         not a regular file.
 */
SQFS_API int sqfs_inode_get_file_block_start(const sqfs_inode_generic_t *inode,
					     sqfs_u64 *location);

/**
 * @brief Unpack the a directory index structure from an inode.
 *
 * The generic inode contains in its payload the raw directory index (with
 * bytes swapped to host enian), but still with single byte alignment. This
 * function seeks through the blob using an integer index (not offset) and
 * fiddles the raw data out into a propperly aligned, external structure.
 *
 * @param inode A pointer to an inode.
 * @param out Returns the index entry. Can be freed with a single free call.
 * @param index An index value between 0 and inodex_count.
 *
 * @return Zero on success, @ref SQFS_ERROR_OUT_OF_BOUNDS if the given index
 *         points outside the directory index. Can return other error codes
 *         (e.g. if the inode isn't a directory or allocation failed).
 */
SQFS_API
int sqfs_inode_unpack_dir_index_entry(const sqfs_inode_generic_t *inode,
				      sqfs_dir_index_t **out,
				      size_t index);

#ifdef __cplusplus
}
#endif

#endif /* SQFS_INODE_H */