root/src/bin/bfs_tools/lib/bfs.h
/* bfs - BFS definitions and helper functions
 *
 * Initial version by Axel Dörfler, axeld@pinc-software.de
 * Parts of this code is based on work previously done by Marcus Overhagen
 *
 * Copyright 2001-2025 pinc Software. All Rights Reserved.
 * This file may be used under the terms of the MIT License.
 */
#ifndef BFS_H
#define BFS_H


#include <SupportDefs.h>


// File types as used in BFS and stored in its inodes
// (coincidentally the same as in Haiku's sys/stat.h)
#define BFS_S_ATTR_DIR                  01000000000     /* attribute directory */
#define BFS_S_ATTR                              02000000000     /* attribute */
#define BFS_S_INDEX_DIR                 04000000000     /* index (or index directory) */
#define BFS_S_STR_INDEX                 00100000000     /* string index */
#define BFS_S_INT_INDEX                 00200000000     /* int32 index */
#define BFS_S_UINT_INDEX                00400000000     /* uint32 index */
#define BFS_S_LONG_LONG_INDEX   00010000000     /* int64 index */
#define BFS_S_ULONG_LONG_INDEX  00020000000     /* uint64 index */
#define BFS_S_FLOAT_INDEX               00040000000     /* float index */
#define BFS_S_DOUBLE_INDEX              00001000000     /* double index */

/* standard file types */
#define BFS_S_IFMT                              00000170000 /* type of file */
#define BFS_S_IFLNK                             00000120000 /* symbolic link */
#define BFS_S_IFREG                     00000100000 /* regular */
#define BFS_S_IFDIR                     00000040000 /* directory */

#define BFS_S_ISREG(mode)               (((mode) & BFS_S_IFMT) == BFS_S_IFREG)
#define BFS_S_ISLNK(mode)               (((mode) & BFS_S_IFMT) == BFS_S_IFLNK)
#define BFS_S_ISDIR(mode)               (((mode) & BFS_S_IFMT) == BFS_S_IFDIR)
#define BFS_S_ISINDEX(mode)             (((mode) & BFS_S_INDEX_DIR) == BFS_S_INDEX_DIR)


struct __attribute__((packed)) block_run {
        int32           allocation_group;
        uint16          start;
        uint16          length;
        
        inline bool operator==(const block_run &run) const;
        inline bool operator!=(const block_run &run) const;
        inline bool IsZero() const;
        inline void SetTo(int32 group, uint16 start, uint16 length = 1);

        inline static block_run Run(int32 group, uint16 start, uint16 length = 1);
};

typedef block_run inode_addr;

//**************************************


#define BFS_DISK_NAME_LENGTH    32

struct __attribute__((packed)) disk_super_block
{
        char            name[BFS_DISK_NAME_LENGTH];
        int32           magic1;
        int32           fs_byte_order;
        uint32          block_size;
        uint32          block_shift;
        int64           num_blocks;
        int64           used_blocks;
        int32           inode_size;
        int32           magic2;
        int32           blocks_per_ag;
        int32           ag_shift;
        int32           num_ags;
        int32           flags;
        block_run       log_blocks;
        int64           log_start;
        int64           log_end;
        int32           magic3;
        inode_addr      root_dir;
        inode_addr      indices;
        int32           pad[8];
};

#define SUPER_BLOCK_FS_LENDIAN          'BIGE'          /* BIGE */

#define SUPER_BLOCK_MAGIC1                      'BFS1'          /* BFS1 */
#define SUPER_BLOCK_MAGIC2                      0xdd121031
#define SUPER_BLOCK_MAGIC3                      0x15b6830e

#define SUPER_BLOCK_CLEAN                       'CLEN'          /* CLEN */
#define SUPER_BLOCK_DIRTY                       'DIRT'          /* DIRT */

//**************************************

#define NUM_DIRECT_BLOCKS                       12

struct __attribute__((packed)) data_stream
{
        block_run       direct[NUM_DIRECT_BLOCKS];
        int64           max_direct_range;
        block_run       indirect;
        int64           max_indirect_range;
        block_run       double_indirect;
        int64           max_double_indirect_range;
        int64           size;
};

