/**********************************************************************/ /* This file contains the datastructures used by PFS */ /* */ /* If you have specific programming plans I would like to know about */ /* it. I can help you with any problems and can supply extra */ /* information, if so needed. I can supply you with PFS specific */ /* code too, like allocation or anode routines. */ /* Of course you're free to do whatever you want with the tools you */ /* make, but if you'll allow me, I would like to add them to the PFS */ /* package, or at least refer to it in the documentation. */ /* */ /**********************************************************************/ // max length of filename, diskname and comment #define FNSIZE 108 #define DNSIZE 32 #define CMSIZE 80 #define ID_PFS_DISK (0x50465300L) //'PFS\0' /**********************************************************************/ /* 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. ** ** DirEntry structure: ** ** The 'type', 'creationtime' and 'protection' are, accept 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 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 midnight UWORD creationtick; // ticks past minute UBYTE protection; // protection bits (ala ADOS) UBYTE nlength; // length 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 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; }; /* 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. */