/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----

                   Copyright (c) 2014-2015 Datalight, Inc.
                       All Rights Reserved Worldwide.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; use version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but "AS-IS," 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 General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*  Businesses and individuals that for commercial or other reasons cannot
    comply with the terms of the GPLv2 license may obtain a commercial license
    before incorporating Reliance Edge into proprietary software for
    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
    more information.
*/
/** @file
*/
#ifndef REDNODES_H
#define REDNODES_H


#define NODEHEADER_SIZE         (16U)
#define NODEHEADER_OFFSET_SIG   (0U)
#define NODEHEADER_OFFSET_CRC   (4U)
#define NODEHEADER_OFFSET_SEQ   (8U)

/** @brief Common header for all metadata nodes.
*/
typedef struct
{
    uint32_t    ulSignature;    /**< Value which uniquely identifies the metadata node type. */
    uint32_t    ulCRC;          /**< CRC-32 checksum of the node contents, starting after the CRC. */
    uint64_t    ullSequence;    /**< Current sequence number at the time the node was written to disk. */
} NODEHEADER;


/** Flag set in the master block when REDCONF_API_POSIX == 1. */
#define MBFLAG_API_POSIX        (0x01U)

/** Flag set in the master block when REDCONF_INODE_TIMESTAMPS == 1. */
#define MBFLAG_INODE_TIMESTAMPS (0x02U)

/** Flag set in the master block when REDCONF_INODE_BLOCKS == 1. */
#define MBFLAG_INODE_BLOCKS     (0x04U)

/** Flag set in the master block when (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1). */
#define MBFLAG_INODE_NLINK      (0x08U)


/** @brief Node which identifies the volume and stores static volume information.
*/
typedef struct
{
    NODEHEADER  hdr;                /**< Common node header. */

    uint32_t    ulVersion;          /**< On-disk layout version number. */
    char        acBuildNum[8U];     /**< Build number of the product (not null terminated). */
    uint32_t    ulFormatTime;       /**< Date and time the volume was formatted. */
    uint32_t    ulInodeCount;       /**< Compile-time configured number of inodes. */
    uint32_t    ulBlockCount;       /**< Compile-time configured number of logical blocks. */
    uint16_t    uMaxNameLen;        /**< Compile-time configured maximum file name length. */
    uint16_t    uDirectPointers;    /**< Compile-time configured number of direct pointers per inode. */
    uint16_t    uIndirectPointers;  /**< Compile-time configured number of indirect pointers per inode. */
    uint8_t     bBlockSizeP2;       /**< Compile-time configured block size, expressed as a power of two. */
    uint8_t     bFlags;             /**< Compile-time booleans which affect on-disk structures. */
} MASTERBLOCK;


#if REDCONF_API_POSIX == 1
#define METAROOT_HEADER_SIZE    (NODEHEADER_SIZE + 16U) /* Size in bytes of the metaroot header fields. */
#else
#define METAROOT_HEADER_SIZE    (NODEHEADER_SIZE + 12U) /* Size in bytes of the metaroot header fields. */
#endif
#define METAROOT_ENTRY_BYTES    (REDCONF_BLOCK_SIZE - METAROOT_HEADER_SIZE) /* Number of bytes remaining in the metaroot block for entries. */
#define METAROOT_ENTRIES        (METAROOT_ENTRY_BYTES * 8U)

/** @brief Metadata root node; each volume has two.
*/
typedef struct
{
    NODEHEADER  hdr;                /**< Common node header. */

    uint32_t    ulSectorCRC;        /**< CRC-32 checksum of the first sector. */
    uint32_t    ulFreeBlocks;       /**< Number of allocable blocks that are free. */
  #if REDCONF_API_POSIX == 1
    uint32_t    ulFreeInodes;       /**< Number of inode slots that are free. */
  #endif
    uint32_t    ulAllocNextBlock;   /**< Forward allocation pointer. */

    /** Imap bitmap.  With inline imaps, this is the imap bitmap that indicates
        which inode blocks are used and which allocable blocks are used.
        Otherwise, this bitmap toggles nodes in the external imap between one
        of two possible block locations.
    */
    uint8_t     abEntries[METAROOT_ENTRY_BYTES];
} METAROOT;