// **************************************

struct bfs_inode;

struct __attribute__((packed)) small_data
{
        uint32          type;
        uint16          name_size;
        uint16          data_size;
        char            name[0];        // name_size long, followed by data

        inline char             *Name();
        inline uint8    *Data();
        inline small_data *Next();
        inline bool             IsLast(bfs_inode *inode);
};

// the file name is part of the small_data structure
#define FILE_NAME_TYPE                  'CSTR'
#define FILE_NAME_NAME                  0x13
#define FILE_NAME_NAME_LENGTH   1

// **************************************

#define SHORT_SYMLINK_NAME_LENGTH       144 // length incl. terminating '\0'

struct __attribute__((packed)) bfs_inode
{
        int32           magic1;
        inode_addr      inode_num;
        int32           uid;
        int32           gid;
        int32           mode;                           // see sys/stat.h
        int32           flags;
        int64           create_time;
        int64           last_modified_time;
        inode_addr      parent;
        inode_addr      attributes;
        uint32          type;                           // attribute type

        int32           inode_size;
        uint32          etc;                            // for in-memory structures (unused in Haiku' fs)

        union __attribute__((packed)) {
                data_stream             data;
                char                    short_symlink[SHORT_SYMLINK_NAME_LENGTH];
        };
        int32           pad[4];
        small_data      small_data_start[0];
};

#define INODE_MAGIC1                    0x3bbe0ad9
#define INODE_TIME_SHIFT                16
#define INODE_FILE_NAME_LENGTH  256

enum inode_flags
{
        INODE_IN_USE                    = 0x00000001,   // always set
        INODE_ATTR_INODE                = 0x00000004,
        INODE_LOGGED                    = 0x00000008,   // log changes to the data stream
        INODE_DELETED                   = 0x00000010,
        INODE_EMPTY                             = 0x00000020,
        INODE_LONG_SYMLINK              = 0x00000040,   // symlink in data stream

        INODE_PERMANENT_FLAGS   = 0x0000ffff,

        INODE_NO_CACHE                  = 0x00010000,
        INODE_WAS_WRITTEN               = 0x00020000,
        INODE_NO_TRANSACTION    = 0x00040000
};

//**************************************


inline int32
divide_roundup(int32 num,int32 divisor)
{
        return (num + divisor - 1) / divisor;
}

inline int64
divide_roundup(int64 num,int32 divisor)
{
        return (num + divisor - 1) / divisor;
}

inline int
get_shift(uint64 i)
{
        int c;
        c = 0;
        while (i > 1) {
                i >>= 1;
                c++;
        }
        return c;
}

inline int32
round_up(uint32 data)
{
        // rounds up to the next off_t boundary
        return (data + sizeof(off_t) - 1) & ~(sizeof(off_t) - 1);
}

/************************ block_run inline functions ************************/
//      #pragma mark -


inline bool block_run::operator==(const block_run &run) const
{
        return allocation_group == run.allocation_group
                && start == run.start
                && length == run.length;
}

inline bool block_run::operator!=(const block_run &run) const
{
        return allocation_group != run.allocation_group
                || start != run.start
                || length != run.length;
}

inline bool block_run::IsZero() const
{
        return allocation_group == 0 && start == 0 && length == 0;
}

inline void block_run::SetTo(int32 _group,uint16 _start,uint16 _length)
{
        allocation_group = _group;
        start = _start;
        length = _length;
}

inline block_run block_run::Run(int32 group, uint16 start, uint16 length)
{
        block_run run;
        run.allocation_group = group;
        run.start = start;
        run.length = length;
        return run;
}


/************************ small_data inline functions ************************/
//      #pragma mark -


inline char *small_data::Name()
{
        return name;
}

inline uint8 *small_data::Data()
{
        return (uint8 *)name + name_size + 3;
}

inline small_data *small_data::Next()
{
        return (small_data *)((uint8 *)(this + 1) + name_size + 3 + data_size + 1);
}

inline bool small_data::IsLast(bfs_inode *inode)
{
        return (addr_t)this > (addr_t)inode + inode->inode_size - sizeof(small_data)
                   || name_size == 0;
}

#endif  /* BFS_H */