/*
  This file describes the layout of the metadata blocks for the dtfs file system.

  (C) 1998 Christian Czezatke

  $Id: dtfs_header.h,v 1.4 1998/04/01 09:23:16 ceci Exp $

  $Log: dtfs_header.h,v $
  Revision 1.4  1998/04/01 09:23:16  ceci
  Updated data structures to make versioning support less brain-damaged

  Revision 1.3  1998/03/30 17:20:05  ceci
  CHK_REMOVEDVERSION was misnamed to SEG_REMOVEDVERSION. Fixed that
  Oops, forgot timestamp in dtfs_checkpoint_block

  Revision 1.2  1998/03/30 16:13:25  ceci
  Fixed up support for versioning
  Checked block structures to be 512 bytes each

  Revision 1.1  1998/03/30 08:41:23  ceci
  Initial revision


 */

#ifndef __dtfs_header_h__
#define __dtfs_header_h__




#define DTFS_MAGIC 0xd1f5d1f5 

typedef unsigned int uint32;
typedef int int32;
typedef unsigned short uint16;
typedef short int16;
typedef unsigned int inode_id;



#define DF_READONLY 1
#define DF_UMOUNT_CLEAN 2

#define SEG_LOCKED 1
#define SEG_CLEAN 2
#define SEG_HAS_CHECKPOINT 4
#define SEG_HAS_BADBLOCK 8 /* Segments with bad blocks will not be used anymore */

#define CHK_LATEST 1
#define CHK_SNAPSHOT 2
#define CHK_READONLY 4

#define NO_TIMESTAMP 0

/*
  dtfs will have three "special files" that contain 
  filesystem-specific information:
  +) ifile -- Inode block log. to phys. translation
              (an array of phys. blockaddresses)
  +) atime -- Accesstime for every inode (an array
              of dtfs_atime_entries)
  +) segsum-- segment summary information. -- For now
              just a bitmap indicating free/used segments

  getting the inode location for inode i:
  phys_block = i / (dtfs_filesystem_descriptor.inodes_per_fsblocks);
  offset = i % (dtfs_filesystem_descriptor.inodes_per_fsblocks);
  read in the physical block address from position "phys_block" from ifile
*/


/*
  an entry in the atime file
*/
struct dtfs_atime_entry {
	uint32 atime;   /* Time of last access (not stored in the inode) */
	uint32 version; /* file version */
};


/*
  A filesystem descriptor. There could be more than one
  (traditional) filesystem within one dtfs file system. 
  Maybe somebody wants to implement that one day...
*/
struct dtfs_filesystem_descriptor { /* 48 bytes */
	uint32 tradfs_super_block;  /* location of the superblock for the trad. filesystem */
	inode_id ifile_inode;       /* reserved inode id for the ifile */
	inode_id atime_inode;       /* reserved inode id for the atime file */
	inode_id segusage_inode;    /* reserved inode id for the segusage file */
	inode_id root_inode;        /* reserved indoe id for the root inode */
	uint32 descriptor_flags;     /* see the DF_* constants */
	uint32 reserved1;           /* for the lost+found inode ? */
	uint16 inodes_per_fsblock;  /* How many inodes fit into one filesystem block */
	uint16 reserved2;
	uint32 checkpoints_1[2];      /* Two checkpoints for this file system */
	uint32 checkpoints_2[2];      /* Two checkpoints for this file system */
	/* the filesystem has actually only two checkpoints, but every checkpoint is replicated 
	   at the end of the device */

};


/*
  Layout for the DTFS super block
*/
struct dtfs_super_block { /* 512 bytes */
	uint32 magic;
	uint16 version;
	uint16 blocksize; /* size of filesystem block in bytes */
	uint32 flags;     /* umount cleanly ? */
	uint32 blocks_in_filesystem;
	uint32 blocks_per_segment;
	uint32 blocks_in_first_seg;    /* first and last segment could be smaller */
	uint32 blocks_in_last_seg;
	uint32 super_replicaton_count; /* every nth seg. has sblock copies */
	uint32 reserved_blocks_start;  /* how many blocks are reserved ad the beginning */
	uint32 reserved_blocks_end;    /* reserved blocks at the device end */
	uint16 num_filesystem_descriptors; /* # of valid entries in filesystems */
	uint16 filesystem_descriptor_size; /* size of one descriptor in filesystems */
	/* 44 bytes so far */
	uint32 pad[9];

