home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Meeting Pearls 3
/
Meeting_Pearls_III.iso
/
Pearls
/
disk
/
Devs+Handler
/
PFS75
/
ProgrammersInfo
< prev
next >
Wrap
Text File
|
1994-05-15
|
13KB
|
430 lines
/**********************************************************************/
/* This file contains the datastructures used by PFS */
/* Most structures that are not directly written to disk are apt */
/* to change. */
/**********************************************************************/
// max lengte van filename, diskname en comment
#define FNSIZE 108
#define DNSIZE 32
#define CMSIZE 80
#define ID_PFS_DISK (0x50465300L) //'PFS\0'
typedef unsigned char *DSTR; // lengte, dan karakters
// IsDir(x) en IsFile(x) Argument: union objectinfo
#define IsDir(oi) ((oi).file.direntry && ((oi).file.direntry->type)>0)
#define IsFile(oi) ((oi).file.direntry && ((oi).file.direntry->type)<=0)
#define IsVolume(oi) ((oi).volume.root==0)
#define IsSameOI(oi1, oi2) ((oi1.file.direntry == oi2.file.direntry) && \
(oi1.file.dirblock == oi2.file.dirblock))
// IsRoot(fi) checked of *oi bij de rootdir hoort
// IsRootA(fi) checked of oi bij de rootdir hoort
#define IsRoot(oi) (((oi)==NULL) || ((oi)->volume.root == NULL))
#define IsRootA(oi) ((oi).volume.root == NULL)
// *lock -> *fileentry
#define LOCKTOFILEENTRY(l) ((struct fileentry *)(((UBYTE*)l)-4))
/**********************************************************************/
/* File administration */
/**********************************************************************/
/* FileInfo
**
** This are internal representations of files. A volumeinfo is recognized
** by a zero root field.
**
** The fileinfo->direntry en fileinfo->dirblock directly point into a
** cached directory block. That block is guaranteed to be present.
** If a pointer to a objectinfo is NULL, the root of the current
** volume is meant.
*/
struct fileinfo
{
struct direntry *direntry;
struct cdirblock *dirblock;
};
struct volumeinfo
{
ULONG root; // always 0
struct volumedata *volume;
};
union objectinfo
{
struct fileinfo file; // 0 =>it's a volumeinfo; <>0 => it's a fileinfo
struct volumeinfo volume;
};
/**********************************************************************/
/* Fileentries/locks & volumes */
/**********************************************************************
**
** Three structures with the same basis, but different length. The are
** all coupled with 'next'.
** lock->fl_Key is the directoryblocknr
*/
/* entrytype's
** PS: ETF_VOLUME and ETF_LOCK are both LOCKS! TEST ON BOTH
*/
#define ET_VOLUME 0x0004
#define ET_FILEENTRY 0x0008
#define ET_LOCK 0x000c
#define ETF_VOLUME 1
#define ETF_FILEENTRY 2
#define ETF_LOCK 3
#define ET_SHAREDREAD 0
#define ET_SHAREDWRITE 1
#define ET_EXCLREAD 2
#define ET_EXCLWRITE 3
#define IsVolumeEntry(e) ((e)->type.flags.type == ETF_VOLUME)
#define IsFileEntry(e) ((e)->type.flags.type == ETF_FILEENTRY)
#define IsLockEntry(e) ((e)->type.flags.type == ETF_LOCK)
#define IsVolumeLock(le) ((le)->type.flags.type == ETF_VOLUME)
#define SHAREDLOCK(t) ((t).flags.access <= 1)
// ANODENR for fe's and le's; FIANODENR for fileinfo's; NOT FOR VOLUMES!!
#define ANODENR(fe) ((fe)->info.file.direntry->anode)
#define FIANODENR(fi) ((fi)->direntry->anode)
union listtype
{
struct
{
unsigned pad:11;
unsigned dir:1; // 0 = file; 1 = dir or volume
unsigned type:2; // 0 = unknown; 3 = lock; 1 = volume; 2 = fileentry
unsigned access:2; // 0 = read shared; 2 = read excl; 1,3 = write shared, excl
} flags;
UWORD value;
};
typedef union listtype listtype;
/* Listentry
**
**- All locks on a volume are coupled from volume->firstfe by 'next'.
** The end of the chain is 0.
**- [volume] points back to the volume
**- [info] is {NULL, don't care} if root.
*/
/* the general structure */
struct listentry
{
struct listentry *next; // the link
struct FileLock lock; // contains accesstype, dirblocknr
listtype type;
union objectinfo info; // directory reference
struct volumedata *volume; // the volume the lock is on
};
/* the specific structures */
struct fileentry
{
struct fileentry *next;
struct FileLock lock;
listtype type;
union objectinfo info;
struct volumedata *volume;
UWORD currentanode; // anodenr belonging to offset in file
UWORD anodeoffset; // blocknr within current anode
UWORD blockoffset; // byteoffset within current block
ULONG offset; // offset from start of file
};
struct lockentry
{
struct lockentry *next;
struct FileLock lock;
listtype type;
union objectinfo info;
struct volumedata *volume;
UWORD nextanode; // used for examinenext
struct fileinfo nextentry; // and examineall
};
/* Converts DOS BCPL Lock pointer (returned by Lock()) to a lockentry
** pointer
*/
#define LockEntryFromLock(x) ((x) ? \
(struct lockentry*)((UBYTE*)BADDR(x)-offsetof(struct lockentry, lock)) : 0)
#define ListEntryFromLock(x) ((x) ? \
(struct listentry*)((UBYTE*)BADDR(x)-offsetof(struct lockentry, lock)) : 0)
/**********************************************************************/
/* Disk administration */
/**********************************************************************/
struct volumedata
{
struct volumedata *next; // volumechain
struct DeviceList *devlist;
struct rootblock *rootblk;
struct listentry *firstfe; // the fileentrylist
struct canodeblock *firstanblk; // the cached anodeblocklist
struct cdirblock *firstdirblk; // the cached dirblocklist
BOOL rootblockchangeflag;
WORD numsofterrors;
WORD diskstate; // normally ID_VALIDATED
LONG numblocks; // total number of blocks
LONG numblocksused;
LONG numblocksfree;
WORD bytesperblock;
};
/**********************************************************************/
/* Blockformat */
/**********************************************************************/
#define DBLKID 0x4442 //'DB'
#define ABLKID 0x4142 //'AB'
/* Directory blocks
**
** dirblock structure:
**
** This is what a dirblock looks like on disk. Reserved fields are for
** future extension.
**
** CDirBlock structure:
**
** This is how a dirblock is cached.
**
** DirEntry structure:
**
** The 'type', 'creationtime' and 'protection' are, except for their
** size, in DOS format.
** The filename and the comment are dynamic: size followed by that
** number of characters (like BSTRs). The comment directly follows the
** filename. The 'next' field contains the size of the direntry. This
** should always be even. The end of the directoryblock is marked by
** next = 0.
**
** The size of a direntry can be calculated by:
** size = (sizeof(struct direntry) + strlen(name) + strlen(comment))&0xfe
*/
#define DB_HEADSPACE (6 + 10)
#define DB_ENTRYSPACE (TD_SECTOR - 16)
struct dirblock // voor disk only (WORD blokno), timestamp later..
{
UWORD id; // 'DB'
ULONG reserved_1[2];
UWORD reserved_2;
UWORD anodenr; // anodenr belonging to this directory (points to FIRST block of dir)
UWORD parent; // parent; ANODE_ROOTDIR = root
UBYTE entries[DB_ENTRYSPACE]; // entries, fini by NULL
};
struct cdirblock
{
struct cdirblock *next; // voor cachelist
struct volumedata *volume;
UWORD blocknr; // for already cached check
UWORD usecount;
UWORD changeflag;
UWORD done;
struct dirblock blk;
};
struct direntry
{
UBYTE next; // sizeof direntry
BYTE type; // dir, file, link etc
UWORD anode; // anode nummer
ULONG size; // sizeof file
UWORD creationday; // days since Jan. 1, 1978 (ala ADOS; WORD ipv LONG)
UWORD creationminute; // minutes past modnight
UWORD creationtick; // ticks past minute, not really needed
UBYTE protection; // protection bits (ala ADOS)
UBYTE nlength; // lenght of filename
UBYTE startofname; // filename, followed by commentlen & comment
UBYTE pad; // make size even
};
// comment: de is struct direntry *
#define COMMENT(de) ((UBYTE*)(&((de)->startofname) + (de)->nlength))
#define OBJECTNAME(de) ((UBYTE*)(&(de->startofname)))
// get a pointer to the next direntry; de = struct direntry *
#define NEXTENTRY(de) ((struct direntry*)((UBYTE*)(de) + (de)->next))
// get a pointer to the firste direntry; blok is struct dirblock *
#define FIRSTENTRY(blok) ((struct direntry*)((blok)->blk.entries))
#define ENTRYLEN(de, comment) ((sizeof(struct direntry) + (de)->nlength + strlen(comment))&0xfffe)
/* Anode blocks and Anodes
**
** All space on disk is allocated by anodes. Anodes are runlength
** lists: a blocknr and a number indicating how many blocks are
** allocated by this anode.
**
** The anodes are organized in an array spread over anodeblocks. The
** anodenr is the index in this array. The anodeblock themselves are
** allocated in the rootblock.
**
** The first 6 anodes have a special function, they allocate
** freespace, the rootdirectory and perform system functions
**
** Every anodeblock contains ANODESINBLOCK anodes.
**
** struct canodeblock:
**
** The anodeblock in the cache..
**
** struct anode:
**
** The anode itself. The 'clustersize' is the number of blocks being
** allocated. The allocated blocks are 'blocknr' up to (but not
** including) 'blocknr' + 'clustersize'. The 'next' field is a link
** to another anode where the allocation continues. At the end of the
** anode chain next = 0.
**
** If blocknr = 0 the anode is free, and can be reused. If the anode
** contains { 0, -1, next} the anode is reserved by a file or
** directory but is not yet in use. PS: empty anodes, with clustersize
** 0, are valid.
** Vrijgeven van een anode: clustersize = 0; blocknr = 0; next = 0;
*/
#define ANODESINBLOCK 82
/* anode 0 is reserved; EOF; must be 0, -1, 0 (use for endoflist detection)
** ANODE_FREESPACE MUST have ANODE_RESERVED linked. All reserved blocks
** must be at the end of the chain. There may not be 'mixed' anodes
** anywhere
**
** Anodes < 6 may be {0,0,0} but should NEVER be allocated for other
** purposes.
** ANODE_FREESPACE, ANODE_BADBLOCKS and ANODE_EOF should always be
** reserved {0, -1, 0}.
** ANODE_USERFIRST is the first anode usable for files etc.
*/
#define ANODE_EOF 0
#define ANODE_FREESPACE 1
#define ANODE_RESERVED 2
#define ANODE_TOBEFREED 3
#define ANODE_BADBLOCKS 4
#define ANODE_ROOTDIR 5
#define ANODE_USERFIRST 6
struct anode
{
UWORD clustersize;
UWORD blocknr;
UWORD next;
};
struct anodeblock
{
UWORD id;
ULONG reserved_1[2];
UWORD seqnr; // sequence number of anodeblock (starts with 0)
UWORD next; // chain, 0 = end
UWORD pad;
struct anode nodes[ANODESINBLOCK];
ULONG reserved_2;
};
struct canodeblock
{
struct canodeblock *next; // for cachelist
struct volumedata *volume;
UWORD blocknr;
UWORD usecount;
UWORD changeflag;
UWORD done;
struct anodeblock blk;
};
/* Data blocks
*/
#define DATAINBLOCK 512
struct datablock
{
UBYTE data[DATAINBLOCK]; // only data
};
/* Rootblock and bootblocks
*/
#define BOOTBLOCK1 0
#define BOOTBLOCK2 1
#define ROOTBLOCK 2
struct rootblock
{
LONG disktype; // 'FDOS' same place as AmigaDos puts id
LONG reserved_1[2];
UWORD creationday; // days since Jan. 1, 1978 (ala ADOS; WORD ipv LONG)
UWORD creationminute; // minutes past midnight
UWORD creationtick; // ticks past minute, not really needed
UWORD protection; // protection bits (ala ADOS)
UBYTE diskname[32]; // DSTR of diskname;
UWORD firstreserved; // first reserved block (must be 0)
UWORD lastreserved; // end of reserved area
LONG reserved_2[10];
WORD anodeblocks[42]; // 0->notallocated
UBYTE reserved_3[DATAINBLOCK - 96 - 2*42];
};
struct bootblock
{
LONG disktype;
UBYTE reserved[508];
};
/* Reserved areas and other notes
**
** Anode and directory blocks are allocated in a special reserved area
** at the start of the partition. The reserved area bounderies are defined
** in the rootblock and vary with disksize. The reserved area can
** overflow in the normal area and vice versa.
**
** Directories are allocated by anodes, but they don't support
** clustersizes other than 1.
**
** Allocation strategy
**
** PFS always always writes directory and anodeblocks in a different
** place than they came from. The same for files: overwritten files are
** actually written somewhere else. A kind of 'freed first reused last'
** (FFRL) algorithm is used, so old versions stay on the disk for a long
** time. Sometimes PFS will abandon FFRL if this can prevent fragmentation.
** Recovery tools could make use of this: old versions can be restored
** if the current one is corrupt, and deleted files can be recovered.
**
** Update strategy
**
** Dirty anode and dirblocks are written to disk before the rootblock.
** Because the rootblock allocates the anodeblock which allocate the
** directories, and every anode is written on a new position, all
** changes take effect at the moment the rootblock is written. Before
** that seemingly nothing has changed.
*/