//+++2004-08-31 // Copyright (C) 2004 Mike Rieker, Beverly, MA USA // // 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; version 2 of the License. // // 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 //---2004-08-31 /************************************************************************/ /* */ /* This is the general disk filesystem driver */ /* It is the fs dependent layer, implementing the native ozone disk fs */ /* */ /************************************************************************/ #define OZ_VDFS_VERSION 5 #include "ozone.h" #include "oz_dev_timer.h" #include "oz_dev_vdfs.h" #include "oz_hw_bootblock.h" #include "oz_io_console.h" #include "oz_io_disk.h" #include "oz_io_fs.h" #include "oz_knl_dcache.h" #include "oz_knl_devio.h" #include "oz_knl_event.h" #include "oz_knl_hw.h" #include "oz_knl_kmalloc.h" #include "oz_knl_procmode.h" #include "oz_knl_sdata.h" #include "oz_knl_section.h" #include "oz_knl_security.h" #include "oz_knl_shuthand.h" #include "oz_knl_spte.h" #include "oz_knl_status.h" #include "oz_knl_thread.h" #include "oz_knl_userjob.h" #include "oz_sys_dateconv.h" #include "oz_sys_xprintf.h" #define VOLNAME_MAX 64 #define FILENAME_MAX 64 #define SECATTR_MAX 256 #define HOMEBLOCK_VERSION 1 #define HEADER_VERSION 1 #define EXTEND_NOTRUNC OZ_FS_EXTFLAG_NOTRUNC #define EXTEND_NOEXTHDR OZ_FS_EXTFLAG_NOEXTHDR #define EXT_DIRCOUNT ((uLong)(-1)) // header.dircount value indicating it is an extension header // otherwise it is a prime header /* These filenumbers are special use and are the same for all volumes */ /* They all have the seq == 0, so they can't be entered, removed or deleted */ /* They are all entered in the root directory of the volume */ /* The numbers start at 1 and count from there. SACRED_FIDNUM_COUNT is the highest numbered (inclusive). */ #define SACRED_FIDNUM_ROOTDIRECTORY (1) #define SACRED_FIDNUM_INDEXHEADERS (2) #define SACRED_FIDNUM_INDEXBITMAP (3) #define SACRED_FIDNUM_STORAGEBITMAP (4) #define SACRED_FIDNUM_BOOTHOMEBLOCK (5) #define SACRED_FIDNUM_COUNT (5) /* Variable areas in the file header */ #define DFS_HEADER_AREA_FILNAME (0) #define DFS_HEADER_AREA_FILATTR (1) #define DFS_HEADER_AREA_SECATTR (2) #define DFS_HEADER_AREA_POINTER (3) #define DFS_HEADER_NAREAS (4) /* Point to the pointers in a file block */ #define POINTERS(__filex) (Pointer *)(__filex -> header.area + __filex -> header.areas[DFS_HEADER_AREA_POINTER].offs) /* Point to filename string in an header */ #define FILENAME(__header) (char *)((__header).area + (__header).areas[DFS_HEADER_AREA_FILNAME].offs) /* Get the end-of-file block and byte numbers from an header */ #define FILATTRS(__header) ((Filattr *)((__header).area + (__header).areas[DFS_HEADER_AREA_FILATTR].offs)) /* Test the directory bit */ #define GET_FATTRFL(__header) FILATTRS (__header) -> filattrflags #define IS_DIRECTORY(__header) ((GET_FATTRFL (__header) & OZ_FS_FILATTRFLAG_DIRECTORY) != 0) /* Test the write-thru flag bits */ #define FIS_WRITETHRU(__header) ((GET_FATTRFL (__header) & OZ_FS_FILATTRFLAG_WRITETHRU) != 0) #define VIS_WRITETHRU(__volume) ((__volume -> mountflags & OZ_FS_MOUNTFLAG_WRITETHRU) != 0) /* Our File-id structure */ #define Seq 24 struct OZ_VDFS_Fileid { uLong num; /* vbn in indexheader file */ unsigned seq : Seq; /* re-use sequence number for the header block */ uByte rvn; /* volume number (starting at 1) in the volume set */ }; /* On-disk directory pointers */ /* Directories are made of an array of cluster-sized 'blocks'. */ /* The eof pointer always includes all allocated blocks. */ /* In the directory as a whole as well as within each block, */ /* filenames (excluding version number) are sorted in */ /* ascending order by name, then descending version number. */ /* Each block starts with a null-terminated filename (excluding */ /* version nubmer). The null-terminated filename is followed by */ /* an zero-terminated array of Dirpnt's, sorted by descending */ /* version number. The last element of a Dirpnt array is a dummy */ /* with a version number of zero. The array of Dirpnt's are */ /* followed by the next null-terminated filename, etc., until */ /* either a null filename is found or the end-of-block is reached. */ /* Filenames do no span block boundaries. Dirpnt arrays do not span */ /* block boundaries. If a filename/dirpnt_array does not fit in a */ /* single directory block, the Dirpnt array is split into more than */ /* one array and a separate filename/dirpnt_array entry is made for */ /* each fragment. Thus, a given filename may appear in the directory */ /* more than once (but with different version numbers). */ typedef struct { uLong version; /* file version number */ OZ_VDFS_Fileid fileid; /* corresponding file-id */ } Dirpnt; /* On-disk file header structure */ typedef struct { OZ_Datebin create_date; /* date the file was created */ OZ_Datebin access_date; /* date the file was last accessed (set by oz_dev_vdfs_readvirtblock, oz_dev_vdfs_writevirtblock, writefilattr) */ OZ_Datebin change_date; /* date the attributes or the data were last changed (set by oz_dev_vdfs_writevirtblock, writefilattr) */ OZ_Datebin modify_date; /* date the data was last changed (set by oz_dev_vdfs_writevirtblock only) */ OZ_Datebin expire_date; /* date the file will expire (external archiving system use) */ OZ_Datebin backup_date; /* date the file was last backed up (external backup system use) */ OZ_Datebin archive_date; /* date the file was archived (external archiving system use) */ uLong eofblock; /* last virtual block number that contains valid data */ uWord eofbyte; /* number of bytes in the eofblock that contain valid data */ /* this number is in range 0..blocksize-1, inclusive */ uWord filattrflags; /* file attribute flags, OZ_FS_FILATTRFLAG_... */ } Filattr; typedef struct { OZ_Dbn blockcount; /* number of contiguous blocks */ OZ_Dbn logblock; /* starting block number */ } Pointer; typedef struct { uWord headerver; /* header version */ uWord checksum; /* checksum such that all words total to zero */ OZ_VDFS_Fileid fileid; /* file id */ OZ_VDFS_Fileid extid; /* extension id, zero if none */ OZ_VDFS_Fileid dirid; /* dircount=EXT_DIRCOUNT : previous header's fileid */ /* else : (original) directory fileid */ uLong dircount; /* EXT_DIRCOUNT : this is an extension header */ /* else : number of directory entries that point to file */ struct { uWord size, offs; } areas[DFS_HEADER_NAREAS]; uByte area[1]; } Header; /* On-disk home block */ typedef struct { uWord homeversion; /* file system version number */ uWord checksum; /* checksum such that all uWords in homeblock total to zero */ char volname[VOLNAME_MAX]; /* volume name (null terminated) */ uLong blocksize; /* size in bytes of disk blocks on this volume */ uLong clusterfactor; /* storage bitmap cluster factor */ OZ_Dbn clustertotal; /* total number of clusters on this volume */ OZ_Dbn clustersfree; /* number of free clusters on this volume */ OZ_Dbn indexhdrlbn; /* logical block number of index header file header */ OZ_Datebin lastwritemount; /* date/time last mounted for write */ /* set when mounted, cleared to zeroes when dismounted */ uLong initflags; /* initialization flags */ } Homeblock; /* In-memory volume extension info */ struct OZ_VDFS_Volex { OZ_VDFS_File *indexbitmap; /* pointer to index bitmap header in openfiles list */ OZ_VDFS_File *indexheaders; /* pointer to index file header in openfiles list */ OZ_VDFS_File *rootdirectory; /* pointer to root directory header in openfiles list */ OZ_VDFS_File *storagebitmap; /* pointer to storage bitmap header in openfiles list */ uByte *dirblockbuff3; /* address of dirblockbuff (used by enter_file and remove_file) */ union { /* used by various mutually exclusive functions: */ struct { // lookup_file function char fname[FILENAME_MAX]; // - filename we're looking for, without ;version, but with null char *name_r; // - where to return the resultant filename, including ;version Dirpnt partialdirpnt; // - partially build dirpnt volatile enum { // - scan state LOOKUP_FILE_STATE_GATHERNAME, LOOKUP_FILE_STATE_SKIPDIRPNTS, LOOKUP_FILE_STATE_MATCHVERSION, LOOKUP_FILE_STATE_DONE } state; OZ_VDFS_File *dirfile; // - directory we're scanning int dirpntbytesaved; // - number of bytes of dirpnt saved in partialdirpnt int level; // - level we're at in the directory tree int namelen; // - length of fname, including the null int nbytes_name_gathered; // - number of bytes of the fname that have been gathered so far // -1 if we haven't read the 'same chars' byte yet int versign; // - 0: positive version, -1: negative version OZ_Dbn nclusters; // - number of clusters in the directory OZ_Dbn multiplier; // - cluster we're currently scanning OZ_Dcmpb dcmpb; // - disk cache map parameter block OZ_VDFS_Fileid *fileid_r; // - where to return the found file-id uLong version; // - version number (abs value) uLong status; // - completion status OZ_Dbn lastbuckvbn; // - last bucket vbn we looked at OZ_Dbn thisbuckvbn; // - this bucket vbn we're looking at char lastname[FILENAME_MAX]; // - last filename scanned in bucket } lf; } v; Homeblock homeblock; // homeblock - MUST be last }; /* In-memory file extension info */ struct OZ_VDFS_Filex { OZ_VDFS_Filex *next; // next extension file pointer volatile Long headerdirty; // this extension header is dirty OZ_Dbn blocksinhdr; // number of blocks mapped by this header // (total of all its pointer blockcounts) Header header; // on-disk header contents }; /* Vector routines */ static int dfs_is_directory (OZ_VDFS_File *file); static int dfs_fis_writethru (OZ_VDFS_File *file, OZ_Dbn virtblock, uLong blockoffs, uLong size, const void *buff); static int dfs_vis_writethru (OZ_VDFS_Volume *volume); static char const *dfs_get_volname (OZ_VDFS_Volume *volume); static uLong dfs_getinfo1 (OZ_VDFS_Iopex *iopex); static void dfs_wildscan_continue (OZ_VDFS_Chnex *chnex); static void dfs_wildscan_terminate (OZ_VDFS_Chnex *chnex); static uLong dfs_getinfo2 (OZ_VDFS_Iopex *iopex); static uLong dfs_getinfo3 (OZ_VDFS_Iopex *iopex); static uLong dfs_init_volume (OZ_VDFS_Devex *devex, OZ_VDFS_Volume *volume, int volnamelen, char const *volname, uLong clusterfactor, uLong secattrsize, const void *secattrbuff, uLong initflags, OZ_VDFS_Iopex *iopex); static uLong dfs_mount_volume (OZ_VDFS_Devex *devex, OZ_VDFS_Volume *volume, uLong mountflags, OZ_VDFS_Iopex *iopex); static uLong dfs_dismount_volume (OZ_VDFS_Devex *devex, OZ_VDFS_Volume *volume, int unload, int shutdown, OZ_VDFS_Iopex *iopex); static uLong dfs_verify_volume (OZ_VDFS_Iopex *iopex, OZ_VDFS_Devex *devex); static uLong dfs_get_rootdirid (OZ_VDFS_Devex *devex, OZ_VDFS_Fileid *rootdirid_r); static const OZ_VDFS_Fileid *dfs_get_fileid (OZ_VDFS_File *file); static uLong dfs_lookup_file (OZ_VDFS_File *dirfile, int namelen, char const *name, OZ_VDFS_Fileid *fileid_r, char *name_r, OZ_VDFS_Iopex *iopex); static uLong dfs_rename_file (OZ_VDFS_File *olddirfile, char const *oldname, OZ_VDFS_File *newdirfile, char const *newdirname, int newnamelen, char const *newname, int newversion, OZ_VDFS_File *file, const OZ_VDFS_Fileid *fileid, char *newname_r, OZ_VDFS_Iopex *iopex); static uLong dfs_enter_file (OZ_VDFS_File *dirfile, char const *dirname, int namelen, char const *name, int newversion, OZ_VDFS_File *file, const OZ_VDFS_Fileid *fileid, char *name_r, OZ_VDFS_Iopex *iopex); static uLong dfs_remove_file (OZ_VDFS_File *dirfile, char const *name, char *name_r, OZ_VDFS_Iopex *iopex); static void dfs_returnspec (char *spec, uLong size, char *buff, OZ_FS_Subs *subs); static uLong dfs_create_file (OZ_VDFS_Volume *volume, int namelen, char const *name, uLong filattrflags, OZ_VDFS_Fileid *dirid, OZ_VDFS_File *file, OZ_VDFS_Fileid **fileid_r, OZ_VDFS_Iopex *iopex); static OZ_VDFS_File *dfs_findopenfile (OZ_VDFS_Volume *volume, const OZ_VDFS_Fileid *fileid); static uLong dfs_open_file (OZ_VDFS_Volume *volume, const OZ_VDFS_Fileid *fileid, OZ_VDFS_File *file, OZ_VDFS_Iopex *iopex); static uLong dfs_set_file_attrs (OZ_VDFS_File *file, uLong numitems, const OZ_Itmlst2 *itemlist, OZ_VDFS_Iopex *iopex); static uLong dfs_close_file (OZ_VDFS_File *file, OZ_VDFS_Iopex *iopex); static uLong dfs_extend_file (OZ_VDFS_File *file, OZ_Dbn nblocks, uLong extflags, OZ_VDFS_Iopex *iopex); static uLong dfs_write_dirty_header (OZ_VDFS_File *dirtyfile, Long alf, OZ_Datebin now, OZ_VDFS_Volume *volume, OZ_VDFS_Iopex *iopex); static uLong dfs_writehomeblock (OZ_VDFS_Volume *volume, OZ_VDFS_Iopex *iopex); static uLong dfs_map_vbn_to_lbn (OZ_VDFS_File *file, OZ_Dbn virtblock, OZ_Dbn *nblocks_r, OZ_Dbn *logblock_r); static void dfs_mark_header_dirty (OZ_VDFS_File *dirtyfile); /* Internal routines */ static uLong cf_default (OZ_Dbn totalblocks, uLong blocksize, uLong secattrsize); static uLong write_init_header (OZ_VDFS_Volume *volume, uByte *rootdirbuff, uLong filenum, char *name, uLong secattrsize, const void *secattrbuff, uLong filattrflags, OZ_Dbn efblk, OZ_Dbn count, OZ_Dbn start, OZ_Dbn index_header_start, OZ_VDFS_Iopex *iopex); static void check_init_alloc (OZ_VDFS_Iopex *iopex, OZ_Dbn cluster, uLong *clusterbuff, OZ_VDFS_Volume *volume, OZ_Dbn count, OZ_Dbn start); static void calc_home_block (OZ_VDFS_Volume *volume); static uLong setbitsinfile (OZ_VDFS_File *file, uLong nbits, uLong bitno, OZ_VDFS_Iopex *iopex, uLong blocksize, uLong *blockbuff); static uLong lookup_file (OZ_VDFS_File *dirfile, int namelen, char const *name, OZ_Dbn *dirvbn_r, OZ_VDFS_Fileid *fileid_r, char *name_r, OZ_VDFS_Iopex *iopex); static int dirisnotempty (OZ_VDFS_File *dirfile, OZ_VDFS_Iopex *iopex); static void adj_wildscans_ins (OZ_VDFS_File *dirfile, OZ_Dbn dirvbn, uLong from, uLong to, OZ_Dbn blocksinserted, uByte *dirbuff, char *fname); static void adj_wildscans_rem (OZ_VDFS_File *dirfile, OZ_Dbn dirvbn, uLong from, uLong to, uByte *dirbuff); static uLong getcresecattr (OZ_VDFS_Iopex *iopex, uLong secattrsize, const void *secattrbuff, OZ_Secattr **secattr_r); static uLong insert_blocks (OZ_VDFS_File *file, OZ_Dbn nblocks, OZ_Dbn atblock, OZ_VDFS_Iopex *iopex); static uLong remove_blocks (OZ_VDFS_File *file, OZ_Dbn nblocks, OZ_Dbn atblock, OZ_VDFS_Iopex *iopex); static uLong makeroomforapointer (OZ_VDFS_File *file, OZ_VDFS_Filex *extfilex, Pointer **pointer_p, OZ_VDFS_Filex **extfilex_r, Pointer **pointer_r, OZ_VDFS_Iopex *iopex); static OZ_Dbn countblocksinfile (OZ_VDFS_File *file, OZ_Dbn **blockarray_r); static void compareblockarrays (OZ_Dbn nbsmall, OZ_Dbn *basmall, OZ_Dbn nbbig, OZ_Dbn *babig, OZ_Dbn atblock, OZ_Dbn logblock); static uLong make_header_room (OZ_VDFS_File *file, OZ_VDFS_Filex *filex, uWord roomsize, int narea, int exthdr, OZ_VDFS_Filex **filex_r, OZ_VDFS_Iopex *iopex); static uLong allocate_header (OZ_VDFS_Volume *volume, OZ_VDFS_Filex **filex_r, OZ_VDFS_Iopex *iopex); static void delete_header (OZ_VDFS_Filex *filex, OZ_VDFS_Iopex *iopex); static uLong allocate_blocks (OZ_VDFS_Volume *volume, OZ_Dbn nblocks, OZ_Dbn startlbn, OZ_Dbn *nblocks_r, OZ_Dbn *logblock_r, OZ_VDFS_Iopex *iopex); static uLong free_blocks (OZ_VDFS_Volume *volume, OZ_Dbn nblocks, OZ_Dbn logblock, OZ_VDFS_Iopex *iopex); static uLong read_header_block (const OZ_VDFS_Fileid *fileid, OZ_Dbn hdrlbn, int exthdr, OZ_VDFS_Fileid *lastfid, OZ_VDFS_Filex *filex, OZ_VDFS_Iopex *iopex); static void mark_exthdr_dirty (OZ_VDFS_Filex *dirtyfilex, OZ_VDFS_File *dirtyfile); static uLong write_header_block (OZ_VDFS_Volume *volume, OZ_VDFS_Filex *filex, OZ_VDFS_Iopex *iopex); static void write_dirty_homeboy (OZ_VDFS_Volume *volume, OZ_VDFS_Iopex *iopex); static int validirbuf (uLong totalsize, uLong blocksize, uByte *dirbuffer, OZ_VDFS_Iopex *iopex); static void validate_volume (OZ_VDFS_Volume *volume, int line); static void validate_file (OZ_VDFS_File *file, OZ_VDFS_Volume *volume, int linef, int linev); static int validate_header (Header *header, OZ_VDFS_Volume *volume, OZ_VDFS_Iopex *iopex); static uLong verify_volume (int readonly, uLong blocksize, OZ_Dbn totalblocks, OZ_VDFS_Iopex *iopex); /************************************************************************/ /* */ /* Boot-time initialization routine */ /* */ /************************************************************************/ static const OZ_VDFS_Vector vector = { sizeof (OZ_VDFS_Fileid), VOLNAME_MAX, FILENAME_MAX, SECATTR_MAX, 1, // it does versions dfs_close_file, // close a file dfs_create_file, // create a new file dfs_dismount_volume, // dismount volume dfs_rename_file, // rename file dfs_enter_file, // enter a new name in a directory dfs_extend_file, // extend a file dfs_findopenfile, // see if a file is already open dfs_get_rootdirid, // get root directory id dfs_get_volname, // get volume name dfs_getinfo2, // get name of file open on a channel dfs_init_volume, // initialize a volume dfs_lookup_file, // look up a particular file in a directory dfs_mount_volume, // mount volume dfs_open_file, // open a file dfs_remove_file, // remove name from directory dfs_set_file_attrs, // write a file's attributes dfs_write_dirty_header, // flush file's header(s) to disk dfs_writehomeblock, // flush volume's header to disk dfs_verify_volume, // verify volume structures dfs_fis_writethru, // see if file is a 'writethru' file dfs_get_fileid, // get file id dfs_getinfo1, // get info about the file open on channel dfs_getinfo3, // get info about the volume dfs_is_directory, // see if file is a directory dfs_map_vbn_to_lbn, // map a file's vbn to equivalent lbn dfs_mark_header_dirty, // mark (prime) header dirty dfs_returnspec, // return filespec string/substrings dfs_vis_writethru, // see if volume is a 'writethru' volume dfs_wildscan_continue, // scan directory block for a particular wildcard match dfs_wildscan_terminate }; // terminate wildcard scan void oz_dev_dfs_init () { oz_dev_vdfs_init (OZ_VDFS_VERSION, "oz_dfs", &vector); } /************************************************************************/ /* */ /* Return whether the file is a directory or not */ /* */ /* Input: */ /* */ /* file = file to make determination about */ /* */ /* Output: */ /* */ /* dfs_is_directory = 0 : file is not a directory */ /* 1 : file is a directory */ /* */ /************************************************************************/ static int dfs_is_directory (OZ_VDFS_File *file) { return (IS_DIRECTORY (file -> filex -> header)); } /************************************************************************/ /* */ /* Return whether the file forces cache writethru mode */ /* */ /* Input: */ /* */ /* file = file to make determination about */ /* virtblock = virtual block being written */ /* blockoffs = offset in the block we're starting at */ /* size = size of the 'buff' being written */ /* buff = data being written */ /* */ /* Output: */ /* */ /* dfs_fis_writethru = 0 : file can use writeback mode */ /* 1 : file forces writethru mode */ /* */ /************************************************************************/ static int dfs_fis_writethru (OZ_VDFS_File *file, OZ_Dbn virtblock, uLong blockoffs, uLong size, const void *buff) { if (FIS_WRITETHRU (file -> filex -> header)) return (1); // maybe the file forces writethru mode if (file != file -> volume -> volex -> indexheaders) return (0); // see if we are writing an index header if (blockoffs != 0) return (0); if (size != file -> volume -> volex -> homeblock.blocksize) return (0); return (FIS_WRITETHRU (*(Header *)buff)); // if so, force writethru if the corresponding file does } /************************************************************************/ /* */ /* Return whether the volume forces cache writethru mode */ /* */ /* Input: */ /* */ /* volume = volume to make determination about */ /* */ /* Output: */ /* */ /* dfs_vis_writethru = 0 : volume can use writeback mode */ /* 1 : volume forces writethru mode */ /* */ /************************************************************************/ static int dfs_vis_writethru (OZ_VDFS_Volume *volume) { return (VIS_WRITETHRU (volume)); } /************************************************************************/ /* */ /* Retrieve the volume's label */ /* */ /* Input: */ /* */ /* volume = volume to get the label of */ /* */ /* Output: */ /* */ /* dfs_get_volname = pointer to null-terminated label string */ /* */ /************************************************************************/ static char const *dfs_get_volname (OZ_VDFS_Volume *volume) { return (volume -> volex -> homeblock.volname); } /************************************************************************/ /* */ /* Get information part 1 */ /* */ /************************************************************************/ static uLong dfs_getinfo1 (OZ_VDFS_Iopex *iopex) { Filattr filattr; OZ_VDFS_Filex *filex; filex = iopex -> chnex -> file -> filex; movc4 (filex -> header.areas[DFS_HEADER_AREA_FILATTR].size, filex -> header.area + filex -> header.areas[DFS_HEADER_AREA_FILATTR].offs, sizeof filattr, &filattr); iopex -> u.getinfo1.p.filattrflags = filattr.filattrflags; iopex -> u.getinfo1.p.create_date = filattr.create_date; iopex -> u.getinfo1.p.access_date = filattr.access_date; iopex -> u.getinfo1.p.change_date = filattr.change_date; iopex -> u.getinfo1.p.modify_date = filattr.modify_date; iopex -> u.getinfo1.p.expire_date = filattr.expire_date; iopex -> u.getinfo1.p.backup_date = filattr.backup_date; iopex -> u.getinfo1.p.archive_date = filattr.archive_date; if (iopex -> u.getinfo1.p.fileidbuff != NULL) { movc4 (sizeof filex -> header.fileid, &(filex -> header.fileid), iopex -> u.getinfo1.p.fileidsize, iopex -> u.getinfo1.p.fileidbuff); } return (OZ_SUCCESS); } /************************************************************************/ /* */ /* Continue scanning the current directory block */ /* */ /* The directory is locked on input and is unlocked on return */ /* */ /************************************************************************/ static void dfs_wildscan_continue (OZ_VDFS_Chnex *chnex) { char c, *p; Dirpnt *dirpnts; int filename_l, newname, nver, rc; OZ_IO_fs_open fs_open; OZ_VDFS_Chnex *dirchnex; OZ_VDFS_Devex *devex; OZ_VDFS_Iopex *iopex; OZ_VDFS_Volume *volume; OZ_VDFS_Wildscan *outerwild, *wildscan; uLong sts, vl; iopex = chnex -> wild_iopex; volume = iopex -> devex -> volume; wildscan = chnex -> wildscan; devex = iopex -> devex; /* Maybe we have more wild versions to return */ /* Never leave blockoffs pointing at the ;0 entry or adj_wildscans_rem will break */ if (wildscan -> ver_output) { dirpnts = (Dirpnt *)(wildscan -> blockbuff + wildscan -> blockoffs); // point to the one to be output wildscan -> blockoffs += sizeof (Dirpnt); // increment blockoffs just past it if (wildscan -> blockoffs + sizeof (Dirpnt) > volume -> dirblocksize) { // see if there is room for another wildscan -> blockoffs = volume -> dirblocksize; // if not, we've used up the whole block wildscan -> ver_output = 0; // ... and next block starts with a filename } else if (dirpnts[1].version == 0) { // see if this is the last one of the array wildscan -> blockoffs += sizeof (Dirpnt); // if so, point past the zero terminator wildscan -> ver_output = 0; // ... and it is followed by a filename } oz_dev_vdfs_wildscan_output (chnex, wildscan -> lastname, dirpnts -> version, &(dirpnts -> fileid)); // anyway, output this version entry return; } /* Assume we will have new names throughout the bucket. However, the name will be the same as last time if we are */ /* at the beginning of a bucket and the file has too many versions to fit in a bucket. So we check for that here. */ newname = ((wildscan -> blockoffs != 0) || (strcmp (wildscan -> lastname, wildscan -> blockbuff + 1) != 0)); /* Scan block for matching filename entry */ while (wildscan -> blockoffs < volume -> dirblocksize) { if (wildscan -> blockoffs != 0) newname = 1; /* Point to the filename in the directory block buffer */ p = wildscan -> blockbuff + wildscan -> blockoffs; // point to the 'same chars' byte rc = *(p ++); // get it if (rc == 0) break; // if zero, end of directory block filename_l = strnlen (p, volume -> dirblocksize - wildscan -> blockoffs - 1); // get length of different chars if ((rc - 1 + filename_l + 1 > sizeof wildscan -> lastname) || (wildscan -> blockoffs + filename_l + 2 > volume -> dirblocksize)) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs wildscan_continue: filename %s+%s in directory %s too long\n", wildscan -> lastname, p, wildscan -> basespec); goto dircorrupt; } memcpy (wildscan -> lastname + rc - 1, p, filename_l + 1); // copy to buffer to make complete name wildscan -> blockoffs += filename_l + 2; // point past filename's null terminator filename_l += rc - 1; // length of complete name, without null /* Count the number of versions we have of this file and move the pointer past all of them for next scan */ dirpnts = (Dirpnt *)(wildscan -> blockbuff + wildscan -> blockoffs); // save where the dirpnt array starts nver = 0; // so far we don't have any versions while (wildscan -> blockoffs + sizeof (Dirpnt) <= volume -> dirblocksize) { // repeat as long as there is room for one wildscan -> blockoffs += sizeof (Dirpnt); // increment pointer past it if (dirpnts[nver].version == 0) break; // stop counting if version is zero nver ++; // non-zero version, count it } if (nver == 0) { // there has to be at least one oz_dev_vdfs_printk (iopex, "oz_dev_dfs wildscan_continue: filename %s in directory %s has no versions\n", wildscan -> lastname, wildscan -> basespec); goto dircorrupt; } /* See if the filename in the blockbuff matches the wildcard spec, and see if we should scan the sub-sirectory */ rc = oz_dev_vdfs_wildscan_match (wildscan, wildscan -> lastname); /* Maybe scan sub-directory. If we also output this directory, we do it either after or before the directory */ /* contents (depending on the setting of delaydir), so we let the subdir output stuff handle that. */ if ((wildscan -> lastname[filename_l-1] == '/') && (rc & 2)) { if (iopex -> aborted) { // don't bother if I/O request is aborted oz_dev_vdfs_wildscan_iodonex (iopex, OZ_ABORTED); return; } if (dirpnts[0].fileid.num != SACRED_FIDNUM_ROOTDIRECTORY) { // don't nest into the root directory if (strlen (wildscan -> basespec) + filename_l >= sizeof wildscan -> basespec) { // make sure directory name not too long oz_dev_vdfs_wildscan_iodonex (iopex, OZ_FILENAMETOOLONG); return; } for (outerwild = wildscan; outerwild != NULL; outerwild = outerwild -> nextouter) { // don't nest into same directory twice dirchnex = oz_knl_iochan_ex (outerwild -> iochan); if (memcmp (&(dirpnts[0].fileid), &(dirchnex -> file -> filex -> header.fileid), sizeof dirpnts[0].fileid) == 0) { oz_dev_vdfs_wildscan_iodonex (iopex, OZ_DIRECTORYLOOP); return; } } oz_dev_vdfs_wildscan_startsubdir (chnex, wildscan -> lastname, &(dirpnts[0].fileid), rc); // start processing sub-directory return; } } /* Return match to caller */ if (!(rc & 1)) continue; // don't if name didn't match if (wildscan -> lastname[filename_l-1] == '/') { // see if it is a dir name if (!(wildscan -> ver_incldirs)) continue; // skip if we don't include directories oz_dev_vdfs_wildscan_output (chnex, wildscan -> lastname, 0, &(dirpnts -> fileid)); // ok, output it return; } if (wildscan -> ver_inclallfiles) { // see if incl all versions of files if (nver > 1) { // ok, see if more than one version wildscan -> ver_output = 1; // if so, do more on next call wildscan -> blockoffs = (char *)(dirpnts + 1) - wildscan -> blockbuff; // ... and here is where the next one is } oz_dev_vdfs_wildscan_output (chnex, wildscan -> lastname, dirpnts -> version, &(dirpnts -> fileid)); // output this version return; } if (wildscan -> ver_number > 0) { // see if looking for one particular version while (-- nver >= 0) { // ok, search the array for it if (dirpnts -> version == wildscan -> ver_number) { oz_dev_vdfs_wildscan_output (chnex, wildscan -> lastname, dirpnts -> version, &(dirpnts -> fileid)); // found, output the entry return; } dirpnts ++; } continue; // not found, skip the entry } if (newname) { // relative version, see if new filename wildscan -> ver_count = wildscan -> ver_number; // if so, reset version counter } while ((wildscan -> ver_count != 0) && (nver > 0)) { // see if any more versions to skip wildscan -> ver_count ++; -- nver; dirpnts ++; // if so, skip over one more } if (nver <= 0) continue; // skip this entry if didn't get count wildscan -> ver_count = 1; // only do one from this name oz_dev_vdfs_wildscan_output (chnex, wildscan -> lastname, dirpnts -> version, &(dirpnts -> fileid)); // output it return; } /* Reached end of currrent block, start reading a new one */ wildscan -> blockvbn += volume -> dirblocksize / iopex -> devex -> blocksize; oz_dev_vdfs_wildscan_readdir (chnex); return; /* Something is corrupt about the directory block */ dircorrupt: oz_dev_vdfs_wildscan_iodonex (iopex, OZ_DIRCORRUPT); } static void dfs_wildscan_terminate (OZ_VDFS_Chnex *chnex) { } /************************************************************************/ /* */ /* Get information part 2 */ /* */ /************************************************************************/ static uLong dfs_getinfo2 (OZ_VDFS_Iopex *iopex) { char *buff, *p; OZ_VDFS_Chnex *chnex; OZ_VDFS_File *dirfile, *file; OZ_VDFS_Filex *filex; uLong i, l, size, sts; chnex = iopex -> chnex; file = chnex -> file; /* Get the complete filespec by looking back through the dirid links */ size = iopex -> u.getinfo2.p.filnamsize; /* get size of user supplied filename buffer */ buff = iopex -> u.getinfo2.p.filnambuff; /* get its address */ i = size; filex = file -> filex; while (filex -> header.fileid.num != SACRED_FIDNUM_ROOTDIRECTORY) { /* repeat until we reach the root directory */ p = FILENAME (filex -> header); /* point to the filename string therein */ l = strlen (p); /* get the filename string length */ if (i <= l) { /* see if there is room for it */ memcpy (buff, p + l - i, i); /* if not, copy what of it will fit */ i = 0; /* no room left in output buffer */ break; /* stop scanning */ } i -= l; /* enough room, back up output buffer offset */ memcpy (buff + i, p, l); /* copy in string */ sts = oz_dev_vdfs_open_file (file -> volume, &(filex -> header.dirid), OZ_SECACCMSK_LOOK, &dirfile, iopex); /* try to open parent directory (must be able to look up a specific file) */ if (file != chnex -> file) oz_dev_vdfs_close_file (file, iopex); /* close prior parent directory */ if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs: getinfo2: error %u getting directory name\n", sts); return (sts); } file = dirfile; filex = file -> filex; } if (file != chnex -> file) oz_dev_vdfs_close_file (file, iopex); /* close parent directory */ if (i > 0) buff[--i] = '/'; /* put in the '/' that represents root directory */ if (i > 0) { l = size - i; /* room left at beginning, see how long string is */ memmove (buff, buff + i, l); /* move string to beginning of buffer */ buff[l] = 0; /* null terminate it */ } return (OZ_SUCCESS); } /************************************************************************/ /* */ /* Get information part 3 (no file need be open) */ /* */ /************************************************************************/ static void dfs_fidtoa (const void *fileid, int size, char *buff); static int dfs_atofid (char const *buff, void *fileid); static uLong dfs_getinfo3 (OZ_VDFS_Iopex *iopex) { char const *unitname; OZ_Devunit *devunit; OZ_VDFS_Volex *volex; volex = iopex -> devex -> volume -> volex; iopex -> u.getinfo3.p.blocksize = volex -> homeblock.blocksize; iopex -> u.getinfo3.p.clusterfactor = volex -> homeblock.clusterfactor; iopex -> u.getinfo3.p.clustersfree = volex -> homeblock.clustersfree; iopex -> u.getinfo3.p.clustertotal = volex -> homeblock.clustertotal; iopex -> u.getinfo3.p.fileidstrsz = 32; iopex -> u.getinfo3.p.fidtoa = dfs_fidtoa; iopex -> u.getinfo3.p.atofid = dfs_atofid; return (OZ_SUCCESS); } static void dfs_fidtoa (const void *fileid, int size, char *buff) { const OZ_VDFS_Fileid *fid; fid = fileid; oz_sys_sprintf (size, buff, "(%u,%u,%u)", fid -> num, fid -> rvn, fid -> seq); } static int dfs_atofid (char const *buff, void *fileid) { char const *p; int i; OZ_VDFS_Fileid *fid; fid = fileid; p = buff; if (*(p ++) != '(') return (-1); fid -> num = oz_hw_atoi (p, &i); p += i; if (*(p ++) != ',') return (-1); fid -> rvn = oz_hw_atoi (p, &i); p += i; if (*(p ++) != ',') return (-1); fid -> seq = oz_hw_atoi (p, &i); p += i; if (*(p ++) != ')') return (-1); return (p - buff); } /************************************************************************/ /* */ /* Initialize a volume */ /* */ /* Input: */ /* */ /* volnamelen = length of volume name string */ /* volname = volume name string (not null terminated) */ /* clusterfactor = cluster factor */ /* secattrsize/buff = security attributes for the volume */ /* */ /* Output: */ /* */ /* init_volume = OZ_SUCCESS : successful */ /* else : error status */ /* */ /************************************************************************/ static uLong dfs_init_volume (OZ_VDFS_Devex *devex, OZ_VDFS_Volume *volume, int volnamelen, char const *volname, uLong clusterfactor, uLong secattrsize, const void *secattrbuff, uLong initflags, OZ_VDFS_Iopex *iopex) { OZ_Dbn boot_home_count, boot_home_start, cluster, clusters_needed, indexclusters, index_bitmap_count, index_bitmap_start, index_header_count, index_header_start; OZ_Dbn low_alloc_cluster, root_directory_count, root_directory_start, storage_bitmap_count, storage_bitmap_start, totalblocks; OZ_VDFS_Volex *volex; uByte *rootdirbuff; uLong bitsperblock, bitspercluster, bytespercluster, *clusterbuff, sts; clusterbuff = NULL; rootdirbuff = NULL; /* Round total blocks down to page size so caching will work on the last page without any crazy code */ totalblocks = devex -> totalblocks; totalblocks /= (1 << OZ_HW_L2PAGESIZE) / devex -> blocksize; totalblocks *= (1 << OZ_HW_L2PAGESIZE) / devex -> blocksize; /* Allocate a temporary volex (volume extension) struct */ sts = ((uByte *)&(volex -> homeblock)) + devex -> blocksize - (uByte *)volex; volex = OZ_KNL_PGPMALLOQ (sts); if (volex == NULL) return (OZ_EXQUOTAPGP); memset (volex, 0, sts); volume -> volex = volex; /* Determine default cluster factor if none specified */ if (clusterfactor == 0) clusterfactor = cf_default (totalblocks, devex -> blocksize, secattrsize); /* Fill in part of the homey */ volex -> homeblock.homeversion = HOMEBLOCK_VERSION; volex -> homeblock.blocksize = devex -> blocksize; volex -> homeblock.clusterfactor = clusterfactor; volex -> homeblock.clustertotal = totalblocks / clusterfactor; volex -> homeblock.initflags = initflags & OZ_FS_INITFLAG_WRITETHRU; volume -> dirblocksize = volex -> homeblock.clusterfactor * volex -> homeblock.blocksize; volume -> clusterfactor = volex -> homeblock.clusterfactor; movc4 (volnamelen, volname, sizeof volex -> homeblock.volname, volex -> homeblock.volname); /* Calculate the homey's logical block number - it puts it either right after or right before the boot block(s) */ calc_home_block (volume); boot_home_count = volume -> bb_nblocks; /* get number of blocks in bootblock */ boot_home_start = volume -> bb_logblock; /* get starting block number of bootblock */ if (volume -> hb_logblock == boot_home_start - 1) boot_home_start --; /* if home block is before bootblock, adjust starting block number */ else if (volume -> hb_logblock != boot_home_count + boot_home_start) { /* otherwise, it had better be just after it! */ oz_crash ("oz_dev_dfs init_volume: hb_logblock %u, boot_home_count %u, boot_home_start %u", volume -> hb_logblock, boot_home_count, boot_home_start); } boot_home_count += boot_home_start; /* point boot_home_count at last block in boot/home combination, inclusive */ /* (note that boot_home_count did not include the home block to start with) */ boot_home_start /= clusterfactor; /* convert starting block number to cluster number */ boot_home_count /= clusterfactor; /* convert ending block number to cluster number (inclusive) */ boot_home_count -= boot_home_start - 1; /* convert boot_home_count to cluster count */ /* Get number of blocks needed for initial files: */ /* index header file : bitspercluster blocks = bitsperblock clusters */ /* index bitmap file : 1 cluster */ /* storage bitmap file : clustertotal / bitspercluster clusters */ /* boot and homeblock file : 0 */ /* root directory file : 1 cluster */ bitsperblock = volex -> homeblock.blocksize * 8; bytespercluster = volex -> homeblock.blocksize * clusterfactor; bitspercluster = bitsperblock * clusterfactor; indexclusters = bitsperblock; if (indexclusters > volex -> homeblock.clustertotal / (clusterfactor + 1)) { // make sure we leave room for that many files, though indexclusters = volex -> homeblock.clustertotal / (clusterfactor + 1); } if (volex -> homeblock.clustertotal <= 5760) { // fudge for small disks (floppies), don't take up more than 1/16th if (indexclusters > volex -> homeblock.clustertotal / 16) indexclusters = volex -> homeblock.clustertotal / 16; } clusters_needed = indexclusters + 1 + volex -> homeblock.clustertotal / bitspercluster + 0 + 1; /* Position all that stuff in the middle of the volume - be sure to miss the boot and home blocks */ /* But for small disks (floppies), put it all at the beginning so we can have a big contig file */ if (volex -> homeblock.clustertotal > 5760) { low_alloc_cluster = (volex -> homeblock.clustertotal - clusters_needed) / 2; if (low_alloc_cluster >= boot_home_count + boot_home_start) goto it_misses; /* if it starts after boot/home blocks end, it's ok */ if (low_alloc_cluster + clusters_needed <= boot_home_start) goto it_misses; /* if it ends before boot/home blocks start, it's ok */ } low_alloc_cluster = boot_home_count + boot_home_start; /* maybe it can go just after boot/home blocks */ if (clusters_needed <= volex -> homeblock.clustertotal - low_alloc_cluster) goto it_misses; low_alloc_cluster = boot_home_start - clusters_needed; /* maybe it can go just before boot/home blocks */ if (clusters_needed <= boot_home_start) goto it_misses; oz_dev_vdfs_printk (iopex, "oz_dev_dfs: not enough room on disk for initialization data\n"); oz_dev_vdfs_printk (iopex, " boot_home_count %u, boot_home_start %u, clusters_needed %u\n", boot_home_count, boot_home_start, clusters_needed); sts = OZ_DISKISFULL; goto cleanup; it_misses: index_header_count = indexclusters * clusterfactor; index_header_start = low_alloc_cluster * clusterfactor; index_bitmap_count = clusterfactor; index_bitmap_start = index_header_start + index_header_count; storage_bitmap_count = ((volex -> homeblock.clustertotal + bitspercluster - 1) / bitspercluster) * clusterfactor; storage_bitmap_start = index_bitmap_start + index_bitmap_count; root_directory_count = clusterfactor; root_directory_start = storage_bitmap_start + storage_bitmap_count; /* Set up root directory buffer */ rootdirbuff = OZ_KNL_PGPMALLOQ (clusterfactor * volex -> homeblock.blocksize); if (rootdirbuff == NULL) { sts = OZ_EXQUOTAPGP; goto cleanup; } memset (rootdirbuff, 0, volume -> dirblocksize); /* Write the file headers and fill in root directory buffer */ boot_home_count *= clusterfactor; boot_home_start *= clusterfactor; sts = write_init_header (volume, rootdirbuff, SACRED_FIDNUM_ROOTDIRECTORY, "oz_fs_rootdirectory/", secattrsize, secattrbuff, OZ_FS_FILATTRFLAG_DIRECTORY, root_directory_count, root_directory_count, root_directory_start, index_header_start, iopex); if (sts == OZ_SUCCESS) sts = write_init_header (volume, rootdirbuff, SACRED_FIDNUM_INDEXHEADERS, "oz_fs_indexheaders", secattrsize, secattrbuff, 0, SACRED_FIDNUM_COUNT, index_header_count, index_header_start, index_header_start, iopex); if (sts == OZ_SUCCESS) sts = write_init_header (volume, rootdirbuff, SACRED_FIDNUM_INDEXBITMAP, "oz_fs_indexbitmap", secattrsize, secattrbuff, 0, index_bitmap_count, index_bitmap_count, index_bitmap_start, index_header_start, iopex); if (sts == OZ_SUCCESS) sts = write_init_header (volume, rootdirbuff, SACRED_FIDNUM_STORAGEBITMAP, "oz_fs_storagebitmap", secattrsize, secattrbuff, 0, storage_bitmap_count, storage_bitmap_count, storage_bitmap_start, index_header_start, iopex); if (sts == OZ_SUCCESS) sts = write_init_header (volume, rootdirbuff, SACRED_FIDNUM_BOOTHOMEBLOCK, "oz_fs_boothomeblock", secattrsize, secattrbuff, 0, boot_home_count, boot_home_count, boot_home_start, index_header_start, iopex); if (sts != OZ_SUCCESS) goto cleanup; /* Write the index bitmap file contents = mark the first SACRED_FIDNUM_COUNT headers as allocated, the rest are free */ clusterbuff = OZ_KNL_PGPMALLOQ (bytespercluster); if (clusterbuff == NULL) { sts = OZ_EXQUOTAPGP; goto cleanup; } memset (clusterbuff, 0, bytespercluster); clusterbuff[0] = (1 << SACRED_FIDNUM_COUNT) - 1; sts = oz_dev_vdfs_writelogblock (index_bitmap_start, 0, bytespercluster, clusterbuff, 0, iopex); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs init_volume: error %u writing initial index bitmap file block at %u\n", sts, index_bitmap_start); goto cleanup; } /* Write the storage bitmap file contents = mark the initial stuff from the three files that take room as being allocated, the rest are free. */ /* Also mark the boot and home blocks as being allocated, and anything at the end of the last storage bitmap that is off the end of the disk. */ volex -> homeblock.clustersfree = 0; for (cluster = 0; cluster < volex -> homeblock.clustertotal; cluster += bitspercluster) { volex -> homeblock.clustersfree += bitspercluster; memset (clusterbuff, 0, bytespercluster); check_init_alloc (iopex, cluster, clusterbuff, volume, boot_home_count, boot_home_start); check_init_alloc (iopex, cluster, clusterbuff, volume, index_header_count, index_header_start); check_init_alloc (iopex, cluster, clusterbuff, volume, index_bitmap_count, index_bitmap_start); check_init_alloc (iopex, cluster, clusterbuff, volume, storage_bitmap_count, storage_bitmap_start); check_init_alloc (iopex, cluster, clusterbuff, volume, root_directory_count, root_directory_start); check_init_alloc (iopex, cluster, clusterbuff, volume, bitspercluster * clusterfactor, volex -> homeblock.clustertotal * clusterfactor); sts = oz_dev_vdfs_writelogblock (storage_bitmap_start + cluster / bitsperblock, 0, bytespercluster, clusterbuff, 0, iopex); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs init_volume: error %u writing initial storage bitmap file block at %u\n", sts, storage_bitmap_start + cluster * clusterfactor); goto cleanup; } } /* Erase the boot block */ for (cluster = 0; cluster < volex -> homeblock.blocksize / sizeof (uLong); cluster ++) { clusterbuff[cluster] = 0xDEADBEEF; } for (cluster = 0; cluster < volume -> bb_nblocks; cluster ++) { sts = oz_dev_vdfs_writelogblock (volume -> bb_logblock + cluster, 0, volex -> homeblock.blocksize, clusterbuff, 0, iopex); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs init_volume: error %u erasing boot block %u\n", sts, volume -> bb_logblock + cluster); goto cleanup; } } /* Write out the root directory cluster */ if (!validirbuf (bytespercluster, bytespercluster, rootdirbuff, iopex)) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs init_volume: initial root directory block corrupt\n"); sts = OZ_DIRCORRUPT; goto cleanup; } sts = oz_dev_vdfs_writelogblock (root_directory_start, 0, bytespercluster, rootdirbuff, 0, iopex); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs init_volume: error %u writing initial root directory file block at %u\n", sts, root_directory_start); goto cleanup; } /* Fill in the rest of the homey */ volex -> homeblock.indexhdrlbn = index_header_start - 1 + SACRED_FIDNUM_INDEXHEADERS; /* Finally, write the homey */ sts = dfs_writehomeblock (volume, iopex); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs init_volume: error %u writing home block at %u\n", sts, volume -> hb_logblock); goto cleanup; } /* Clean up and return status */ cleanup: if (clusterbuff != NULL) OZ_KNL_PGPFREE (clusterbuff); if (rootdirbuff != NULL) OZ_KNL_PGPFREE (rootdirbuff); OZ_KNL_PGPFREE (volex); volume -> volex = NULL; return (sts); } /************************************************************************/ /* */ /* Determine default cluster factor for disk */ /* */ /* We do this by figuring out what cluster factor would let us fill */ /* the disk just before running out of index header space, assuming */ /* the files are at least one cluster each */ /* */ /* Also, we assume that the indexbitmap file is extended in one */ /* cluster increments each time the indexheaders file fills */ /* */ /************************************************************************/ static uLong cf_default (OZ_Dbn totalblocks, uLong blocksize, uLong secattrsize) { OZ_Dbn bitsinablock, maxpointersinindexheader; uLong cf; bitsinablock = blocksize * 8; /* Caclulate the number of pointers that fit into the index file header */ /* The '24' is for the length of "oz_fs_indexheaders;1" */ maxpointersinindexheader = (blocksize - 24 - sizeof (Header) - sizeof (Filattr) - secattrsize) / sizeof (Pointer); /* So the index file header can hold maxpointersinindexheader */ /* Each indexbitmap extension will be one cluster, so that is 'bitsinablock * cf' bits */ /* Each indexbitmap bit corresponds to one indexheaders block, so each indexheaders pointer is 'bitsinablock * cf' blocks */ /* We assume each file will be just one cluster in size, plus one block for its header */ /* So we want to select a cluster factor as small as possible that the */ /* index header won't overflow (the disk will run out of space first) */ /* Start with 1, 2, 4, 8, etc, up to cache page size so a integer number of clusters fit in a cache page */ for (cf = 1; cf * blocksize <= OZ_KNL_CACHE_PAGESIZE; cf *= 2) { if (maxpointersinindexheader * bitsinablock * cf * (1 + cf) >= totalblocks) return (cf); } /* Now just do multiples of cache page size until we get a big enough number */ do cf += OZ_KNL_CACHE_PAGESIZE / blocksize; while (maxpointersinindexheader * bitsinablock * cf * (1 + cf) < totalblocks); return (cf); } /************************************************************************/ /* */ /* Write an initialization header */ /* */ /* Input: */ /* */ /* volume = pointer to volume block */ /* rootdirbuff = root directory buffer */ /* or NULL to not enter the file */ /* filenum = sacred filenum of header being written */ /* name = file name string */ /* secattrsize/buff = security attributes buffer size and address */ /* filattrflags = file attribute flags */ /* efblk = end-of-file block number */ /* count = number of blocks allocated to file */ /* start = first logical block allocated to file */ /* index_header_start = logical block number for header #1 */ /* */ /* Output: */ /* */ /* write_init_header = OZ_SUCCESS : successfully written */ /* else : error status */ /* */ /************************************************************************/ static uLong write_init_header (OZ_VDFS_Volume *volume, uByte *rootdirbuff, uLong filenum, char *name, uLong secattrsize, const void *secattrbuff, uLong filattrflags, OZ_Dbn efblk, OZ_Dbn count, OZ_Dbn start, OZ_Dbn index_header_start, OZ_VDFS_Iopex *iopex) { char lastname[FILENAME_MAX], nextname[FILENAME_MAX]; Dirpnt dirpnt[2]; int cmp; uLong areaoffs, cksm, i, j, k, l, m, sts; Filattr filattr; Header *header; OZ_VDFS_Volex *volex; volex = volume -> volex; /* Allocate a temp header memory block and fill in fixed items */ header = OZ_KNL_PGPMALLOQ (volex -> homeblock.blocksize); if (header == NULL) return (OZ_EXQUOTAPGP); memset (header, 0, volex -> homeblock.blocksize); header -> headerver = HEADER_VERSION; header -> fileid.num = filenum; header -> fileid.rvn = 1; /* Put entry in root directory block */ header -> dirid.num = SACRED_FIDNUM_ROOTDIRECTORY; header -> dirid.rvn = 1; header -> dircount = 1; memset (dirpnt, 0, sizeof dirpnt); // set up the dirpnts dirpnt[0].version = 1; dirpnt[0].fileid = header -> fileid; memset (lastname, 0, sizeof lastname); // clear 'last name in bucket' buffer for (i = 0; rootdirbuff[i] != 0; i += sizeof dirpnt) { // scan as long as there are entries to look at memcpy (nextname, lastname, rootdirbuff[i] - 1); // make up the full name we're looking at strcpy (nextname + rootdirbuff[i] - 1, rootdirbuff + i + 1); cmp = strcmp (nextname, name); // compare it to the one we want to enter if (cmp > 0) break; // if it is .gt., stop scanning i += strlen (rootdirbuff + i) + 1; // .lt., skip to next entry strcpy (lastname, nextname); } for (j = i; rootdirbuff[j] != 0; j += sizeof dirpnt) { // skip to end of existing buffer j += strlen (rootdirbuff + j) + 1; } for (k = 0;; k ++) if (name[k] != lastname[k]) break; // see how much we match with last entry if (j == i) { m = strlen (name) + 1; // see how long string is we are going to enter if (j + 1 + m - k + sizeof dirpnt > volume -> dirblocksize) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs write_init_header: cluster not big enough to hold all initial filenames\n"); return (OZ_DIRCORRUPT); } rootdirbuff[i++] = k + 1; // store 'same as last + 1' byte memcpy (rootdirbuff + i, name + k, m - k); // copy the different string i += m - k; // increment to where dirpnts go memcpy (rootdirbuff + i, dirpnt, sizeof dirpnt); // copy them in } else { for (l = 0;; l ++) if (name[l] != nextname[l]) break; // see how much we match with next entry m = strlen (name) + 1; // see how long string is we are going to enter if (j + 1 + m - k + sizeof dirpnt + rootdirbuff[i] - l - 1 > volume -> dirblocksize) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs write_init_header: cluster not big enough to hold all initial filenames\n"); return (OZ_DIRCORRUPT); } memmove (rootdirbuff + i + 1 + m - k + sizeof dirpnt + 1, // make room for new entry rootdirbuff + i + l + 2 - rootdirbuff[i], j - i - l - 2 + rootdirbuff[i]); rootdirbuff[i++] = k + 1; // store 'same as last + 1' byte memcpy (rootdirbuff + i, name + k, m - k); // copy the different string i += m - k; // increment to where dirpnts go memcpy (rootdirbuff + i, dirpnt, sizeof dirpnt); // copy them in i += sizeof dirpnt; rootdirbuff[i] = l + 1; // how much of next entry is same as new one } if (!validirbuf (volume -> dirblocksize, volume -> dirblocksize, rootdirbuff, iopex)) return (OZ_DIRCORRUPT); /* Fill in variable areas of the header */ areaoffs = 0; for (i = 0; i < DFS_HEADER_NAREAS; i ++) { header -> areas[i].offs = areaoffs; switch (i) { /* Fill in filename including the null */ case DFS_HEADER_AREA_FILNAME: { header -> areas[DFS_HEADER_AREA_FILNAME].size = strlen (name) + 1; memcpy (header -> area + areaoffs, name, header -> areas[DFS_HEADER_AREA_FILNAME].size); break; } /* Fill in file attributes */ case DFS_HEADER_AREA_FILATTR: { memset (&filattr, 0, sizeof filattr); filattr.create_date = oz_hw_tod_getnow (); filattr.change_date = filattr.create_date; filattr.modify_date = filattr.create_date; filattr.access_date = filattr.create_date; filattr.eofblock = efblk + 1; filattr.filattrflags = filattrflags; header -> areas[DFS_HEADER_AREA_FILATTR].size = sizeof filattr; memcpy (header -> area + areaoffs, &filattr, sizeof filattr); break; } /* Fill in the security attributes, if any */ case DFS_HEADER_AREA_SECATTR: { header -> areas[DFS_HEADER_AREA_SECATTR].size = secattrsize; memcpy (header -> area + areaoffs, secattrbuff, secattrsize); break; } /* Fill in the pointer(s), if any */ case DFS_HEADER_AREA_POINTER: { if (count != 0) { header -> areas[DFS_HEADER_AREA_POINTER].size = sizeof (Pointer); ((Pointer *)(header -> area + areaoffs)) -> blockcount = count; ((Pointer *)(header -> area + areaoffs)) -> logblock = start; } break; } } areaoffs += (header -> areas[i].size + 3) & -4; } /* Fill in the checksum */ cksm = 0; for (i = 0; i < volex -> homeblock.blocksize / sizeof (uWord); i ++) { cksm -= ((uWord *)header)[i]; } header -> checksum = cksm; /* Write the header to disk */ sts = oz_dev_vdfs_writelogblock (index_header_start + filenum - 1, 0, volex -> homeblock.blocksize, header, 0, iopex); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs init_volume: error %u writing %s initial header to block %u\n", sts, name, index_header_start + filenum - 1); } /* Free temp memory and return write status */ OZ_KNL_PGPFREE (header); return (sts); } /************************************************************************/ /* */ /* Check to see if storage bitmap block has some bits allocated in it */ /* */ /* Input: */ /* */ /* cluster = cluster number represented by first bit of clusterbuff /* clusterbuff = buffer of storage bitmap bits */ /* volume = volume block pointer */ /* count = number of blocks that are allocated */ /* start = starting logical block number of allocated blocks */ /* */ /* Output: */ /* */ /* bits set in clusterbuff for any allocated blocks */ /* */ /************************************************************************/ static void check_init_alloc (OZ_VDFS_Iopex *iopex, OZ_Dbn cluster, uLong *clusterbuff, OZ_VDFS_Volume *volume, OZ_Dbn count, OZ_Dbn start) { uLong bitsincluster; OZ_Dbn clustercount, clusterstart; OZ_VDFS_Volex *volex; volex = volume -> volex; bitsincluster = volex -> homeblock.clusterfactor * volex -> homeblock.blocksize * 8; /* Get the clustercount and clusterstart corresponding to the given block count and start */ clustercount = count; clusterstart = start; while (clusterstart % volex -> homeblock.clusterfactor != 0) { clusterstart --; clustercount ++; } while (clustercount % volex -> homeblock.clusterfactor != 0) clustercount ++; clustercount /= volex -> homeblock.clusterfactor; clusterstart /= volex -> homeblock.clusterfactor; /* If they are not within the clusterbuff, get out now */ if (clusterstart + clustercount <= cluster) return; if (cluster + bitsincluster <= clusterstart) return; /* Ok, determine bit number within clusterbuff to start at */ if (clusterstart >= cluster) clusterstart -= cluster; else { clustercount -= cluster - clusterstart; clusterstart = 0; } /* Make sure it doesn't run off end */ if (clusterstart + clustercount > bitsincluster) { clustercount = bitsincluster - clusterstart; } /* Set the bits as long as there are clusters to do */ while (clustercount > 0) { if (!((1 << (clusterstart & 31)) & clusterbuff[clusterstart/32])) { clusterbuff[clusterstart/32] |= 1 << (clusterstart & 31); if (volex -> homeblock.clustersfree == 0) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs check_init_alloc: clustersfree is zero\n"); } else { volex -> homeblock.clustersfree --; } } clustercount --; clusterstart ++; } } /************************************************************************/ /* */ /* Mount a volume */ /* */ /* Output: */ /* */ /* mount_volume = OZ_SUCCESS : successful */ /* else : error status */ /* *volume_r = volume pointer */ /* */ /************************************************************************/ static uLong dfs_mount_volume (OZ_VDFS_Devex *devex, OZ_VDFS_Volume *volume, uLong mountflags, OZ_VDFS_Iopex *iopex) { Header *headerbuff; OZ_Dbn fno, vbn; OZ_VDFS_Fileid fileid; OZ_VDFS_Volex *volex; OZ_Secaccmsk secaccmsk; Pointer *pointer; uLong *blockbuff, i, sts; uWord cksm; /* Allocate volex (volume extension) struct */ volex = OZ_KNL_PGPMALLOQ (((uByte *)&(volex -> homeblock)) + devex -> blocksize - (uByte *)volex); if (volex == NULL) return (OZ_EXQUOTAPGP); memset (volex, 0, ((uByte *)&(volex -> homeblock)) - (uByte *)volex); volume -> volex = volex; /* Calculate homeblock location */ calc_home_block (volume); /* Read the homey and validate it */ sts = oz_dev_vdfs_readlogblock (volume -> hb_logblock, 0, devex -> blocksize, &(volex -> homeblock), iopex); if (sts != OZ_SUCCESS) goto rtnerr; sts = OZ_BADHOMEBLKVER; if (volex -> homeblock.homeversion != HOMEBLOCK_VERSION) goto rtnerr; cksm = 0; for (i = 0; i < sizeof volex -> homeblock / sizeof (uWord); i ++) { cksm += ((uWord *)&(volex -> homeblock))[i]; } sts = OZ_BADHOMEBLKCKSM; if (cksm != 0) goto rtnerr; if (volex -> homeblock.blocksize != devex -> blocksize) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs mount: volume blocksize %u, disk blocksize %u\n", volex -> homeblock.blocksize, devex -> blocksize); sts = OZ_BADBLOCKSIZE; goto rtnerr; } /* Check the last write mount date. If non-zero, volume was not dismounted, so rebuild index and storage bitmaps. */ /* Also perform verification if OZ_FS_MOUNTFLAG_VERIFY set. */ if (OZ_HW_DATEBIN_TST (volex -> homeblock.lastwritemount) || (mountflags & OZ_FS_MOUNTFLAG_VERIFY)) { if (OZ_HW_DATEBIN_TST (volex -> homeblock.lastwritemount)) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs: volume %s mounted at %t was not dismounted\n", volex -> homeblock.volname, volex -> homeblock.lastwritemount); } if (!(mountflags & OZ_FS_MOUNTFLAG_READONLY) || (mountflags & OZ_FS_MOUNTFLAG_VERIFY)) { /* Scan and fix the volume */ sts = verify_volume ((mountflags & OZ_FS_MOUNTFLAG_READONLY) != 0, devex -> blocksize, devex -> totalblocks, iopex); if (sts != OZ_SUCCESS) goto rtnerr; /* Re-read the homeblock */ sts = oz_dev_vdfs_readlogblock (volume -> hb_logblock, 0, devex -> blocksize, &(volex -> homeblock), iopex); if (sts != OZ_SUCCESS) goto rtnerr; } } /* If homeblock indicates WRITETHRU mode, force mountflags to do WRITETHRU mode so we only have to test one bit */ if (volex -> homeblock.initflags & OZ_FS_INITFLAG_WRITETHRU) volume -> mountflags |= OZ_FS_MOUNTFLAG_WRITETHRU; /* Allocate directory block buffer (used by lookup_file, extend_file and remove_file routines) */ volume -> dirblocksize = volex -> homeblock.clusterfactor * volex -> homeblock.blocksize; volume -> clusterfactor = volex -> homeblock.clusterfactor; volex -> dirblockbuff3 = OZ_KNL_PGPMALLOQ (volume -> dirblocksize * 3); if (volex -> dirblockbuff3 == NULL) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs mount: no quota for %u byte dirblockbuff3\n", volume -> dirblocksize * 3); sts = OZ_EXQUOTAPGP; goto rtnerr; } /* Determine security access mask to open files with */ secaccmsk = OZ_SECACCMSK_LOOK | OZ_SECACCMSK_READ | OZ_SECACCMSK_WRITE; if (mountflags & OZ_FS_MOUNTFLAG_READONLY) secaccmsk = OZ_SECACCMSK_LOOK | OZ_SECACCMSK_READ; /* Open the indexheaders file. This must be the first file opened as it is how we figure out where all other file headers are. */ fileid.num = SACRED_FIDNUM_INDEXHEADERS; fileid.seq = 0; fileid.rvn = 1; sts = oz_dev_vdfs_open_file (volume, &fileid, secaccmsk, &(volex -> indexheaders), iopex); if (sts != OZ_SUCCESS) goto rtnerr; /* Open the index bitmap file */ fileid.num = SACRED_FIDNUM_INDEXBITMAP; fileid.seq = 0; fileid.rvn = 1; sts = oz_dev_vdfs_open_file (volume, &fileid, secaccmsk, &(volex -> indexbitmap), iopex); if (sts != OZ_SUCCESS) goto rtnerr; /* Open the storage bitmap file */ fileid.num = SACRED_FIDNUM_STORAGEBITMAP; fileid.seq = 0; fileid.rvn = 1; sts = oz_dev_vdfs_open_file (volume, &fileid, secaccmsk, &(volex -> storagebitmap), iopex); if (sts != OZ_SUCCESS) goto rtnerr; /* Open the root directory file (we only need lookup access, though). This is done as an optimisation */ /* so we aren't constantly doing an open/close on the root directory for every lookup_file operation. */ fileid.num = SACRED_FIDNUM_ROOTDIRECTORY; fileid.seq = 0; fileid.rvn = 1; sts = oz_dev_vdfs_open_file (volume, &fileid, OZ_SECACCMSK_LOOK, &(volex -> rootdirectory), iopex); if (sts != OZ_SUCCESS) goto rtnerr; /* Now write the home block back if we are write enabled (with the current datebin) */ if (!(mountflags & OZ_FS_MOUNTFLAG_READONLY)) { volex -> homeblock.lastwritemount = oz_hw_tod_getnow (); sts = dfs_writehomeblock (volume, iopex); if (sts == OZ_WRITELOCKED) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs: volume is write-locked - proceeding in read-only mode\n"); volume -> mountflags |= OZ_FS_MOUNTFLAG_READONLY; } else if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs: error %u marking volume dirty\n", sts); goto rtnerr; } } return (OZ_SUCCESS); rtnerr: if (volex -> dirblockbuff3 != NULL) OZ_KNL_PGPFREE (volex -> dirblockbuff3); OZ_KNL_PGPFREE (volex); volume -> volex = NULL; return (sts); } /* Calculate volume's home block location */ static void calc_home_block (OZ_VDFS_Volume *volume) { if (volume -> bb_logblock > 0) volume -> hb_logblock = volume -> bb_logblock - 1; /* if there's room just before the boot block, put it there */ else volume -> hb_logblock = volume -> bb_logblock + volume -> bb_nblocks; /* otherwise, put it just after the boot block */ } /* Set 'nbits' starting at 'bitno' in 'file' */ static uLong setbitsinfile (OZ_VDFS_File *file, uLong nbits, uLong bitno, OZ_VDFS_Iopex *iopex, uLong blocksize, uLong *blockbuff) { uLong bitinblock, bitsinblock, sts; OZ_Dbn vbn; bitsinblock = blocksize * 8; while (nbits > 0) { vbn = (bitno / bitsinblock) + 1; sts = oz_dev_vdfs_readvirtblock (file, vbn, 0, blocksize, blockbuff, iopex, 0); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs setbitsinfile: error %u reading vbn %u\n", sts, vbn); return (sts); } bitinblock = bitno % bitsinblock; while ((nbits > 0) && (bitinblock < bitsinblock)) { blockbuff[bitinblock/32] |= 1 << (bitinblock % 32); -- nbits; bitno ++; bitinblock ++; } sts = oz_dev_vdfs_writevirtblock (file, vbn, 0, blocksize, blockbuff, iopex, 0); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs setbitsinfile: error %u writing vbn %u\n", sts, vbn); return (sts); } } return (OZ_SUCCESS); } /************************************************************************/ /* */ /* Dismount volume */ /* */ /* Input: */ /* */ /* volume = volume to be dismounted */ /* unload = 0 : leave volume online */ /* 1 : unload volume (if possible) */ /* */ /* Output: */ /* */ /* volume dismounted */ /* */ /************************************************************************/ static uLong dfs_dismount_volume (OZ_VDFS_Devex *devex, OZ_VDFS_Volume *volume, int unload, int shutdown, OZ_VDFS_Iopex *iopex) { OZ_VDFS_Volex *volex; uLong n, sts; volex = volume -> volex; if (volex == NULL) return (OZ_NOTMOUNTED); /* Make sure just our internal files are open */ if (!shutdown) { n = 0; if (volex -> indexbitmap != NULL) n ++; if (volex -> indexheaders != NULL) n ++; if (volex -> rootdirectory != NULL) n ++; if (volex -> storagebitmap != NULL) n ++; if (volume -> nopenfiles != n) return (OZ_OPENFILESONVOL); } /* Close all those files - in shutdown mode there may be channels pointing */ /* to them but having devex -> shutdown set will prevent all access */ while (volume -> openfiles != NULL) { oz_dev_vdfs_close_file (volume -> openfiles, iopex); } /* If we were write enabled, clear lastwritemount in the home block to indicate properly dismounted */ if (!(volume -> mountflags & OZ_FS_MOUNTFLAG_READONLY)) { OZ_HW_DATEBIN_CLR (volex -> homeblock.lastwritemount); sts = dfs_writehomeblock (volume, iopex); if (sts != OZ_SUCCESS) oz_dev_vdfs_printk (iopex, "oz_dev_dfs dismount: error %u clearing lastwritemount date in header\n", sts); } /* Free off directory block buffer */ if (volex -> dirblockbuff3 != NULL) { OZ_KNL_PGPFREE (volex -> dirblockbuff3); volex -> dirblockbuff3 = NULL; } /* Free off the volex struct */ OZ_KNL_PGPFREE (volex); volume -> volex = NULL; return (OZ_SUCCESS); } /************************************************************************/ /* */ /* Get root directory's fileid */ /* */ /************************************************************************/ static uLong dfs_get_rootdirid (OZ_VDFS_Devex *devex, OZ_VDFS_Fileid *rootdirid_r) { rootdirid_r -> num = SACRED_FIDNUM_ROOTDIRECTORY; rootdirid_r -> rvn = 1; rootdirid_r -> seq = 0; return (OZ_SUCCESS); } /************************************************************************/ /* */ /* Get pointer to a file's fileid */ /* */ /************************************************************************/ static const OZ_VDFS_Fileid *dfs_get_fileid (OZ_VDFS_File *file) { return (&(file -> filex -> header.fileid)); } /************************************************************************/ /* */ /* Lookup a file in a directory */ /* */ /* Input: */ /* */ /* dirfile = directory file */ /* namelen = length of *name string */ /* name = name to lookup (not necessarily null terminated) */ /* */ /* Output: */ /* */ /* lookup_file = OZ_SUCCESS : successful */ /* OZ_NOSUCHFILE : entry not found */ /* else : error status */ /* *dirvbn_r = dir vbn that the entry was found in (or last vbn looked at) /* *fileid_r = file-id of found file */ /* *name_r = name found */ /* */ /* Note: */ /* */ /* This routine does not do wildcard scanning, it just finds a */ /* particular file (like for an 'open' type request). */ /* */ /************************************************************************/ static uLong lookup_file_scan (OZ_Dcmpb *dcmpb, uLong status); static uLong dfs_lookup_file (OZ_VDFS_File *dirfile, int namelen, char const *name, OZ_VDFS_Fileid *fileid_r, char *name_r, OZ_VDFS_Iopex *iopex) { OZ_Dbn dirvbn; return (lookup_file (dirfile, namelen, name, &dirvbn, fileid_r, name_r, iopex)); } static uLong lookup_file (OZ_VDFS_File *dirfile, int namelen, char const *name, OZ_Dbn *dirvbn_r, OZ_VDFS_Fileid *fileid_r, char *name_r, OZ_VDFS_Iopex *iopex) { char c, *p; OZ_VDFS_File *file; int cmp, naml; uLong i, sts; OZ_Event *event; OZ_VDFS_Filex *dirfilex; OZ_VDFS_Volex *volex; OZ_VDFS_Volume *volume; dirfilex = dirfile -> filex; volume = dirfile -> volume; volex = volume -> volex; if (!IS_DIRECTORY (dirfilex -> header)) return (OZ_FILENOTADIR); /* An null name string means looking up the directory itself */ if (namelen == 0) { *fileid_r = dirfilex -> header.fileid; /* return the fileid of the directory */ if (name_r != NULL) *name_r = 0; /* return a null name string */ return (OZ_SUCCESS); /* always successful */ } /* Parse name into ; */ /* omitted, ;, ;0, ;-0 all refer to the very newest */ /* ;n means that exact version */ /* ;-n means next to n'th newest, eg, ;-1 means second newest */ naml = namelen; if (naml >= sizeof volex -> v.lf.fname) return (OZ_FILENAMETOOLONG); memcpy (volex -> v.lf.fname, name, naml); /* copy the filename string */ volex -> v.lf.fname[naml++] = 0; /* null terminate and count the null in the length */ p = strchr (volex -> v.lf.fname, ';'); /* scan given spec for version number */ volex -> v.lf.version = 0; /* default to looking for ;0, ie, the very newest version */ volex -> v.lf.versign = 0; if (p != NULL) { /* see if ; is present */ *(p ++) = 0; /* replace ; with a null */ naml = p - volex -> v.lf.fname; /* set new length excluding ;version but including terminating null */ c = *p; /* get first character following ; */ if (c == '-') volex -> v.lf.versign = -1; /* if -, set the flag */ if (volex -> v.lf.versign != 0) c = *(++ p); /* get character following the - */ while ((c >= '0') && (c <= '9')) { /* repeat as long as we have numeric characters */ volex -> v.lf.version = volex -> v.lf.version * 10 + c - '0'; /* convert character and put in accumulator */ c = *(++ p); /* get next character in version string */ } if (c != 0) return (OZ_BADFILEVER); /* non-numeric character in version, error */ } if (naml < 2) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs lookup_file: name must contain at least one character in %*.*s\n", namelen, namelen, name); return (OZ_BADFILENAME); } /* Scan directory for the file */ volex -> v.lf.nclusters = dirfile -> allocblocks / volex -> homeblock.clusterfactor; // get number of clusters in directory file if (volex -> v.lf.nclusters == 0) return (OZ_NOSUCHFILE); // - that sure made the job easy volex -> v.lf.multiplier = 1; // start with middle volex -> v.lf.level = 1; // ... ie, cluster=(nclusters*1)>>1 volex -> v.lf.dirfile = dirfile; volex -> v.lf.fileid_r = fileid_r; volex -> v.lf.name_r = name_r; volex -> v.lf.namelen = naml; volex -> v.lf.state = LOOKUP_FILE_STATE_GATHERNAME; volex -> v.lf.lastbuckvbn = 0; volex -> v.lf.thisbuckvbn = ((volex -> v.lf.nclusters >> 1) * volex -> homeblock.clusterfactor) + 1; volex -> v.lf.dcmpb.virtblock = volex -> v.lf.thisbuckvbn; volex -> v.lf.dcmpb.nbytes = volex -> homeblock.clusterfactor * volex -> homeblock.blocksize; volex -> v.lf.dcmpb.blockoffs = 0; // start at beginning of block volex -> v.lf.dcmpb.entry = lookup_file_scan; // call this scanning routine volex -> v.lf.dcmpb.param = iopex; // this is its parameter volex -> v.lf.dcmpb.ix4kbuk = 0; volex -> v.lf.status = OZ_PENDING; // assume async completion sts = oz_dev_vdfs_dcache_map_vbn_to_lbn (&(volex -> v.lf.dcmpb), dirfile); // get lbn of first directory block to scan if (sts == OZ_SUCCESS) sts = oz_dev_vdfs_dcache_map_blocks (&(volex -> v.lf.dcmpb)); // process the request if (sts == OZ_STARTED) { event = iopex -> devex -> event; // wait for async completion while (volex -> v.lf.state != LOOKUP_FILE_STATE_DONE) { oz_knl_event_waitone (event); oz_knl_event_set (event, 0); } sts = volex -> v.lf.status; } else if (sts == OZ_SUCCESS) sts = volex -> v.lf.status; // get completion status *dirvbn_r = volex -> v.lf.dcmpb.virtblock; // return last vbn looked at return (sts); // all done } /* But the kettle's on the boil, and we're so easy called away */ /************************************************************************/ /* */ /* This routine is called by the disk cache routines when a page of */ /* the directory is available */ /* */ /* Input: */ /* */ /* dcmpb -> virtblock = virtual block number of directory */ /* dcmpb -> blockoffs = offset in block to start at (should be zero) /* dcmpb -> phypage = cache page's physical page number */ /* dcmpb -> pageoffs = byte offset in the phypage of virtblock */ /* dcmpb -> nbytes = number of bytes that we have to work with */ /* volex -> v.lf.fname = filename we're looking for */ /* volex -> v.lf.nclusters = number of clusters in directory */ /* volex -> v.lf.multiplier = multiplier factor for current cluster number /* volex -> v.lf.level = search level we're at */ /* volex -> v.lf.dirfile = directory file */ /* volex -> v.lf.fileid_r = where to return found file-id */ /* volex -> v.lf.name_r = where to return resultant filename */ /* volex -> v.lf.namelen = length of fname including null */ /* volex -> v.lf.versign = 0: ;n form, -1: ;-n form */ /* volex -> v.lf.version = version number (abs value) */ /* volex -> v.lf.dcmpb = same as dcmpb */ /* volex -> v.lf.nbytes_name_matched = how many bytes of fname matched on last page /* volex -> v.lf.state = current scanning state */ /* volex -> v.lf.partialdirpnt = saved partial dirpnt when it crosses page boundary /* */ /************************************************************************/ static uLong lookup_file_scan (OZ_Dcmpb *dcmpb, uLong status) { char c, *dirpagebuff, *p; Dirpnt *dirpnt; int begofbucket, cmp; OZ_VDFS_Iopex *iopex; OZ_Dbn deltacluster; OZ_Pagentry savepte; uLong cmplen, dirpagesize, i, sts; OZ_VDFS_Volex *volex; OZ_VDFS_Volume *volume; iopex = dcmpb -> param; volume = iopex -> devex -> volume; volex = volume -> volex; /* Maybe this is the final call to finish up */ if (status != OZ_PENDING) { if (volex -> v.lf.status == OZ_PENDING) volex -> v.lf.status = status; // save completion status OZ_HW_MB; // make sure main loop sees all written volex -> v.lf.state = LOOKUP_FILE_STATE_DONE; // ... before it sees we're all done oz_knl_event_set (iopex -> devex -> event, 1); // wake it up return (0); } /* We always work on block boundaries */ if (dcmpb -> blockoffs != 0) oz_crash ("oz_dev_dfs lookup_file_scan: blockoffs %u", dcmpb -> blockoffs); if ((dcmpb -> nbytes % volex -> homeblock.blocksize) != 0) { oz_crash ("oz_dev_dfs lookup_file_scan: nbytes %u", dcmpb -> nbytes); } /* Map the cache page to virtual memory */ dirpagesize = dcmpb -> nbytes; // this is how much we have to work with if (dirpagesize > volex -> homeblock.clusterfactor * volex -> homeblock.blocksize) oz_crash ("oz_dev_dfs lookup_file: nbytes %u", dirpagesize); dirpagebuff = oz_hw_phys_mappage (dcmpb -> phypage, &savepte); // map physical page to virtual memory dirpagebuff += dcmpb -> pageoffs; // point to the first byte of 'virtblock' /* If we're at the beginning of a bucket, reset search state */ /* If we're in the middle of a bucket, retain previous search state */ begofbucket = 0; if (((dcmpb -> virtblock - 1) % volex -> homeblock.clusterfactor) == 0) { volex -> v.lf.nbytes_name_gathered = -1; // we need to get the 'same as' byte volex -> v.lf.dirpntbytesaved = 0; // no bytes saved in partialdirpnt yet volex -> v.lf.state = LOOKUP_FILE_STATE_GATHERNAME; // start by gathering up a filename string if (dirpagebuff[0] != 1) { // it must start with a complete filename oz_dev_vdfs_printk (iopex, "oz_dev_dfs lookup_file: vbn %u doesn't begin with a complete filename\n", dcmpb -> virtblock); volex -> v.lf.status = OZ_DIRCORRUPT; goto alldone; } begofbucket = 1; } /* Scan the directory page for correct filename */ check_state: switch (volex -> v.lf.state) { /* Gathering up a name string and putting it in v.lf.lastname. Then, if we have it all, compare it to requested name string. */ case LOOKUP_FILE_STATE_GATHERNAME: { if (volex -> v.lf.nbytes_name_gathered < 0) { // if we need the 'same as' byte if (dirpagesize == 0) goto on_to_next_page; // see if reached end-of-block -- dirpagesize; volex -> v.lf.nbytes_name_gathered = *(dirpagebuff ++); // get 'same as' byte if (-- (volex -> v.lf.nbytes_name_gathered) < 0) goto on_to_next_bucket; // if zero, hit end-of-bucket } do { if (volex -> v.lf.nbytes_name_gathered == sizeof volex -> v.lf.lastname) { // check for buffer overflow oz_dev_vdfs_printk (iopex, "oz_dev_dfs lookup_file: vbn %u filename too long\n", dcmpb -> virtblock); volex -> v.lf.status = OZ_DIRCORRUPT; goto alldone; } if (dirpagesize == 0) goto on_to_next_page; // see if reached end-of-page -- dirpagesize; // if not, copy the char } while ((volex -> v.lf.lastname[volex->v.lf.nbytes_name_gathered++] = *(dirpagebuff ++)) != 0); volex -> v.lf.dirpntbytesaved = 0; // we are at beg of dirpnts now cmp = strcmp (volex -> v.lf.fname, volex -> v.lf.lastname); // compare the names if (cmp < 0) goto on_to_prev_bucket; // if name .lt. directory, back up some if (cmp == 0) { volex -> v.lf.state = LOOKUP_FILE_STATE_MATCHVERSION; // if name .eq. directory, find matching version if (!begofbucket) goto check_state; // if not at beg of bucket, it can't have a same-named entry in prev bucket begofbucket = 0; // (any other names we find in bucket aren't at the beginning) if (volex -> v.lf.thisbuckvbn == volex -> v.lf.lastbuckvbn + volex -> homeblock.clusterfactor) goto check_state; // if we are already in sequential mode, find matching version if (volex -> v.lf.thisbuckvbn <= volex -> homeblock.clusterfactor) goto check_state; // nothing prior to this bucket, find matching version if ((volex -> v.lf.versign == 0) && (volex -> v.lf.version > 0)) { // see if looking for an explicit version number if (dirpagesize < sizeof (Dirpnt)) goto check_state; // do it the hard way if we don't have a version to check if (((Dirpnt *)dirpagebuff) -> version >= volex -> v.lf.version) goto check_state; // check the array if it's possible we're in there } volex -> v.lf.lastbuckvbn = volex -> v.lf.thisbuckvbn; // it might have started in bucket immediately before this one volex -> v.lf.thisbuckvbn -= volex -> homeblock.clusterfactor; goto read_bucket; } volex -> v.lf.state = LOOKUP_FILE_STATE_SKIPDIRPNTS; // if name .gt. directory, skip all its versions begofbucket = 0; // (any other names we find in bucket aren't at the beginning) // fall through ... } /* Skip what's left of a dirpnt array from last time, it terminates on an entry with a zero version number */ case LOOKUP_FILE_STATE_SKIPDIRPNTS: { /* Optimise if there is nothing in partialdirpnt and the page contains a whole dirpnt */ if ((volex -> v.lf.dirpntbytesaved == 0) && (dirpagesize >= sizeof (Dirpnt))) { dirpnt = (Dirpnt *)dirpagebuff; // point to a complete dirpnt entry dirpagesize -= sizeof (Dirpnt); // skip pointers past it dirpagebuff += sizeof (Dirpnt); } /* Either there is something in partialdirpnt or we have to put something in there */ else { i = sizeof (Dirpnt) - volex -> v.lf.dirpntbytesaved; if (i > dirpagesize) i = dirpagesize; memcpy (((char *)&(volex -> v.lf.partialdirpnt)) + volex -> v.lf.dirpntbytesaved, dirpagebuff, i); volex -> v.lf.dirpntbytesaved += i; /* Read another directory page if we don't have a whole dirpnt yet */ if (volex -> v.lf.dirpntbytesaved < sizeof (Dirpnt)) goto on_to_next_page; /* We have the whole thing, point to it */ dirpagesize -= i; dirpagebuff += i; volex -> v.lf.dirpntbytesaved = 0; dirpnt = &(volex -> v.lf.partialdirpnt); } /* Stop skipping if we found a version zero entry, otherwise continue skipping */ if (dirpnt -> version == 0) { // see if it was version zero volex -> v.lf.state = LOOKUP_FILE_STATE_GATHERNAME; // last one had version zero, so we're not skipping dirpnt array volex -> v.lf.nbytes_name_gathered = -1; // now we start looking for the filename again } goto check_state; } /* Name matches, find matching dirpnt array entry */ /* omitted, ;, ;0, ;-0 all refer to the very newest */ /* ;n means that exact version */ /* ;-n means next to n'th newest, eg, ;-1 means second newest */ case LOOKUP_FILE_STATE_MATCHVERSION: { while (1) { /* Point dirpnt at entry to check and advance dirpagesize/dirpagebuff on to next entry */ if ((volex -> v.lf.dirpntbytesaved == 0) && (dirpagesize >= sizeof (Dirpnt))) { // see if wholly contained within dirpagebuff dirpnt = (Dirpnt *)dirpagebuff; // if so, just point to it dirpagesize -= sizeof (Dirpnt); // ... and advance dirpagebuff pointer dirpagebuff += sizeof (Dirpnt); } else { i = sizeof (Dirpnt) - volex -> v.lf.dirpntbytesaved; // if not, see how much we need from dirpagebuff if (i > dirpagesize) i = dirpagesize; // don't take more than it has memcpy (((char *)&(volex -> v.lf.partialdirpnt)) + volex -> v.lf.dirpntbytesaved, dirpagebuff, i); volex -> v.lf.dirpntbytesaved += i; // we have this much more now if (volex -> v.lf.dirpntbytesaved < sizeof (Dirpnt)) goto on_to_next_page; // go read more if we don't have enough yet dirpagesize -= i; // ok, remove the last bytes from dirpagebuff dirpagebuff += i; dirpnt = &(volex -> v.lf.partialdirpnt); // ... and point to temp dirpnt volex -> v.lf.dirpntbytesaved = 0; // we're using it up so it's no longer saved } /* Check for end of dirpnt array indicated by a version number of zero. If so, the version we want might be in the very next */ /* bucket. But don't bother checking if we're not at the end of this bucket or we've already looked in the very next bucket. */ if (dirpnt -> version == 0) { // maybe we're at end of dirpnt array if ((dirpagesize == 0) || (*dirpagebuff == 0)) { // if so, see if we're at end of directory bucket if ((volex -> v.lf.versign < 0) || (volex -> v.lf.lastbuckvbn != volex -> v.lf.thisbuckvbn + volex -> homeblock.clusterfactor)) { // ... and we haven't already looked in next bucket volex -> v.lf.lastbuckvbn = volex -> v.lf.thisbuckvbn; // ... then go on to next bucket as it might have our version volex -> v.lf.thisbuckvbn += volex -> homeblock.clusterfactor; goto read_bucket; } } goto not_found; // ... otherwise, the name is not found } /* See if we have found the correct version */ if (volex -> v.lf.version == 0) goto found_it; // finding ;-n form, and we've counted enough of them if (volex -> v.lf.versign < 0) volex -> v.lf.version --; // skipping over one more for the ;-n form else { if (dirpnt -> version == volex -> v.lf.version) goto found_it; // maybe we found an exact match for ;n form if (dirpnt -> version < volex -> v.lf.version) goto not_found; // ... or maybe we aren't going to find it } } } /* Who knows what */ default: oz_crash ("oz_dev_dfs lookup_file: bad state %d", volex -> v.lf.state); } /* Go on to next bucket in the directory */ on_to_next_bucket: if (volex -> v.lf.lastbuckvbn == volex -> v.lf.thisbuckvbn + volex -> homeblock.clusterfactor) { volex -> v.lf.lastbuckvbn = volex -> v.lf.thisbuckvbn; volex -> v.lf.thisbuckvbn += volex -> homeblock.clusterfactor; goto read_bucket; } volex -> v.lf.multiplier += volex -> v.lf.multiplier + 1; goto on_to_bucket; on_to_prev_bucket: if (volex -> v.lf.thisbuckvbn == volex -> v.lf.lastbuckvbn + volex -> homeblock.clusterfactor) goto not_found; volex -> v.lf.multiplier += volex -> v.lf.multiplier - 1; on_to_bucket: volex -> v.lf.lastbuckvbn = volex -> v.lf.thisbuckvbn; // save what bucket we've just finished volex -> v.lf.level ++; // we're one level deeper into tree, now #ifdef OZ_HW_TYPE_486 asm ("\n" "movl %1,%%eax\n" // get nclusters in %eax "mull %2\n" // mult %eax by multiplier, put in %edx:%eax "movb %3,%%cl\n" // get level in %cl "shrdl %%edx,%%eax\n" // shift %edx:%eax right by %cl "mull %4\n" // mult %eax by clusterfactor, put in %edx:%eax "incl %%eax\n" // increment because vbn's are 1-based "movl %%eax,%0\n" // store result in thisbuckvbn : "=m" (volex -> v.lf.thisbuckvbn) // %0 : "m" (volex -> v.lf.nclusters), // %1 "m" (volex -> v.lf.multiplier), // %2 "m" (volex -> v.lf.level), // %3 "m" (volex -> homeblock.clusterfactor) // %4 : "eax", "ecx", "edx"); #else volex -> v.lf.thisbuckvbn = ((((uQuad)(volex -> v.lf.nclusters) * volex -> v.lf.multiplier) >> volex -> v.lf.level) * volex -> homeblock.clusterfactor) + 1; #endif if (volex -> v.lf.thisbuckvbn == volex -> v.lf.lastbuckvbn) goto not_found; read_bucket: volex -> v.lf.dcmpb.virtblock = volex -> v.lf.thisbuckvbn; goto read_again; /* Go on to next page in the same directory block */ on_to_next_page: dcmpb -> virtblock += dcmpb -> nbytes / volex -> homeblock.blocksize; // increment block number by how much we got // nbytes is always a multiple of blocksize /* Start reading 'dcmpb.virtblock' from disk. Call myself back when it's available. */ read_again: oz_hw_phys_unmappage (savepte); // unmap the old cache page dcmpb -> nbytes = volex -> homeblock.clusterfactor * volex -> homeblock.blocksize; // map a whole cluster at a time if possible sts = oz_dev_vdfs_dcache_map_vbn_to_lbn (dcmpb, volex -> v.lf.dirfile); // get lbn of directory page to scan if (sts != OZ_SUCCESS) { // if ok, return to start reading if (sts == OZ_ENDOFFILE) sts = OZ_NOSUCHFILE; volex -> v.lf.status = sts; // map failed, return error status dcmpb -> nbytes = 0; // we don't want any more pages read from disk } return (0); // we (hopefully) didn't modify the page at all /* Correct entry will not be found */ not_found: volex -> v.lf.status = OZ_NOSUCHFILE; goto alldone; /* Correct entry has been found */ found_it: *(volex -> v.lf.fileid_r) = dirpnt -> fileid; // return the file-id if (volex -> v.lf.name_r != NULL) { // see if caller wants resultant name string memcpy (volex -> v.lf.name_r, volex -> v.lf.fname, volex -> v.lf.namelen); // if so, copy out name w/out version if (volex -> v.lf.fname[volex -> v.lf.namelen-2] != '/') { // see if it is a directory name volex -> v.lf.name_r[volex -> v.lf.namelen-1] = ';'; // if not, append ;version oz_hw_itoa (dirpnt -> version, FILENAME_MAX - volex -> v.lf.namelen, volex -> v.lf.name_r + volex -> v.lf.namelen); } } volex -> v.lf.status = OZ_SUCCESS; // successful alldone: dcmpb -> nbytes = 0; // we don't want any more pages read from disk oz_hw_phys_unmappage (savepte); // unmap the cache page return (0); // we (hopefully) didn't modify the page at all } /************************************************************************/ /* */ /* Rename a file */ /* */ /* Input: */ /* */ /* olddirfile = old directory file */ /* oldname = old file name */ /* */ /* newdirfile = new directory file */ /* newdirname = new directory name (diag only) */ /* newnamelen = length of new name to enter */ /* newname = new name to enter */ /* newversion = make sure name is the highest version */ /* */ /* file = open file pointer (or NULL if not open) */ /* fileid = the file's id */ /* */ /* Output: */ /* */ /* rename_file = OZ_SUCCESS : successful */ /* else : error status */ /* *newname_r = filled in with resultant name (incl version) */ /* */ /************************************************************************/ static uLong dfs_rename_file (OZ_VDFS_File *olddirfile, char const *oldname, OZ_VDFS_File *newdirfile, char const *newdirname, int newnamelen, char const *newname, int newversion, OZ_VDFS_File *file, const OZ_VDFS_Fileid *fileid, char *newname_r, OZ_VDFS_Iopex *iopex) { int i; OZ_VDFS_File *entfile; OZ_VDFS_Filex *extfilex, *filex; uLong sts; /* Open the file to be renamed */ entfile = file; if (entfile == NULL) { sts = oz_dev_vdfs_open_file (olddirfile -> volume, fileid, 0, &entfile, iopex); if (sts != OZ_SUCCESS) return (sts); } /* Enter the file in the new directory */ sts = dfs_enter_file (newdirfile, newdirname, newnamelen, newname, newversion, entfile, fileid, newname_r, iopex); /* Remove it from the old directory */ if (sts == OZ_SUCCESS) sts = dfs_remove_file (olddirfile, oldname, NULL, iopex); /* Write new directory and name to the file's header. Do this regardless of whether the */ /* header has the old name to allow someone to 'fix' a broken backlink by doing a rename. */ if (sts == OZ_SUCCESS) { filex = entfile -> filex; filex -> header.areas[DFS_HEADER_AREA_FILNAME].size = 0; // wipe out old name sts = make_header_room (entfile, filex, newnamelen + 1, DFS_HEADER_AREA_FILNAME, 1, &extfilex, iopex); // make room for new name if (sts == OZ_SUCCESS) { filex -> header.dirid = newdirfile -> filex -> header.fileid; // set up new directory id extfilex -> header.areas[DFS_HEADER_AREA_FILNAME].size = newnamelen + 1; // set new name length incl null memcpy (extfilex -> header.area + extfilex -> header.areas[DFS_HEADER_AREA_FILNAME].offs, newname, newnamelen); // copy name string extfilex -> header.area[extfilex->header.areas[DFS_HEADER_AREA_FILNAME].offs+newnamelen] = 0; // put in a null oz_dev_vdfs_mark_header_dirty (entfile); // header needs to be written back out mark_exthdr_dirty (extfilex, entfile); } } if (entfile != file) oz_dev_vdfs_close_file (entfile, iopex); return (sts); } /************************************************************************/ /* */ /* Enter a file in a directory */ /* */ /* Input: */ /* */ /* dirfile = directory file */ /* dirname = directory name (diag only) */ /* namelen = length of name to enter */ /* name = name to enter */ /* newversion = make sure name is the highest version */ /* file = open file pointer (or NULL if not open) */ /* fileid = the file's id */ /* */ /* Output: */ /* */ /* enter_file = OZ_SUCCESS : successful */ /* else : error status */ /* *name_r = filled in with resultant name (incl version) */ /* */ /************************************************************************/ static uLong dfs_enter_file (OZ_VDFS_File *dirfile, char const *dirname, int namelen, char const *name, int newversion, OZ_VDFS_File *file, const OZ_VDFS_Fileid *fileid, char *name_r, OZ_VDFS_Iopex *iopex) { uByte *dirblockbuff, *dirblockbuff2, *dirblockbuff3; char c, fname[FILENAME_MAX], lastname[FILENAME_MAX], nextname[FILENAME_MAX], *p, vstr[12]; Dirpnt dirpnt[2]; OZ_VDFS_File *entfile; OZ_VDFS_Filex *extfilex; int casx, cmp, i, j, k, l, match_last, match_next, n, prev_cmp; OZ_Dbn direfblk, dirvbn, extendsize, extendvbn; OZ_Dbn prev_vbn; OZ_VDFS_Fileid dummy_fileid; uLong adj_from, adj_to, dirblocksize, sts, v, version, vl, writesize; uLong prev_i, prev_j, prev_k; OZ_VDFS_Volex *volex; OZ_VDFS_Volume *volume; volume = dirfile -> volume; volex = volume -> volex; if (name_r != NULL) *name_r = 0; vstr[0] = 0; casx = 0; adj_from = 0; adj_to = 0; /* Must be a directory we are entering into */ /* Cannot enter a sacred file, either */ if (!IS_DIRECTORY (dirfile -> filex -> header)) return (OZ_FILENOTADIR); if (fileid -> seq == 0) return (OZ_SACREDFILE); /* Make sure they give us sane stuff and count length including trailing null */ /* Only allow printable characters, and allow a slash only as the last char */ /* Don't allow wildcard characters in the filename, either (stupid nerds) */ if (namelen >= sizeof fname) return (OZ_FILENAMETOOLONG); for (i = 0; i < namelen; i ++) { c = name[i]; if ((c < ' ') || (c >= 127)) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: can't have non-printable character (0x%2.2X) in a filename\n", ((uLong)c) & 0xFF); return (OZ_BADFILENAME); } if ((c == '*') || (c == '?') || (c == '~')) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: can't have wildcard character '%c' in a filename\n", c); return (OZ_BADFILENAME); } if ((c == '(') || (c == ')')) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: can't have parentheses in a filename (looks like secattrs)\n"); return (OZ_BADFILENAME); } if ((c == '/') && (i != namelen - 1)) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: can't have embedded '/'s in a filename\n"); return (OZ_BADFILENAME); } } memcpy (fname, name, namelen); fname[namelen] = 0; /* Don't allow a null name */ if ((namelen == 0) || ((namelen == 1) && (fname[0] == '/'))) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: can't have a null filename\n"); return (OZ_BADFILENAME); } /* Don't allow name of '.', './', '..' or '../' so they can't create files named as such, because we use those names specially */ if ((fname[0] == '.') && ((namelen == 1) || (fname[1] == '/') || ((fname[1] == '.') && ((namelen == 2) || (fname[2] == '/'))))) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: can't have ., ./ or ../ as name in %*.*s\n", namelen, namelen, name); return (OZ_BADFILENAME); } /* Have 'namelen' include the trailing null */ namelen ++; /* Open the file to be entered so we can increment its dircount - this also validates its fileid */ entfile = file; if (entfile == NULL) { sts = oz_dev_vdfs_open_file (volume, fileid, 0, &entfile, iopex); if (sts != OZ_SUCCESS) goto cleanup; } /* If the file being entered is a directory then the name must end in a slash, otherwise it must not */ sts = OZ_BADFILENAME; if (IS_DIRECTORY (entfile -> filex -> header) ^ (name[namelen-2] == '/')) goto cleanup; /* Get version number (only allow nothing, ;, ;0, ;n (no +/- stuff)) */ p = strchr (fname, ';'); version = 0; if (p != NULL) { *(p ++) = 0; namelen = p - fname; c = *p; while ((c >= '0') && (c <= '9')) { version = version * 10 + c - '0'; c = *(++ p); } sts = OZ_BADFILEVER; if (c != 0) goto cleanup; } memset (dirpnt, 0, sizeof dirpnt); dirpnt[0].version = version; dirpnt[0].fileid = *fileid; sts = OZ_BADFILENAME; if (namelen < 2) goto cleanup; /* Directories are always version 1 */ /* Note that the above stuff precludes specifying a version number */ /* with a directory, eg, you can't give dirname;5/ or dirname/;5 */ if (IS_DIRECTORY (entfile -> filex -> header)) { dirpnt[0].version = version = 1; newversion = 0; } /* Look in directory for a spot to enter it */ dirblocksize = volume -> dirblocksize; /* get the 'block size' = one cluster at a time */ dirblockbuff = volex -> dirblockbuff3; /* point to block buffer for reading the directory */ dirblockbuff2 = dirblockbuff + dirblocksize; /* ... plus two extras for extending it */ dirblockbuff3 = dirblockbuff2 + dirblocksize; direfblk = dirfile -> allocblocks + 1; /* compute eof pointer = very end of the file */ dirvbn = 1; if (direfblk == 1) { if (dirpnt[0].version == 0) dirpnt[0].version = 1; dirblockbuff[0] = 1; /* directory is empty, create a new block with this one entry */ memcpy (dirblockbuff + 1, fname, namelen); memcpy (dirblockbuff + 1 + namelen, dirpnt, sizeof dirpnt); memset (dirblockbuff + 1 + namelen + sizeof dirpnt, 0, dirblocksize - 1 - namelen - sizeof dirpnt); casx = 1; extendsize = 1; extendvbn = 1; writesize = dirblocksize; oz_hw_itoa (dirpnt[0].version, sizeof vstr, vstr); if (namelen + strlen (vstr) + 1 >= FILENAME_MAX) { sts = OZ_FILENAMETOOLONG; goto cleanup; } adj_from = 0; adj_to = 1 + namelen + sizeof dirpnt; goto write_dir_blocks; } if (direfblk >= volex -> homeblock.clusterfactor * 8) { // check for hefty sized directory sts = lookup_file (dirfile, namelen - 1, fname, &dirvbn, &dummy_fileid, NULL, iopex); // if so, find insertion spot quickly if ((sts != OZ_SUCCESS) && (sts != OZ_NOSUCHFILE)) goto cleanup; // abort if some bad error dirvbn = (dirvbn - 1) / volex -> homeblock.clusterfactor; // get zero-based bucket number if (dirvbn > 0) -- dirvbn; // back up in case name fits in prior bucket dirvbn = (dirvbn * volex -> homeblock.clusterfactor) + 1; // get one-based block number } prev_cmp = -1; /* say prev name was .lt. new name */ prev_vbn = 0; /* it wasn't really in any block */ prev_i = dirblocksize; /* say the prev block was completely full */ /* j and k aren't really valid when prev_cmp .lt. 0 */ while (1) { /* loop through the whole directory */ /* Read in block from directory file */ sts = oz_dev_vdfs_readvirtblock (dirfile, dirvbn, 0, dirblocksize, dirblockbuff, iopex, 0); /* read a whole dirblockbuff */ if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: error %u reading directory block %u\n", sts, dirvbn); goto cleanup; } /* Validate existing directory block contents */ if (!validirbuf (dirblocksize, dirblocksize, dirblockbuff, iopex)) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: existing directory block %u corrupt\n", dirvbn); sts = OZ_DIRCORRUPT; goto cleanup; } /* Scan through the block */ lastname[0] = 0; for (i = 0; i < dirblocksize;) { /* scan the dirblockbuff */ if (dirblockbuff[i] == 0) break; /* null filename string means end of block */ j = i + strlen (dirblockbuff + i) + 1; /* get index to dirpnt array for this filename */ memcpy (nextname + dirblockbuff[i] - 1, dirblockbuff + i + 1, j - i - 1); /* make up the complete filename string */ k = j; /* set dirpnt index to first dirpnt array element */ /* Compare name in directory block to name being entered */ cmp = strcmp (nextname, fname); /* If name in dir is .gt. name being entered, enter the new name just before this name */ if (cmp > 0) goto foundspotgt; /* If name in dir is .eq. name being entered, enter the new name */ /* in this one's array of versions by descending version number */ if (cmp == 0) { /* If no version was specified, make the version */ /* one greater than the current highest version. */ /* Make sure the incrementing doesn't overflow. */ if ((version == 0) || newversion) { /* see if they specified ;0 or equiv */ v = ((Dirpnt *)(dirblockbuff + j))[0].version; /* set up version = lastest existing version + 1 */ if (dirpnt[0].version <= v) dirpnt[0].version = v + 1; /* ... unless caller supplied a higher number */ if (dirpnt[0].version != 0) goto foundspoteq; /* if it didn't overflow, enter it */ sts = OZ_BADFILEVER; /* bad version number if overflow */ goto cleanup; } /* A specific version was specified, scan through the array until */ /* we find one that is lower than what was specified. If we don't */ /* find one in this array, we look at the next spec in the directory. */ /* If we find an equal one, that is an error. */ while ((k < dirblocksize) && ((v = ((Dirpnt *)(dirblockbuff + k)) -> version) != 0)) { /* scan version array until we hit version 0 */ if (v < version) goto foundspoteq; /* stop if dirpnt version .lt. new file's version */ /* (so we enter the new file just before it) */ if (v == version) { /* if exact match, error */ sts = OZ_FILEALREADYEXISTS; goto cleanup; } k += sizeof (Dirpnt); /* dirpnt version .gt. new file version, check out next dirpnt */ } /* We didn't find a lower entry, save pointer to end of this */ /* array. If the name in the next entry in the directory is */ /* .eq. the name being entered, we continue scanning its */ /* version array. If it is .gt. name being entered, then we */ /* want to insert the new one at the spot being saved here. */ prev_cmp = 0; prev_i = i; prev_j = j; prev_k = k; prev_vbn = dirvbn; } /* Skip 'i' forward to next filename in the directory block */ i = k; do i += sizeof (Dirpnt); while ((i < dirblocksize) && (((Dirpnt *)(dirblockbuff + i))[-1].version != 0)); /* Save the last name we compared */ strcpy (lastname, nextname); } /* If prev thing in this directory block was .lt. name being entered, */ /* save index at end of block where we might insert new name (provided */ /* the first name of the next block is .gt. name being entered). */ if (cmp < 0) { prev_cmp = -1; prev_i = i; prev_vbn = dirvbn; } /* If we hit end of directory, pretend we just found */ /* a name that is .gt. the name being entered. */ if (dirvbn + volex -> homeblock.clusterfactor == direfblk) goto foundspotgt; /* if this is the prev block, insert here */ /* Otherwise, continue on with the next block in the directory */ dirvbn += volex -> homeblock.clusterfactor; /* otherwise, go on to read next block */ } /*****************************************************************/ /* Found name in directory greater than name to be entered */ /* dirvbn = virtual block number to enter it */ /* i = offset in dirvbn where .gt. filename string starts */ /* prev_cmp == 0 : prev entry had matching filename */ /* < 0 : prev entry's filename didn't match either */ /* prev_i = start of prev entry */ /* prev_j = start of prev entry's dirpnts */ /* prev_k = end of prev entry's dirpnts */ /* prev_vbn = prev entry's vbn */ /*****************************************************************/ foundspotgt: #if 000 oz_knl_printk ("oz_dev_dfs enter_file*: fname %s\n", fname); oz_knl_printk (" dirvbn %u\n", dirvbn); oz_knl_printk (" i %d\n", i); oz_knl_printk (" prev_cmp %d\n", prev_cmp); oz_knl_printk (" prev_i %d\n", prev_i); oz_knl_printk (" prev_j %d\n", prev_j); oz_knl_printk (" prev_k %d\n", prev_k); oz_knl_printk (" prev_vbn %u\n", prev_vbn); oz_knl_printk (" lastname %s\n", lastname); oz_knl_printk (" nextname %s\n", nextname); oz_knl_dumpmem2 (dirblocksize, dirblockbuff, dirvbn << 16); #endif /* Maybe this is really the oldest (lowest numbered) version of the prev entry */ /* prev_cmp .eq. 0 indicates that new name matched prev directory entry */ if (prev_cmp == 0) { casx = 2; i = prev_i; /* offset of name in prev directory block */ j = prev_j; /* offset of version array in prev directory block */ k = prev_k; /* index in version array to insert new entry */ dirvbn = prev_vbn; /* previous directory block number */ sts = oz_dev_vdfs_readvirtblock (dirfile, dirvbn, 0, dirblocksize, dirblockbuff, iopex, 0); if (sts == OZ_SUCCESS) goto foundspoteq; oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: error %u reading directory block %u\n", sts, dirvbn); goto cleanup; } /* It is a completely new name - make sure we have a version number and make sure result doesn't overflow filename string */ if (dirpnt[0].version == 0) dirpnt[0].version = 1; oz_hw_itoa (dirpnt[0].version, sizeof vstr, vstr); sts = OZ_FILENAMETOOLONG; if (namelen + strlen (vstr) + 1 >= FILENAME_MAX) goto cleanup; /* Find out how much of it matches the last and next entries */ for (match_last = 0;; match_last ++) if (fname[match_last] != lastname[match_last]) break; for (match_next = 0;; match_next ++) if (fname[match_next] != nextname[match_next]) break; /* If at offset 0 in this block, maybe it will fit on end of previous block */ /* This keeps us from unnecessarily extending directory when adding entries on the end */ /* i .eq. 0 means we are trying to insert at beginning of this block */ /* prev_i is how much room is already used up in previous block */ if ((i == 0) && (prev_i + namelen - match_last + 1 + sizeof dirpnt <= dirblocksize)) { casx = 3; dirvbn = prev_vbn; sts = oz_dev_vdfs_readvirtblock (dirfile, dirvbn, 0, dirblocksize, dirblockbuff, iopex, 0); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: error %u reading directory block %u\n", sts, dirvbn); goto cleanup; } i = prev_i; } /* Set 'j' to number of bytes in current directory block that are used */ for (j = i; (j < dirblocksize) && (dirblockbuff[j] != 0);) { j += strlen (dirblockbuff + j) + 1; do j += sizeof (Dirpnt); while ((j < dirblocksize) && (((Dirpnt *)(dirblockbuff + j))[-1].version != 0)); } /* If there is no next name in the block, insert at end of block */ if (j == i) goto ins_at_eob; /* See if there is enough room in current block to insert it */ if (j + namelen - match_last + 1 + sizeof dirpnt + dirblockbuff[i] - 1 - match_next <= dirblocksize) { casx = 4; extendsize = 0; /* ok, no extension required */ memmove (dirblockbuff + i + 1 + namelen - match_last + sizeof dirpnt + 1, dirblockbuff + i + 1 + match_next + 1 - dirblockbuff[i], j - i - (1 + match_next + 1 - dirblockbuff[i])); dirblockbuff[i] = match_last + 1; memcpy (dirblockbuff + i + 1, fname + match_last, namelen - match_last); memcpy (dirblockbuff + i + 1 + namelen - match_last, dirpnt, sizeof dirpnt); dirblockbuff[i+1+namelen-match_last+sizeof dirpnt] = match_next + 1; adj_from = i; /* shift any directory reads on this block */ adj_to = i + 1 + namelen - match_last + sizeof dirpnt; /* ... so they point to the same name */ } /* See if name will fit in old block with just the stuff that comes before it */ /* Stuff that comes after it gets placed in a new block */ else if (i + 1 + namelen - match_last + sizeof dirpnt <= dirblocksize) { casx = 5; extendsize = 1; /* ok, extend to create second block */ dirblockbuff2[0] = 1; /* put part that follows in second block */ memcpy (dirblockbuff2 + 1, nextname, dirblockbuff[i] - 1); memcpy (dirblockbuff2 + 1 + dirblockbuff[i] - 1, dirblockbuff + i + 1, j - i - 1); memset (dirblockbuff2 + 1 + dirblockbuff[i] - 1 + j - i - 1, 0, /* zero fill second block */ dirblocksize - (1 + dirblockbuff[i] - 1 + j - i - 1)); dirblockbuff[i] = match_last + 1; /* copy in new name */ memcpy (dirblockbuff + i + 1, fname + match_last, namelen - match_last); memcpy (dirblockbuff + i + 1 + namelen - match_last, dirpnt, sizeof dirpnt); /* copy in new dirpnt followed by a null dirpnt */ k = i + 1 + namelen - match_last + sizeof dirpnt; /* get index past the new entry */ if (k < j) memset (dirblockbuff + k, 0, j - k); /* zero fill over the old stuff that was there */ adj_from = i; /* shift any directory reads on old block to new block */ adj_to = dirblocksize; } /* If not, see if it will fit in a new block with the stuff that comes after it */ else if (1 + namelen + sizeof dirpnt + 1 + j - i - 1 - (match_next + 1 - dirblockbuff[i]) <= dirblocksize) { casx = 6; extendsize = 1; /* ok, extend to create second block */ dirblockbuff2[0] = 1; memcpy (dirblockbuff2 + 1, fname, namelen); memcpy (dirblockbuff2 + 1 + namelen, dirpnt, sizeof dirpnt); dirblockbuff2[1+namelen+sizeof dirpnt] = match_next + 1; memcpy (dirblockbuff2 + 1 + namelen + sizeof dirpnt + 1, dirblockbuff + i + 1 + match_next + 1 - dirblockbuff[i], j - (i + 1 + match_next + 1 - dirblockbuff[i])); memset (dirblockbuff2 + 1 + namelen + sizeof dirpnt + j + 1 - (i + 1 + match_next + 1 - dirblockbuff[i]), 0, dirblocksize - (1 + namelen + sizeof dirpnt + j + 1 - (i + 1 + match_next + 1 - dirblockbuff[i]))); memset (dirblockbuff + i, 0, j - i); /* wipe stuff from first block */ adj_from = i; /* shift any directory reads on old block to new block */ adj_to = dirblocksize + 1 + namelen + sizeof dirpnt; } /* Put it in a block all by itself */ else { casx = 7; extendsize = 2; /* extend to create second and third blocks */ dirblockbuff3[0] = 1; /* copy part that follows to third block */ memcpy (dirblockbuff3 + 1, nextname, dirblockbuff[i] - 1); memcpy (dirblockbuff3 + 1 + dirblockbuff[i] - 1, dirblockbuff + i + 1, j - i - 1); memset (dirblockbuff3 + 1 + dirblockbuff[i] - 1 + j - i - 1, 0, dirblocksize - (1 + dirblockbuff[i] - 1 + j - i - 1)); dirblockbuff2[0] = 1; /* put new name in second block by itself */ memcpy (dirblockbuff2 + 1, fname, namelen); memcpy (dirblockbuff2 + 1 + namelen, dirpnt, sizeof dirpnt); memset (dirblockbuff2 + 1 + namelen + sizeof dirpnt, 0, dirblocksize - (1 + namelen + sizeof dirpnt)); memset (dirblockbuff + i, 0, j - i); /* clear junk out of first block */ adj_from = i; /* shift any directory reads on old block to second new block */ adj_to = 2 * dirblocksize; } goto write_dir_blocks_ex; /* Name is being inserted at the end of the block */ /* i = offset to end of bucket */ ins_at_eob: /* Maybe it just fits at the end of the block */ if (i + 1 + namelen - match_last + sizeof dirpnt <= dirblocksize) { casx = 20; extendsize = 0; dirblockbuff[i] = match_last + 1; memcpy (dirblockbuff + i + 1, fname + match_last, namelen - match_last); memcpy (dirblockbuff + i + 1 + namelen - match_last, dirpnt, sizeof dirpnt); adj_from = i; adj_to = i + namelen - match_last + sizeof dirpnt; } /* Put it in its own block */ else { casx = 21; extendsize = 1; dirblockbuff2[0] = 1; memcpy (dirblockbuff2 + 1, fname, namelen); memcpy (dirblockbuff2 + 1 + namelen, dirpnt, sizeof dirpnt); memset (dirblockbuff2 + 1 + namelen + sizeof dirpnt, 0, dirblocksize - (1 + namelen + sizeof dirpnt)); adj_from = i; adj_to = dirblocksize + 1 + namelen + sizeof dirpnt; } goto write_dir_blocks_ex; /********************************************************************/ /* The filename is already in the directory with different versions */ /* dirvbn = virtual block number to enter it */ /* i = offset in dirvbn where .eq. filename string starts */ /* j = offset in dirvbn where its first pointer is */ /* k = index of pointers where next lower version is */ /* prev_cmp == 0 : prev entry had matching filename */ /* < 0 : prev entry's filename didn't match */ /* prev_i = start of prev entry */ /* prev_j = start of prev entry's dirpnts */ /* prev_k = index of end of prev entry's dirpnts (the 0 entry) */ /* prev_vbn = prev entry's vbn */ /********************************************************************/ foundspoteq: oz_hw_itoa (dirpnt[0].version, sizeof vstr, vstr); sts = OZ_FILENAMETOOLONG; if (namelen + strlen (vstr) + 1 >= FILENAME_MAX) goto cleanup; /* If there is room in prev_vbn's block (and it can go there) do so */ /* prev_cmp .eq. 0 means the previous entry matched new entry's name */ /* k .eq. j means we can insert new entry at beginning of this entry */ /* implying it can be entered at end of previous entry */ /* i .eq. 0 means this is at beginning of block, so prev must have */ /* been at end of its block */ if ((prev_cmp == 0) && (k == j) && (i == 0) && (prev_k + sizeof dirpnt <= dirblocksize)) { casx = 8; dirvbn = prev_vbn; sts = oz_dev_vdfs_readvirtblock (dirfile, dirvbn, 0, dirblocksize, dirblockbuff, iopex, 0); if (sts != OZ_SUCCESS) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: error %u reading directory block %u\n", sts, dirvbn); goto cleanup; } memcpy (dirblockbuff + prev_k, dirpnt, sizeof dirpnt); adj_from = prev_k; adj_to = prev_k + sizeof dirpnt; extendsize = 0; goto write_dir_blocks_ex; } /* Get offset to next name entry in directory block */ for (n = k + sizeof (Dirpnt); n < dirblocksize; n += sizeof (Dirpnt)) { if (((Dirpnt *)(dirblockbuff + n))[-1].version == 0) break; } /* Get total length of all stuff in directory block */ for (l = n; (l < dirblocksize) && (dirblockbuff[l] != 0);) { l += strlen (dirblockbuff + l) + 1; do l += sizeof (Dirpnt); while ((l < dirblocksize) && (((Dirpnt *)(dirblockbuff + l))[-1].version != 0)); } /* Assuming we are entering 'mno;5' : */ /* def 5 4 0 mno 8 7 6 4 3 0 pqr 2 0 */ /* i j k n l */ /* See if there is enough room in current block to insert new pointer */ if (l + sizeof dirpnt[0] <= dirblocksize) { casx = 9; memmove (dirblockbuff + k + sizeof dirpnt[0], dirblockbuff + k, l - k); /* ok, move rest of stuff to make room for dirpnt[0] */ memcpy (dirblockbuff + k, dirpnt, sizeof dirpnt[0]); /* copy in dirpnt[0] which has new file's version and file-id */ extendsize = 0; /* no extension required */ adj_from = k; // get existing offset of mno;4 adj_to = k + sizeof dirpnt[0]; // new offset of mno;4 } /* See if version will fit in old block with just the stuff that comes before it */ else if (k + sizeof dirpnt <= dirblocksize) { extendsize = 1; /* extend to create second block */ if (((Dirpnt *)(dirblockbuff + k))[0].version == 0) { /* see if being inserted at end of dirpnt array */ casx = 10; dirblockbuff2[0] = 1; memcpy (dirblockbuff2 + 1, fname, dirblockbuff[n] - 1); memcpy (dirblockbuff2 + 1 + dirblockbuff[n] - 1, dirblockbuff + n + 1, l - n - 1); memset (dirblockbuff2 + 1 + dirblockbuff[n] - 1 + l - n - 1, 0, dirblocksize - (1 + dirblockbuff[n] - 1 + l - n - 1)); memcpy (dirblockbuff + k, dirpnt, sizeof dirpnt); memset (dirblockbuff + k + sizeof dirpnt, 0, l - k - sizeof dirpnt); adj_from = n; // old offset of pqr20 adj_to = dirblocksize; // new offset of pqr20 } else { casx = 11; dirblockbuff2[0] = 1; memcpy (dirblockbuff2 + 1, fname, namelen); memcpy (dirblockbuff2 + 1 + namelen, dirblockbuff + k, l - k); memset (dirblockbuff2 + 1 + namelen + l - k, 0, dirblocksize - (1 + namelen + l - k)); memcpy (dirblockbuff + k, dirpnt, sizeof dirpnt); memset (dirblockbuff + k + sizeof dirpnt, 0, l - k - sizeof dirpnt); adj_from = k; // old offset of 430 adj_to = dirblocksize + 1 + namelen; // new offset of 430 } } /* If not, see if it will fit in new block with the stuff that comes after it */ else if (namelen + sizeof dirpnt[0] + l - k <= dirblocksize) { extendsize = 1; /* ok, extend to create second block */ dirblockbuff2[0] = 1; memcpy (dirblockbuff2 + 1, fname, namelen); memcpy (dirblockbuff2 + 1 + namelen, dirpnt + 0, sizeof dirpnt[0]); memcpy (dirblockbuff2 + 1 + namelen + sizeof dirpnt[0], dirblockbuff + k, l - k); memset (dirblockbuff2 + 1 + namelen + sizeof dirpnt[0] + l - k, 0, dirblocksize - (1 + namelen + sizeof dirpnt[0] + l - k)); if (k == j) { /* see if new entry is the very newest */ casx = 12; memset (dirblockbuff + i, 0, l - i); /* if so, zero fill starting at filename in first block */ } else { casx = 13; memset (dirblockbuff + k, 0, l - k); /* if not, zero fill starting at lower version number in first block */ } adj_from = k; // old offset of mno;4 adj_to = dirblocksize + 1 + namelen + sizeof dirpnt[0]; // new offset of mno;4 } /* See if the name, with its new version and all old versions, can fit in a block by itself */ /* Hopefully, the conditions of i.eq.0 or n.eq.l were taken care of above so we don't waste space */ else if (1 + namelen + n - j <= dirblocksize) { casx = 14; extendsize = 2; /* ok, extend directory by two blocks */ dirblockbuff3[0] = 1; memcpy (dirblockbuff3 + 1, fname, dirblockbuff[n] - 1); memcpy (dirblockbuff3 + 1 + dirblockbuff[n] - 1, dirblockbuff + n, l - n); memset (dirblockbuff3 + 1 + dirblockbuff[n] - 1 + l - n, 0, dirblocksize - (1 + dirblockbuff[n] - 1 + l - n)); dirblockbuff2[0] = 1; memcpy (dirblockbuff2 + 1, fname, namelen); memcpy (dirblockbuff2 + 1 + namelen, dirblockbuff + j, k - j); memcpy (dirblockbuff2 + 1 + namelen + k - j, dirpnt + 0, sizeof dirpnt[0]); memcpy (dirblockbuff2 + 1 + namelen + k - j + sizeof dirpnt[0], dirblockbuff + k, n - k); memset (dirblockbuff2 + 1 + namelen + k - j + sizeof dirpnt[0] + n - k, 0, dirblocksize - (1 + namelen + k - j + sizeof dirpnt[0] + n - k)); memset (dirblockbuff + i, 0, l - i); adj_from = n; // old offset of pqr adj_to = 2 * dirblocksize; // new offset of pqr } /* See if the name with its new version and all higher versions can fit in a block */ else if (1 + namelen + k - j + sizeof dirpnt <= dirblocksize) { casx = 15; extendsize = 2; /* ok, extend directory by two blocks */ dirblockbuff3[0] = 1; memcpy (dirblockbuff3 + 1, fname, namelen); memcpy (dirblockbuff3 + 1 + namelen, dirblockbuff + k, l - k); memset (dirblockbuff3 + 1 + namelen + l - k, 0, dirblocksize - (1 + namelen + l - k)); dirblockbuff2[0] = 1; memcpy (dirblockbuff2 + 1, fname, namelen); memcpy (dirblockbuff2 + 1 + namelen, dirblockbuff + j, k - j); memcpy (dirblockbuff2 + 1 + namelen + k - j, dirpnt, sizeof dirpnt); memset (dirblockbuff2 + 1 + namelen + k - j + sizeof dirpnt, 0, dirblocksize - (1 + namelen + k - j + sizeof dirpnt)); memset (dirblockbuff + i, 0, l - i); /* wipe starting at filename from first block */ adj_from = k; // old offset of mno;4 adj_to = 2 * dirblocksize + 1 + namelen; // new offset of mno;4 } /* See if the name with its new version and all lower versions can fit in a block */ else if (1 + namelen + sizeof dirpnt[0] + n - k <= dirblocksize) { extendsize = 2; /* ok, extend directory by two blocks */ dirblockbuff3[0] = 1; memcpy (dirblockbuff3 + 1, fname, dirblockbuff[n] - 1); memcpy (dirblockbuff3 + 1 + dirblockbuff[n] - 1, dirblockbuff + n + 1, l - n - 1); memset (dirblockbuff3 + 1 + dirblockbuff[n] - 1 + l - n - 1, 0, dirblocksize - (1 + dirblockbuff[n] - 1 + l - n - 1)); dirblockbuff2[0] = 1; memcpy (dirblockbuff2 + 1, fname, namelen); memcpy (dirblockbuff2 + 1 + namelen, dirpnt + 0, sizeof dirpnt[0]); memcpy (dirblockbuff2 + 1 + namelen + sizeof dirpnt[0], dirblockbuff + k, n - k); memset (dirblockbuff2 + 1 + namelen + sizeof dirpnt[0] + n - k, 0, dirblocksize - (1 + namelen + sizeof dirpnt[0] + n - k)); if (k == j) { /* see if the new version is the very latest */ casx = 16; memset (dirblockbuff + i, 0, l - i); /* if so, wipe the filename from the first block */ } else { casx = 17; memset (dirblockbuff + k, 0, l - k); /* else, just wipe the lower versions from the first block */ } adj_from = n; // old offset of pqr adj_to = 2 * dirblocksize; // new offset of pqr } /* Put it in a block all by itself */ else { extendsize = 2; /* extend directory by two blocks */ dirblockbuff3[0] = 1; memcpy (dirblockbuff3 + 1, fname, namelen); memcpy (dirblockbuff3 + 1 + namelen, dirblockbuff + k, l - k); memset (dirblockbuff3 + 1 + namelen + l - k, 0, dirblocksize - (1 + namelen + l - k)); dirblockbuff2[0] = 1; memcpy (dirblockbuff2 + 1, fname, namelen); memcpy (dirblockbuff2 + 1 + namelen, dirpnt, sizeof dirpnt); memset (dirblockbuff2 + 1 + namelen + sizeof dirpnt, 0, dirblocksize - (1 + namelen + sizeof dirpnt)); if (k == j) { /* see if the new version is the very latest */ casx = 18; memset (dirblockbuff + i, 0, l - i); /* if so, wipe the filename from the first block */ } else { casx = 19; memset (dirblockbuff + k, 0, l - k); /* else, just wipe the lower versions from the first block */ } adj_from = k; // old offset of mno;4 adj_to = 2 * dirblocksize + 1 + namelen; // new offset of mno;4 } /***************************************/ /* Write the modified block(s) to disk */ /***************************************/ write_dir_blocks_ex: extendvbn = dirvbn + volex -> homeblock.clusterfactor; // determine block to extend at writesize = (extendsize + 1) * dirblocksize; // determine how much to write write_dir_blocks: if (adj_from >= adj_to) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: adj_from %u >= adj_to %u (case %d)\n", adj_from, adj_to, casx); oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: entering '%s' into '%s' at vbn %u\n", name, dirname, dirvbn); sts = OZ_DIRCORRUPT; goto cleanup; } /* Validate new dir block contents */ #if 000 oz_knl_printk ("oz_dev_dfs enter_file*: case %d, adj_from %u, _to %u\n", casx, adj_from, adj_to); for (i = 0; i < writesize; i += dirblocksize) { oz_knl_dumpmem2 (dirblocksize, dirblockbuff + i, (dirvbn + (i / dirblocksize)) << 16); } #endif if (!validirbuf (writesize, dirblocksize, dirblockbuff, iopex)) { oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: new directory blocks corrupt (case %d)\n", casx); oz_dev_vdfs_printk (iopex, "oz_dev_dfs enter_file: entering '%s' into '%s' at vbn %u\n", name, dirname, dirvbn); sts = OZ_DIRCORRUPT; goto cleanup; } /* Maybe we need to extend */ oz_dev_vdfs_lock_dirfile_for_write (dirfile); /* block shortcuts from reading directory */ if (extendsize != 0) { extendsize *= volex -> homeblock.clusterfactor; /* get number of blocks to insert */ sts = insert_bloc