/*
 * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv@gmail.com>
 *
 * 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; either version 2 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 General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#ifndef _UFS_H_
#define _UFS_H_

#include <stdint.h>

/* Sector addresses */
#define UFS1_SBLOCK_OFFSET	8192
#define UFS2_SBLOCK_OFFSET	65536
#define UFS2_SBLOCK2_OFFSET	262144

#define UFS1_ADDR_SHIFT 2
#define UFS2_ADDR_SHIFT 3

/* Super magic numbers */
#define UFS1_SUPER_MAGIC	(0x011954)
#define UFS2_SUPER_MAGIC	(0x19540119)

#define UFS_ROOT_INODE 2

#define UFS_DIRECT_BLOCKS 12
#define UFS_INDIRECT_BLOCK 1
#define UFS_DOUBLE_INDIRECT_BLOCK 1
#define UFS_TRIPLE_INDIRECT_BLOCK 1
/* Total number of block addr hold by inodes */
#define UFS_NBLOCKS 15

/* Blocks span 8 fragments */
#define FRAGMENTS_PER_BLK 8

/* UFS types */
typedef enum {
    NONE,
    UFS1,
    UFS2,
    UFS2_PIGGY,
} ufs_t;

/*
 * UFS1/UFS2 SUPERBLOCK structure
 * CG stands for Cylinder Group.
 *
 * Variables prepended with off store offsets relative to
 * base address of a Cylinder Group (CG).
 */
struct ufs_super_block { // supporting either ufs1 or ufs2
    uint8_t  unused[8];
    /* Offset values */
    uint32_t off_backup_sb; // Backup super block
    uint32_t off_group_desc; // Group Descriptor
    uint32_t off_inode_tbl; // Inode table
    uint32_t off_data_block; // First data block
    union {
	struct {  /* Used for UFS1 */
	    uint32_t delta_value; // For calc staggering offset
	    uint32_t cycle_mask; // Mask for staggering offset
	    uint32_t last_written; // Last written time
	    uint32_t nr_frags; // Number of frags in FS
	    uint32_t storable_frags_nr; // Nr of frags that can store data
	} ufs1;
	uint8_t unused1[20];
    };
    uint32_t nr_cyl_groups; // Number of cylinder groups.
    uint32_t block_size; // Block size in bytes.
    uint32_t fragment_size; // Fragment size in bytes.
    uint8_t  unused2[16];
    uint32_t block_addr_mask; // to calculate the address
    uint32_t frag_addr_mask;
    uint32_t block_shift; // to calculate byte address
    uint32_t frag_shift;
    uint32_t nr_contiguous_blk; // max number of continuous blks to alloc
    uint32_t nr_blks_per_cg; // max number of blks per cylinder group
    uint32_t c_blk_frag_shift; // Bits to convert blk and frag address.
    uint32_t c_frag_sect_shift; // Bits to convert frag and sect address.
    uint32_t superblk_size; // Superblock size.
    uint8_t  unused3[76];
    uint32_t inodes_per_cg; // Inodes per cylinder group
    uint32_t frags_per_cg; // Fragments per cylinder group
    union {
	struct { /* Used for UFS2 */
	    uint8_t  unused[888];
	    uint64_t nr_frags; // Number of fragments in FS
	    uint8_t  unused1[232];
	} ufs2;
	uint8_t unused4[1128];
    };
    uint32_t maxlen_isymlink; // Max length of internal symlink
    uint32_t inodes_format; // Format of inodes
    uint8_t  unused5[44];
    uint32_t magic; // Magic value
    uint8_t  pad[160]; // padding up to sector (512 bytes) boundary
} __attribute__((__packed__));

/*
 * Info about UFS1/2 super block.
 */
struct ufs_sb_info {
    uint32_t blocks_per_cg; // Blocks per cylinder group
    uint32_t inodes_per_cg; // Inodes per cylinder group
    uint32_t inode_size;
    uint32_t inodes_per_block; // Inodes per block
    struct { /* UFS1 only! */
	/* Values for calculating staggering offset */
	uint32_t delta_value;
	uint32_t cycle_mask;
    } ufs1;
    uint32_t off_inode_tbl; // Inode table offset.
    uint32_t groups_count; // Number of groups in the fs
    uint32_t addr_shift; // 2 ^ addr_shift = size in bytes of default addr.
    uint32_t c_blk_frag_shift; // Convert blk/frag addr (vice-versa)
    uint32_t maxlen_isymlink; // Max length of internal symlink
    struct inode *(*ufs_iget_by_inr)(struct fs_info *, uint32_t);
    void (*ufs_read_blkaddrs)(struct inode *, char *);
    ufs_t    fs_type; // { UFS1, UFS2, UFS2_PIGGY }
};