#if REDCONF_IMAP_EXTERNAL == 1
#define IMAPNODE_HEADER_SIZE    (NODEHEADER_SIZE) /* Size in bytes of the imap node header fields. */
#define IMAPNODE_ENTRY_BYTES    (REDCONF_BLOCK_SIZE - IMAPNODE_HEADER_SIZE) /* Number of bytes remaining in the imap node for entries. */
#define IMAPNODE_ENTRIES        (IMAPNODE_ENTRY_BYTES * 8U)

/** @brief One node of the external imap.
*/
typedef struct
{
    NODEHEADER  hdr;            /**< Common node header. */

    /** Bitmap which indicates which inode blocks are used and which allocable
        blocks are used.
    */
    uint8_t     abEntries[IMAPNODE_ENTRY_BYTES];
} IMAPNODE;
#endif


#define INODE_HEADER_SIZE   (NODEHEADER_SIZE + 8U + ((REDCONF_INODE_BLOCKS == 1) ? 4U : 0U) + \
    ((REDCONF_INODE_TIMESTAMPS == 1) ? 12U : 0U) + 4U + ((REDCONF_API_POSIX == 1) ? 4U : 0U))
#define INODE_ENTRIES       ((REDCONF_BLOCK_SIZE - INODE_HEADER_SIZE) / 4U)

#if (REDCONF_DIRECT_POINTERS < 0) || (REDCONF_DIRECT_POINTERS > (INODE_ENTRIES - REDCONF_INDIRECT_POINTERS))
  #error "Configuration error: invalid value of REDCONF_DIRECT_POINTERS"
#endif
#if (REDCONF_INDIRECT_POINTERS < 0) || (REDCONF_INDIRECT_POINTERS > (INODE_ENTRIES - REDCONF_DIRECT_POINTERS))
  #error "Configuration error: invalid value of REDCONF_INDIRECT_POINTERS"
#endif

/** @brief Stores metadata for a file or directory.
*/
typedef struct
{
    NODEHEADER  hdr;            /**< Common node header. */

    uint64_t    ullSize;        /**< Size of the inode, in bytes. */
#if REDCONF_INODE_BLOCKS == 1
    uint32_t    ulBlocks;       /**< Total number file data blocks allocated to the inode. */
#endif
#if REDCONF_INODE_TIMESTAMPS == 1
    uint32_t    ulATime;        /**< Time of last access (seconds since January 1, 1970). */
    uint32_t    ulMTime;        /**< Time of last modification (seconds since January 1, 1970). */
    uint32_t    ulCTime;        /**< Time of last status change (seconds since January 1, 1970). */
#endif
    uint16_t    uMode;          /**< Inode type (file or directory) and permissions (reserved). */
#if (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1)
    uint16_t    uNLink;         /**< Link count, number of names pointing to the inode. */
#else
    uint8_t     abPadding[2];   /**< Padding to 32-bit align the next member. */
#endif
#if REDCONF_API_POSIX == 1
    uint32_t    ulPInode;       /**< Parent inode number.  Only guaranteed to be accurate for directories. */
#endif

    /** Block numbers for lower levels of the file metadata structure.  Some
        fraction of these entries are for direct pointers (file data block
        numbers), some for indirect pointers, some for double-indirect
        pointers; the number allocated to each is static but user-configurable.
        For all types, an array slot is zero if the range is sparse or beyond
        the end of file.
    */
    uint32_t    aulEntries[INODE_ENTRIES];
} INODE;


#define INDIR_HEADER_SIZE   (NODEHEADER_SIZE + 4U)
#define INDIR_ENTRIES       ((REDCONF_BLOCK_SIZE - INDIR_HEADER_SIZE) / 4U)

/** @brief Node for storing block pointers.
*/
typedef struct
{
    NODEHEADER  hdr;        /**< Common node header. */

    uint32_t    ulInode;    /**< Inode which owns this indirect or double indirect. */

    /** For indirect nodes, stores block numbers of file data.  For double
        indirect nodes, stores block numbers of indirect nodes.  An array
        slot is zero if the corresponding block or indirect range is beyond
        the end of file or entirely sparse.
    */
    uint32_t    aulEntries[INDIR_ENTRIES];
} INDIR, DINDIR;


#endif

