diff options
-rw-r--r-- | doc/format.txt | 9 | ||||
-rw-r--r-- | include/sqfs/dir_writer.h | 7 | ||||
-rw-r--r-- | include/sqfs/inode.h | 11 | ||||
-rw-r--r-- | lib/sqfs/dir_writer.c | 6 |
4 files changed, 27 insertions, 6 deletions
diff --git a/doc/format.txt b/doc/format.txt index ca444f2..a783c07 100644 --- a/doc/format.txt +++ b/doc/format.txt @@ -664,6 +664,11 @@ +------+--------------+------------------------------------------------------+ | u16 | file size | Total (uncompressed) size in bytes of the entry | | | | listing in the directory table, including headers. | + | | | | + | | | This value is 3 bytes larger than the real listing. | + | | | The Linux kernel creates "." and ".." entries for | + | | | offsets 0 and 1, and only after 3 looks into the | + | | | listing, subtracting 3 from the size. | +------+--------------+------------------------------------------------------+ | u16 | block offset | The (uncompressed) offset within the metadata block | | | | in the directory table where the directory listing | @@ -679,8 +684,8 @@ directory. I.e. a directory with N entries has at least N + 2 link count. - If the "file size" is set to 0, the directory is empty and there is no - corresponding listing in the directory table. + If the "file size" is set to a value < 4, the directory is empty and there is + no corresponding listing in the directory table. An extended directory can have a listing that is at most 4GiB in size, may diff --git a/include/sqfs/dir_writer.h b/include/sqfs/dir_writer.h index 9615a41..7b35fe0 100644 --- a/include/sqfs/dir_writer.h +++ b/include/sqfs/dir_writer.h @@ -161,6 +161,11 @@ SQFS_API int sqfs_dir_writer_end(sqfs_dir_writer_t *writer); * size of the directory listing that is required for the directory inodes. * And also to determine which kind of directory inode to create. * + * Note that this size is only what was written to disk. SquashFS directory + * inodes need you to add 3 to this value, to account for "." and ".." entries + * which are not actually stored on disk. The @ref sqfs_dir_writer_create_inode + * function takes this into account and adds the 3 internally. + * * @param writer A pointer to a directory writer object. * * @return The size of the entire, uncompressed listing in bytes. @@ -226,6 +231,8 @@ SQFS_API size_t sqfs_dir_writer_get_index_size(const sqfs_dir_writer_t *writer); * convenience function called @ref sqfs_dir_writer_write_index to write the * index meta data after writing the inode itself. * + * @note The size is already adjusted for the required off-by-3 value. + * * @param writer A pointer to a directory writer object. * @param hlinks The number of hard links pointing to the directory. * @param xattr If set to something other than 0xFFFFFFFF, an extended diff --git a/include/sqfs/inode.h b/include/sqfs/inode.h index 0bc33db..8df7158 100644 --- a/include/sqfs/inode.h +++ b/include/sqfs/inode.h @@ -400,6 +400,15 @@ struct sqfs_inode_dir_t { /** * @brief Combined size of all directory entries and headers in bytes. + * + * The value stored here is off by 3 bytes, i.e. 3 bytes larger than + * the actual listing on disk. The Linux implementation of SquashFS + * uses readdir() offsets 0 and 1 to synthesize "." and ".." entries, + * and after that looks into the actual directory. + * + * Why store an off-by-3 value on disk instead of faking it in the + * kernel and have something consistent here? Beats me, but that's + * how it is implemented. */ sqfs_u16 size; @@ -428,7 +437,7 @@ struct sqfs_inode_dir_ext_t { sqfs_u32 nlink; /** - * @brief Combined size of all directory entries and headers in bytes. + * @brief Size of all directory entries and headers in bytes plus 3. */ sqfs_u32 size; diff --git a/lib/sqfs/dir_writer.c b/lib/sqfs/dir_writer.c index 0c76e8c..574e809 100644 --- a/lib/sqfs/dir_writer.c +++ b/lib/sqfs/dir_writer.c @@ -399,7 +399,7 @@ sqfs_inode_generic_t block_offset = writer->dir_ref & 0xFFFF; if (xattr != 0xFFFFFFFF || start_block > 0xFFFFFFFFUL || - writer->dir_size > 0xFFFF) { + writer->dir_size > (0xFFFF - 3)) { inode->base.type = SQFS_INODE_EXT_DIR; } else { inode->base.type = SQFS_INODE_DIR; @@ -408,12 +408,12 @@ sqfs_inode_generic_t if (inode->base.type == SQFS_INODE_DIR) { inode->data.dir.start_block = start_block; inode->data.dir.nlink = writer->ent_count + hlinks + 2; - inode->data.dir.size = writer->dir_size; + inode->data.dir.size = writer->dir_size + 3; inode->data.dir.offset = block_offset; inode->data.dir.parent_inode = parent_ino; } else { inode->data.dir_ext.nlink = writer->ent_count + hlinks + 2; - inode->data.dir_ext.size = writer->dir_size; + inode->data.dir_ext.size = writer->dir_size + 3; inode->data.dir_ext.start_block = start_block; inode->data.dir_ext.parent_inode = parent_ino; inode->data.dir_ext.offset = block_offset; |