/*
 * Get super block info struct
 */
static inline struct ufs_sb_info *UFS_SB(struct fs_info *fs)
{
    return fs->fs_info;
}

/*
 * Convert frag addr to blk addr
 */
static inline block_t frag_to_blk(struct fs_info *fs, uint64_t frag)
{
    return frag >> UFS_SB(fs)->c_blk_frag_shift;
}

/*
 * UFS1 inode structures
 */
struct ufs1_inode {
    uint16_t file_mode;
    uint16_t link_count;
    uint8_t  unused[4];
    uint64_t size;
    uint32_t a_time; // Access time
    uint32_t a_time_nanosec;
    uint32_t m_time; // Modified time
    uint32_t m_time_nanosec;
    uint32_t ch_time; // Change time
    uint32_t ch_time_nanosec;
    uint32_t direct_blk_ptr[12];
    uint32_t indirect_blk_ptr;
    uint32_t double_indirect_blk_ptr;
    uint32_t triple_indirect_blk_ptr;
    uint32_t flags; // Status flags
    uint32_t blocks_held; // Blocks held
    uint32_t generation_nrb; // (NFS)
    uint32_t used_id;
    uint32_t group_id;
    uint8_t  unused1[8];
} __attribute__((__packed__));

/*
 * UFS2 inode structures
 */
struct ufs2_inode {
    uint16_t file_mode;
    uint16_t link_count;
    uint32_t user_id;
    uint32_t group_id;
    uint32_t inode_blocksize;
    uint64_t size;
    uint64_t bytes_held;
    uint64_t a_time; // Access time
    uint64_t m_time; // Modified time
    uint64_t ch_time; // Change time
    uint64_t creat_time; // Creation time
    uint32_t a_time_nanosec;
    uint32_t m_time_nanosec;
    uint32_t ch_time_nanosec;
    uint32_t creat_time_nanosec;
    uint32_t generation_nrb; // (NFS)
    uint32_t kernel_flags;
    uint32_t flags;
    uint32_t ext_attr_size; // Extended attrib size.
    uint64_t ext_direct_blk_ptrs[2]; // Ext. attrib blk pointers.
    uint64_t direct_blk_ptr[12];
    uint64_t indirect_blk_ptr;
    uint64_t double_indirect_blk_ptr;
    uint64_t triple_indirect_blk_ptr;
    uint8_t  unused[24];
} __attribute__((__packed__));

#define PVT(p) ((struct ufs_inode_pvt *) p->pvt)

struct ufs_inode_pvt {
    uint64_t direct_blk_ptr[12];
    uint64_t indirect_blk_ptr;
    uint64_t double_indirect_blk_ptr;
    uint64_t triple_indirect_blk_ptr;
};

struct ufs_dir_entry {
    uint32_t inode_value;
    uint16_t dir_entry_len;
    uint8_t  file_type;
    uint8_t  name_length;
    uint8_t  name[1]; // Dir names are null terminated!!!
} __attribute__((__packed__));

enum inode_type_flags {
    UFS_INO_FIFO	= 0x1000,
    UFS_INO_CHARDEV	= 0x2000,
    UFS_INO_DIRECTORY 	= 0x4000,
    UFS_INO_BLOCKDEV	= 0x6000,
    UFS_INO_RFILE	= 0x8000,
    UFS_INO_SYMLINK	= 0xA000,
    UFS_INO_UNIXSOCKET 	= 0xC000,
};

enum dir_type_flags {
    UFS_DTYPE_UNKNOWN	= 0,
    UFS_DTYPE_FIFO 	= 1,
    UFS_DTYPE_CHARDEV	= 2,
    UFS_DTYPE_DIR 	= 4,
    UFS_DTYPE_BLOCK	= 6,
    UFS_DTYPE_RFILE	= 8,
    UFS_DTYPE_SYMLINK 	= 10,
    UFS_DTYPE_SOCKET	= 12,
    UFS_DTYPE_WHITEOUT 	= 14,
};

/* Functions from bmap.c */
extern uint64_t ufs_bmap (struct inode *, block_t, size_t *);
extern int ufs_next_extent(struct inode *, uint32_t);

#define ufs_debug dprintf
//extern void ufs_checking (struct fs_info *);

#endif /* _UFS_H_ */