	struct dtfs_filesystem_descriptor filesystems[9]; /* to make up 512 bytes */
};

/*
  Structure of a checkpoint entry. A checkpoint block
  consists of several dtfs_checkpoint_entry structures
*/
struct dtfs_checkpoint_entry { /* 12 bytes */
	uint32 segment_checkpoint; /* blockaddr of segment header with checkpoint */
	uint32 timestamp;          /* logical timestamp of snapshot creation */
	uint16 flags;              /* See the CHK_* defines */
	unsigned char filesystem_version; /* version of the file system the entry points to -- unused */
	unsigned char predecessor; /* pointer to checkpoint entry of predecessor +1 */
};

/*
  Layout of a checkpoint block. We support more than one active checkpoint for a
  file system. This will enable us to do things like versioning and snapshots
  of a file system in the future.
*/
struct dtfs_checkpoint_block {
	uint16 checkpoint_entry_size; 
	uint16 num_entries;                    /* # of valid entries in block */
	uint32 timestamp;
	struct dtfs_checkpoint_entry checkpoints[42]; /* to make up 512 bytes */
};


/*
 * Segment information starts here
 */


/*
  A dirlog entry represents a directory manipulation entry for a segment.
  BSD LFS uses segment batching to avoid the dirlog, but this seems to
  complicate things so we leave it aside for now...

  Contrary to Sprite LFS we don't save the name of the directory being created
  (this information seems to be rather useless anyway...)

*/
struct dtfs_dirlog_entry { /* 16 bytes */
	char optype; /* c l u r: create, link, unlink, remove ??? */
	char pad[3];
	uint32 parent_blocknum; /* # of block in the parent that holds the reference ti child */
	inode_id parent;        /* inode of parent dir */
	inode_id child;         /* inode of affected file/dir */
};


/*
  A description for a data block in the partial segment. 
*/
struct dtfs_blkdesc_entry { /* 16 bytes */
	char blocktype; /* i 1 2 3 d: inode 1,2,3 indirect, data */
	unsigned char filesys_id; /* filesystem the block belongs to */
	unsigned char filesys_rev; /* revision of the filesystem the block belongs to -- unused */
	unsigned char flags; /* would be convenient to know whether the block belongs to a dir or a file */
	inode_id owner; /* inode of file the block belongs to (for 1 2 3 d) */
	uint32 blocknum; /* # of block in the owning file */
	uint32 version; /* file version the block belongs to */
};


/* blkdesk and dirlog have the same size. -- convenient... */

/*
  An entry in the segment database is either a block description
  or a directory log entry. -- can be determined by 1st char...
*/
union dtfs_segdatabase_entry {
	struct dtfs_dirlog_entry d;
	struct dtfs_blkdesc_entry e;
};

/*
  A segment database block is just an array of segment database entries
*/

struct dtfs_segdatabase_block {
	union dtfs_segdatabase_entry segdb[32]; /* 32 entries w. 16 byte = 512 byte */
};


/*
  A block describing a partial segment (will be written at the end
  of the partial segment)
*/

struct dtfs_psegment_block { /* 512 bytes */
	uint32 timestamp;   /* creation time */
	uint32 blkaddr_prev_segment;
	uint32 blkaddr_next_segment; /* log chain */
	uint32 flags; /* see SEG_* defines */
	uint16 checkpoint_entries[2]; /* index of the checkpoint entry(ies) in the checkpoint block
					 can be 2 if new version is created */
	uint32 physaddr_special_inodes; 
	        /* if checkpoint: address of ifile, segusage, atime inode block  */
	uint32 num_dirlogs; /* total # of dirlogs in segment */
	uint32 num_blkdesc; /* total # of block descriptions in segment */
	uint32 segsize; /* size of this segment in logical blocks */
	uint32 checksum_1;
	uint32 checksum_2;
	unsigned char filesystem_id; /* if the part. segment is a checkpoint, this
					indicates for which filesystem it is one */
	unsigned char pad1;
	uint16 pad2;

	union dtfs_segdatabase_entry segdb[29];
	/* there is room for 29 segment database entries in the segment block itself */
};    


#endif
