/* File: disknew.c * Author: Jean Thierry-Mieg (mieg@mrc-lmb.cam.ac.uk) * Copyright (C) J Thierry-Mieg and R Durbin, 1992 * ------------------------------------------------------------------- * Acedb is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt * ------------------------------------------------------------------- * This file is part of the ACEDB genome database package, written by * Richard Durbin (MRC LMB, UK) rd@mrc-lmb.cam.ac.uk, and * Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr * * Description: * Exported functions: * HISTORY: * Last edited: Apr 9 14:38 2003 (edgrif) * * Sep 21 12:04 1999 (edgrif): Add sync write mode code for disk writes. * * May 10 10:22 1999 (edgrif): Altered DataBaseReadDefinition for Jean. * * Apr 16 16:08 1999 (fw): fixed very subtle bug where totalNbpartions * was wrong, because the global variable wasn't reset * probably cause for SANgc02258 and other diskextend bugs * * Oct 15 11:33 1998 (fw): included the output from * messSysErrorText in messcrash texts * resulting from disk operations or other OS calls * * * * * * * * - extern uid_t euid, ruid moved to session.h * * Jun 3 12:56 1996 (rd) * * Apr 11 - 94 : multi-partitions version. (pierre Simondon) * * Jan 22 10:33 1992 (mieg): Modify bat and session obj en place. * Created: Wed Jan 22 10:33:50 1992 (mieg) * CVS info: $Id: disknew.c,v 1.88 2003/04/09 14:06:32 edgrif Exp $ *------------------------------------------------------------------- */ #ifdef ACEDB5 void compilersDoNotLike (void *emptyFiles) {return ; } #else /* ACEDB 4 only */ /***************************************************************/ /***************************************************************/ /** File disksubs.c : **/ /** Handles the disk of the ACeDB program. **/ /***************************************************************/ /***************************************************************/ /* */ /* Six routines are public : */ /* avail,prepare, alloc/free, blockread/blockwrite. */ /* */ /* A single long sequential file large enough to hold */ /* DISKSIZE blocks is "prepared" once and for all. */ /* */ /* "diskalloc", using the static BAT table, returns a DISK */ /* value which is the number of the first free block following */ /* This number is then stored in the lexique as lxi->diskaddr*/ /* and used, after multiplication by BLOC_SIZE as an offset */ /* in "diskblockread/write", which use fseek. */ /* "diskfree" returns a bloc to the BAT */ /* diskavail gives disk statistics. */ /* */ /* The BAT table handling is private. */ /* */ /***************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for cacheSaveAll() */ #include #if !defined(MACINTOSH) #include #else #include "macfilemgrsubs.h" #endif extern void swapABlock(BP bp) ; extern void swapBBlock(BP bp) ; static void diskBATread(void); static void diskBATwrite(void); static void diskBATcompress(Array plusA, Array minusA) ; static void diskExtend(BOOL zeroing) ; static BOOL isBat(DISK d) ; static int PartitionSize(char *partitionName); /* in blocks */ /* base size in bytes and check accessibility to the data base : */ static int PhysicalBaseSize(); /* relative address of a block in a partition (in bytes) */ static long PartitionAddress(DISK d, int *partition); /* full pathname of a partition : */ static char *PartitionName(int numPartition); /* close all open partitions of the base : */ static void CloseDataBase(); /* creation and update database map : */ static BOOL DataBaseReadDefinition(BOOL new, BOOL skip); static BOOL DataBaseUpdateMap(void); static void readPartitionMap (void); static char *diskGetErrorText (char,char); int INITIALDISKSIZE = 512 ; /* number of blocks on the disk, * called from blocksubs */ static Array batArray = 0 ; /*block allocation table */ static Array plusArray = 0 ; /* new blocks in this session */ static Array minusArray = 0 ; /* blocked freed by this sesion */ static BOOL batIsModified = FALSE ; static int blocksRead = 0, blocksWritten = 0; /* syncWrite - indicates whether acedb should open database files for */ /* writing using the O_SYNC flag. This should ensure that acedb */ /* will be able to detect timeouts for writes (applies to NFS */ /* mounted databases especially). */ static BOOL syncWrite = FALSE ; /* for debug purpose only : */ static int ps_debug = 0 ; static int ps_debug1 = 0 ; /* Database splited into partitions */ /* partitions description must reside on a unique disk block : */ /* ATTENTION : * only a multiple of INITIALDISKSIZE blocks can be used in each partition * See wh/disk.h , wspec/cachesize.wrm (DISK option) */ /* default maximum DataBase size in blocks */ #define MAX_DATABASE_SIZE 100 * 1024 #define UNIX_FILE_SYSTEM 1 #define RAW_FILE_SYSTEM 2 #define LG_NAME 128 /* Partition: describes a database partition (i.e. one of the blockNN.wrm files) */ typedef struct { int type; /* unix file system, raw, distant.. */ char hostname[LG_NAME]; /* machine on which is the partition */ char fileSystem[LG_NAME]; /* partition file system or "ACEDN if relative */ char fileName[LG_NAME]; /* file name or "NONE" if not a file system. */ int currentBlocks; /* current size in blocks of this partition. */ int maxBlocks; /* max. size possible in blocks for this partition. */ int debut ; /* total blocks in database up to the end of _previous_ partition.*/ int fin ; /* total blocks in database up to end of this partition. */ } PARTITION; static int lastPartition_L , /* (i.e. number of active partitions) */ totalNbPartitions_L; /* number of partitions */ static Array pTable = 0 ; /* partition acces handles : (currently only "fd") . */ /* current read/write cursor position in partitions */ static Array rPos = 0, wPos = 0 , fdRead = 0 , fdWrite = 0 ; /*******************************************************************/ /* Partitions configuration : must be called on first session : */ /*******************************************************************/ /* read database definition and create static description. This description */ /* will be set for futher retrieval in the base itself */ BOOL dataBaseCreate() { VoidRoutine previousCrashRoutine = messcrashroutine ; messcrashroutine = simpleCrashRoutine ; if (ps_debug) fprintf(stderr, "DataBaseCreate\n"); pTable = arrayReCreate (pTable, 2, PARTITION) ; rPos = arrayReCreate (rPos, 2, myoff_t) ; wPos = arrayReCreate (wPos, 2, myoff_t) ; fdRead = arrayReCreate (fdRead, 2, int) ; fdWrite = arrayReCreate (fdWrite, 2, int) ; if (!DataBaseReadDefinition(TRUE, TRUE)) messcrash (" DataBase creation : no description file found !!!\n"); DataBaseUpdateMap() ; dataBaseAccessInit(); messcrashroutine = previousCrashRoutine ; return TRUE; } /*******************************************************************/ /* must be called at session open. */ /*******************************************************************/ void dataBaseAccessInit() { int i ; PARTITION *pp ; fdRead = arrayReCreate (fdRead, 2, int) ; fdWrite = arrayReCreate (fdWrite, 2, int) ; pTable = arrayReCreate (pTable, 2, PARTITION) ; /* get partitions from database map-file */ readPartitionMap (); /* control Database access : */ PhysicalBaseSize () ; for (i = 0; i <= lastPartition_L; i++) { char *nam = PartitionName(i); pp = arrayp(pTable, i, PARTITION) ; pp->currentBlocks = PartitionSize(nam); messfree(nam); if (ps_debug) fprintf(stderr, "Partition %d:\n" "\ttype %d, f system %s, file %s\n" "\tcurrentBlocks: %d, maxBlocks: %d, debut: %d, fin: %d\n", i, pp->type, pp->fileSystem, pp->fileName, pp->currentBlocks, pp->maxBlocks, pp->debut, pp->fin); } /* all partitions closed : */ fdRead = arrayReCreate (fdRead, 2, int) ; fdWrite = arrayReCreate (fdWrite, 2, int) ; rPos = arrayReCreate (rPos, 2, myoff_t) ; wPos = arrayReCreate (wPos, 2, myoff_t) ; return; } /* dataBaseAccessInit */ /**************************************************************/ /* On first call during a given session, any modified item is moved by blocksave to a new disk position, this alters the lex and BAT but the change is not registered on disk, hence the need for a sequence of write */ BOOL saveAll(void) { extern BOOL blockCheck (void) ; int i = 0, j = 3 ; BOOL modif = FALSE ; VoidRoutine previousCrashRoutine = messcrashroutine ; /* ici, in regarde si une session est ouverte */ if(!isWriteAccess()) return FALSE ; messcrashroutine = simpleCrashRoutine; if (!blockCheck()) messcrash ("Sorry - bad cache structure - in saveAll") ; messStatus ("Saving..."); while(j--) { int oldBlocks = blocksWritten; if(batIsModified) { j++ ; /* Three additional loops for security ! */ modif = TRUE ; } diskBATwrite() ; cacheSaveAll(); /* Secondary Cache */ bIndexSave() ; lexSave () ; /* Lexiques go to cache */ blockSave(TRUE) ; /* flush the cache */ if(modif) sessionRegister() ; if (ps_debug) printf("saveAll: pass %d: wrote %d blocks\n", i++, blocksWritten - oldBlocks); } messcrashroutine = previousCrashRoutine ; return modif ; } /* saveAll */ /**************************************************************/ /* If no explicit call is made, when the first cache is full, blockalloc flushes a block, calling diskwrite calling BATread calling diskalloc with a resulting fatal knot in the global used blockend pointer */ void diskPrepare(void) { if (thisSession.session > 1 ) /* Except during session 0/1 */ diskBATread() ; /* la BAT existe deja : */ else { batArray = arrayCreate(100,unsigned char) ; plusArray = arrayCreate(100,unsigned char) ; minusArray = arrayCreate(100,unsigned char) ; /* select first current partition : */ lastPartition_L = 0; /* reset first partition file : */ diskExtend(TRUE) ; } } /**************************************************************/ /* query extends the size of the main database file */ static void diskExtend(BOOL zeroing) { #if defined(MACINTOSH) int blockfile; #else FILE * blockfile; #endif int i ; long dd, newdd; /*, dphysical ;*/ int physicalBaseSize; int blockExtend; unsigned int un = 1; unsigned int _64_b = 64 * BLOC_SIZE; void *emptybp = messalloc((int) _64_b); /* zeroing implied */ BOOL newPartition; PARTITION *pp = arrayp(pTable, lastPartition_L, PARTITION) ; VoidRoutine previousCrashRoutine = messcrashroutine ; char *attempt_text, *pnam; messcrashroutine = simpleCrashRoutine ; /* il faut une session ouverte */ if(!isWriteAccess()) messcrash("diskExtend() called without Write Access") ; chrono("diskExtend") ; /* l'extension porte sur la derniere partition : */ /* on commence par une sequence solide : fermeture systematique de */ /* toutes les partitions en cas d'extension : */ CloseDataBase(); blockExtend = INITIALDISKSIZE - (INITIALDISKSIZE % 64); if (zeroing) { dd = 0 ; newdd = blockExtend; newPartition = TRUE; } else { if(!batArray) messcrash("No batArray in diskextend(FALSE)") ; dd = 8 * arrayMax(batArray) ; if (ps_debug) fprintf(stderr, "diskExtend : current blocks number in bat = %ld\n",dd); /* compute new size of the base */ newdd = dd + blockExtend; physicalBaseSize = PhysicalBaseSize() ; if (physicalBaseSize < dd) messcrash("Diskextend found a physical size shorter than expected"); /* Add a new partition if the new size would overflow the current one. */ if (newdd > pp->debut + pp->maxBlocks) { lastPartition_L++ ; if (lastPartition_L >= totalNbPartitions_L) /* we need a partition which isn't yet available * in pTable, so we need to restart and make sure * database.wrm is bigger next time */ messExit("Can't extend database. " "You need to add partitions.\n" "Please edit wpsec/database.wrm"); pp = arrayp(pTable, lastPartition_L, PARTITION) ; pp->debut = dd ; pnam = PartitionName(lastPartition_L); messdump ("Disk extend: Creating partition %d: %s", lastPartition_L, pnam) ; messfree(pnam); /* create new partition : * go to the end of the partition, * extend it with the new extend size * (initialize by chunks of 64 blocks) */ newPartition = TRUE; } else newPartition = FALSE; } pnam = PartitionName(lastPartition_L); if (!newPartition) { attempt_text = "open (for appending)"; #if defined(MACINTOSH) blockfile = FMfopen( (unsigned char*) pnam, (unsigned char*) "ab"); #else seteuid (euid); blockfile = fopen(pnam, "ab"); #endif } else { attempt_text = "open (for writing)"; #if defined(MACINTOSH) blockfile = FMfopen( (unsigned char*) pnam, (unsigned char*) "wb") ; #else seteuid (euid); blockfile = fopen(pnam, "wb") ; #endif pp->currentBlocks = 0; pp->fin = 0 ; } if (!blockfile) { messExit ("Error occured when trying to %s partition file %s (%s)\n" "%s" "I will have to exit now ...", attempt_text, pnam, messSysErrorText(), diskGetErrorText('o','w')) ; } messfree(pnam); i = (63 + blockExtend)/64 ; #if defined(MACINTOSH) if (i > 0) { int writeLength ; writeLength = FMPBwrite(blockfile, 0, fsFromLEOF, emptybp, _64_b * i ) ; if ( writeLength != ( _64_b * i ) ) messExit ("Error occured when writing to disk (%s)\n" "%s" "I will have to exit now ...", messSysErrorText(), diskGetErrorText('a','w')); } FMfilclose ( blockfile ) ; #else /* UNIX */ if (i > 0) { fseek(blockfile, 0L, SEEK_END) ; while (i--) if (fwrite (emptybp, _64_b, un, blockfile) != un) { messExit ("Error occured writing to disk (%s)\n" "%s" "I will have to exit now ...", messSysErrorText(), diskGetErrorText('a','w')); } } fclose (blockfile) ; seteuid(ruid); #endif /* UNIX */ pp->currentBlocks += blockExtend; if ( pp->currentBlocks > 50000) invokeDebugger() ; pp->fin += blockExtend; /* write new database map-file, because some partition-records * may have changed if new partitions have been created */ DataBaseUpdateMap () ; messdump ("Now %ld blocks, %d in partition %s", newdd, pp->currentBlocks , pp->fileName) ; /* update bats : */ for(i= dd/8; iaddr ? KEY2LEX(__Global_Bat)->addr->p->h.session : 0); */ else messcrash("no bat self reference for address %d",j); messcrashroutine = previousCrashRoutine ; return; } /* diskBATread */ /********************************************/ static void diskBATwrite(void) { KEY kPlus, kMinus ; if (!batArray || ! batIsModified) return ; batIsModified = FALSE ; /* must come before actual write */ if(ps_debug) printf("\nWriting BAT, address %d", KEY2LEX(__Global_Bat)->dlx.dk) ; lexaddkey(messprintf("p-%d",thisSession.session),&kPlus,_VBat) ; lexaddkey(messprintf("m-%d",thisSession.session),&kMinus,_VBat) ; arrayStore(__Global_Bat, batArray,"c") ; arrayStore(kPlus, plusArray,"c") ; arrayStore(kMinus, minusArray,"c") ; return; } /* diskBATwrite */ /********************************************/ /* compress block which have been allocated and freed during the same session, i.e. necessary after a batFusion */ static void diskBATcompress(Array plusA, Array minusA) { register int i; register unsigned char *plus, *minus, *bat , c ; VoidRoutine previousCrashRoutine = messcrashroutine ; messcrashroutine = simpleCrashRoutine ; bat = arrp(batArray,0,unsigned char) ; plus = arrp(plusA,0,unsigned char) ; minus = arrp(minusA,0,unsigned char) ; if (arrayMax(batArray) < arrayMax(plusA)) messcrash("bat size inconsistency in diskBATcompress") ; i = arrayMax(plusA) ; while(i--) { c = *minus & *plus ; /* reset c bits to zero */ if(c) { batIsModified = TRUE ; if ( (*bat & c) != c ) messcrash("diskBatCompress releasing a non set block %c %c", *bat , c ) ; *bat &= ~c ; /* tilda NOT c) */ *plus &= ~c ; /* tilda NOT c) */ *minus &= ~c ; /* tilda NOT c) */ } bat++ ; plus++ ; minus++ ; } messcrashroutine = previousCrashRoutine ; return; } /* diskBATcompress */ /********************************************/ static BOOL fuseBats(Array old, Array new) { register int i; register unsigned char *o , *n ; BOOL result = FALSE ; VoidRoutine previousCrashRoutine = messcrashroutine ; if( !arrayExists(old) || !arrayExists(new) ) return FALSE ; messcrashroutine = simpleCrashRoutine ; o = arrp(old,0,unsigned char) ; n = arrp(new,0,unsigned char) ; if (arrayMax(old) > arrayMax(new)) messcrash("Inconsistency in fuseBats") ; i = arrayMax(old) ; o--; n-- ; while(o++, n++, i--) if (*o) { if (*o & *n) messcrash("Duplication in fuseBats") ; result = TRUE ; *n |= *o ; } messcrashroutine = previousCrashRoutine ; return result ; } /* fuseBats */ /**************************************************************/ static int diskBatSet(Array a) ; BOOL diskFuseBats(KEY fPlus, KEY fMinus, KEY sPlus, KEY sMinus, int *nPlusp, int *nMinusp) { BOOL done = FALSE, modif = FALSE ; Array fPlusArray = 0, fMinusArray = 0, sPlusArray = 0, sMinusArray = 0; fPlusArray = arrayGet(fPlus,unsigned char,"c") ; fMinusArray = arrayGet(fMinus,unsigned char,"c") ; sPlusArray = arrayGet(sPlus,unsigned char,"c") ; sMinusArray = arrayGet(sMinus,unsigned char,"c") ; if (!sPlusArray || !sMinusArray || !fPlusArray || !fMinusArray) goto abort ; modif |= fuseBats(fPlusArray, sPlusArray) ; modif |= fuseBats(fMinusArray, sMinusArray) ; if(modif) diskBATcompress(sPlusArray, sMinusArray) ; *nPlusp = diskBatSet(sPlusArray) ; *nMinusp = diskBatSet(sMinusArray) ; arrayStore(sPlus, sPlusArray,"c") ; arrayStore(sMinus, sMinusArray,"c") ; arrayKill(fPlus) ; /* not valid if there were session branches */ arrayKill(fMinus) ; done = TRUE ; abort: arrayDestroy(sPlusArray) ; arrayDestroy(sMinusArray) ; arrayDestroy(fPlusArray) ; arrayDestroy(fMinusArray) ; return done ; } /**************************************************************/ void diskFuseOedipeBats(Array fPlusArray, Array fMinusArray) { BOOL modif = FALSE ; modif |= fuseBats(fPlusArray, plusArray) ; modif |= fuseBats(fMinusArray, minusArray) ; if(modif) diskBATcompress(plusArray, minusArray) ; } /********************************************/ /* Returns the number of set bits */ static int diskBatSet(Array a) { register unsigned char *cp ; register unsigned int i; register int j = arrayExists(a) ? arrayMax(a) : 0 , n = 0 ; if(!j) return 0 ; cp = arrp(a,0,unsigned char) ; while (j--) { if(!*cp) n+=8 ; else if(*cp != 0xff) for (i=1;i<256;i<<=1) if(~(*cp) & i) n++; cp++; } return 8*arrayMax(a) - n ; } /********************************************/ /*returns the number of used and free blocks*/ void diskavail(int *free,int *used, int *plus, int *minus, int *dr, int* dw) { if(!batArray) diskBATread(); *used = diskBatSet(batArray) ; *plus = diskBatSet(plusArray) ; *minus = diskBatSet(minusArray) ; *free = 8*arrayMax(batArray) - *used ; *dr = blocksRead ; *dw = blocksWritten ; if (getenv ("ACEDB_DEBUG")) messdump ("diskavail: used %d, plus %d, minus %d, free %d", *used, *plus, *minus, *free) ; } /********************************************/ /* The strategy is always to move blocks on disk when rewriting and never reuse a block during a given session. In this way, the former session certainly remains consistent There is however a very important exception to this rule, The session objects and the Bat arrays are not moved. The reason is that during session 8 you may be fusing session 3 and 4, then you modify obj session4 and its bats. By doing it en place you insure that it is written in a block allocated during session 4. Otherwise, if you eventually destroy session 8 before session 4, the bat of session 8 would be lost. */ void diskalloc(BP bp) { register unsigned int i ; /*unsigned because we use a right shift operator on i*/ register int j ; register unsigned char *cp , *top, *end , minus ; static DISK d = 0; if(bp->h.disk && ( bp->h.session == thisSession.session || class(bp->h.key) == _VBat || class(bp->h.key) == _VSession ) ) return; if(!batArray) diskBATread() ; /* We move the block on disk */ if (class(bp->h.key) != _VGlobal) /* Freeing the global class in unconsistent */ diskfree(bp) ; lao: j = arrayMax(batArray) ; /* must come after eventual BATread */ top = arrp(batArray,0,unsigned char) ; end = arrp(batArray,arrayMax(batArray) - 1 ,unsigned char) + 1 ; cp = arrp(batArray,(int) (d/8),unsigned char) ; if (cp >= end) cp = top ; while (j--) /* explore the whole BAT starting in the middle */ { if(*cp != 0xff) for (i=1;i<256;i<<=1) if(~(*cp) & i) /* free block on the global BAT */ { d = (DISK)(8*(cp - top)) ; minus = arr(minusArray,(int)(d/8),unsigned char) ; if(~minus & i) /* Not freed in this session */ { *cp |= (unsigned char)i ; arr(plusArray,(int)(d/8),unsigned char) |= i ; while(i>>=1) d++; bp->h.disk=d ; bp->h.session = thisSession.session ; batIsModified = TRUE ; return ; } else { messcrashroutine = simpleCrashRoutine ; messcrash ("disk alloc inconsistency") ; } } cp++; if (cp >= end) cp = top; } diskExtend(FALSE); goto lao ; } /* diskalloc */ /*************************************************/ /* do free only if allocated during this session */ /*************************************************/ void diskfree(BP bp) { register unsigned char i; /*unsigned because we use a right shift operator on i*/ register unsigned char *cp , *minus, *plus ; register int j ; DISK d = bp->h.disk ; bp->h.disk = 0 ; /* prevents recursion */ if( !d) return ; chrono("diskfree") ; if(!batArray) diskBATread() ; j = arrayMax(batArray) ; /* must come after eventual BATread */ if ((d >= 8*j) || (d < 0)) { messcrashroutine = simpleCrashRoutine ; messcrash("diskfree called with a wrong disk address"); } cp = arrp(batArray,(int) (d/8),unsigned char) ; minus = arrp(minusArray,(int) (d/8),unsigned char) ; plus = arrp(plusArray,(int) (d/8),unsigned char) ; i = 1 << (d%8); if (! (*cp & i)) { messcrashroutine = simpleCrashRoutine ; messcrash("diskfree called on free block "); } /* on peut oublier les blocs crees pendant la session */ if(*plus & i) /* allocated during this session */ { (*cp) &= ~i; /* free it */ (*plus) &= ~i; /* free it */ } else (*minus) |= i ; batIsModified = TRUE ; chronoReturn() ; return; } /* diskfree */ /********************************************/ static BOOL isBat(DISK d) { register unsigned char i; /*unsigned because we use a right shift operator on i*/ register unsigned char *cp ; register int j ; if(!batArray) return TRUE ; /* during init, one does a few direct address reads */ j = arrayMax(batArray) ; /* must come after eventual BATread */ if ((d >= 8*j) || (d < 0)) /* hors limite de la bat pour ce bloc */ { messcrashroutine = simpleCrashRoutine ; messcrash("diskfree called with a wrong disk address"); } cp = arrp(batArray,(int) (d/8),unsigned char) ; i = 1 << (d % 8); if (*cp & i) return TRUE ; return FALSE ; } /* isBat */ /* This could be an option that was turned on/off interactively, but since */ /* sync mode writing is at least 5x slower, the option is currently set from */ /* session.c via an entry in passwd.wrm. It's the sort of thing a database */ /* administrator should set when there are NFS problems. */ void diskSetSyncWrite(void) { syncWrite = TRUE ; } /**************************************************************/ /* to write a block to disk : return 0 if success */ /* called by blockunload */ void diskblockwrite(BP bp) { char *vp=(char *)bp; long adrPartition; int partition; unsigned int nb,size=BLOC_SIZE; myoff_t pos,pp ; long d = bp->h.disk ; long delta; int fd, nretry = 0 ; VoidRoutine previousCrashRoutine = messcrashroutine ; if(!isWriteAccess()) return ; messcrashroutine = simpleCrashRoutine ; chrono("diskblockwrite") ; blocksWritten++; if(!batArray) diskBATread() ; if ((d < 0)|| (d >= 8 * arrayMax(batArray))) messcrash("Diskblockwrite called with wrong DISK address d=%ld, max = %d", d, 8 * arrayMax(batArray)); /* select partition and block address on partition : */ adrPartition = PartitionAddress( d, &partition); if (adrPartition < 0) messcrash(" Bad Block Address in partition "); /* open partition if not yet */ fd = array (fdWrite, partition, int) - 1 ; if (fd < 0) { char* partName = PartitionName(partition) ; #if defined(MACINTOSH) fd = FMPBopen ( (unsigned char *)partName, O_RDWR | O_BINARY ) ; #else /* UNIX */ int mode = O_WRONLY | O_BINARY ; seteuid(euid); /* NFS can time out without the code being able to detect this, but if */ /* we open the file for synchronous writing we should be able to detect*/ /* this because write calls do not return until data is written to */ /* disk. */ #if !defined(DARWIN) if (syncWrite) mode |= O_SYNC ; #endif fd = open(partName, mode, 0); seteuid(ruid); #endif /* UNIX */ if (fd < 0) messExit ("Error occured when trying to open (for read/write) partition file %s (%s)\n" "%s" "I will have to exit now ...", partName, messSysErrorText(), diskGetErrorText('o','w')); array (fdWrite, partition, int) = fd + 1 ; /* i shift, so that 0 means not open, and arrayExtends is ok */ array (wPos, partition, myoff_t) = 0 ; messfree(partName); } if (fd < 0) messcrash("diskblockwrite() - fd < 0"); pos = (myoff_t) (adrPartition *(long)size ) ; delta = pos - array (wPos,partition,myoff_t) ; if (ps_debug1) fprintf(stderr, "diskblockwrite: d %ld, part %d adr part %d offset %ld\n", (long)d, (int)partition, (int)adrPartition, (long)pos); #ifdef ACEDB4 { static BLOCK copyBlock; if (swapData) { memcpy(©Block, bp, sizeof(BLOCK)); vp = (char *)©Block; switch (bp->h.type) /* type must be invariant internal->external */ { case 'S': swapSuperBlock(©Block); break; case 'A': case 'C': swapABlock(©Block); break; case 'B': swapBBlock(©Block); break; default: messcrash("unknown block type trying to swap data"); } } } #endif /* ACEDB4 */ #if defined(MACINTOSH) nb = FMPBwrite (fd, (myoff_t)delta, fsFromMark, vp, size ); if ( nb != size ) messcrash("Diskblockwrite cannot actually write the DISK,n=%d (%s)\n", nb, messSysErrorText()); #else seteuid(euid); if (delta) if ( (pp = lseek(fd, delta, SEEK_CUR))!= pos ) messExit("Error occured when trying to access (for seek) partition file %s at disk address %d (should be pos=%ld, returned=%ld)\n" "(%s)\n" "I will have to exit now ...", PartitionName(partition), d, (long) pos, (long)pp, messSysErrorText()); while ( (nb = write(fd, vp, size)) != size ) if (nretry++ == 5) messExit ("Error occured when trying to access (for writing) partition file %s at disk address %d (%s)\n" "%s" "I will have to exit now ...", PartitionName(partition), d, messSysErrorText(), diskGetErrorText('a','w')); seteuid(ruid); #endif /* update current position : */ array (wPos, partition, myoff_t) = pos + (long)nb; chronoReturn() ; messcrashroutine = previousCrashRoutine ; return; } /* diskblockwrite */ /**************************************************************/ /* to read a block from disk : return 0 if success */ /* called by blockload */ void diskblockread (BLOCK *bp, DISK d) { char *vp=(char *)bp; long adrPartition; long delta; int partition; unsigned int size=BLOC_SIZE; myoff_t pos,pp ; int n, nretry = 0, fd ; VoidRoutine previousCrashRoutine = messcrashroutine ; messcrashroutine = simpleCrashRoutine ; chrono("diskblockread") ; blocksRead++ ; /* select partition and block address on partition : */ adrPartition = PartitionAddress( d, &partition); if (adrPartition < 0) messcrash(" Bad Block Address in partition "); /* open partition if not yet */ fd = array (fdRead, partition, int) - 1 ; if (fd < 0) { char *partName = PartitionName(partition); #if defined(MACINTOSH) fd = FMPBopen( (unsigned char*) partName, O_RDONLY | O_BINARY ); #else fd = open(partName, O_RDONLY | O_BINARY ,0); #endif if (fd < 0) messExit ("Error occured when trying to open (for reading) partition file %s (%s)\n" "%s" "I will have to exit now ...", partName, messSysErrorText(), diskGetErrorText('o','r')); array (fdRead, partition, int) = fd + 1 ; array (rPos, partition, myoff_t) = 0 ; messfree(partName); } if (fd < 0) messcrash("diskblockread() - fd < 0"); pos = (myoff_t)(adrPartition * (long)size) ; delta = pos - array(rPos, partition, myoff_t) ; if (ps_debug1) fprintf(stderr, "Diskblockread : d %ld, part %d adr part %d offset %ld\n", (long)d, (int)partition, (int)adrPartition, (long)pos); #if defined(MACINTOSH) n = FMPBread (fd, (myoff_t)delta, fsFromMark, vp, size ); if ( n != size ) messcrash("Diskblockread : cannot actually read the DISK,n=%d (%s)\n", n, messSysErrorText()); /* update current position : */ array(rPos, partition, myoff_t) = pos + size ; #else if (delta) if ( (pp = lseek(fd, delta, SEEK_CUR)) != pos ) messExit("Error occured when trying to access (for seek) partition file %s at disk address %d (should be pos=%ld, returned=%ld)\n" "(%s)\n" "I will have to exit now ...", PartitionName(partition), d, (long) pos, (long)pp, messSysErrorText()); while( (n = read(fd, vp, size)) != size ) if (nretry++ == 5) messExit ("Error occured when trying to access (for reading) partition file %s (%s)\n" "%s" "I will have to exit now...", PartitionName(partition), messSysErrorText(), diskGetErrorText('a','r')); /* update current position : */ array (rPos, partition, myoff_t) = pos + size ; #endif #ifdef ACEDB4 if (swapData) switch (bp->h.type) /* type must be invariant internal->external */ { case 'S': break; /* Don't swap superblock, we need to read it before swapData is valid */ case 'A': case 'C': swapABlock(bp); break; case 'B': swapBBlock(bp); break; default: messcrash("Diskblockread : unknown block type trying to transform data"); } #endif /* ACEDB4 */ if (bp->h.type != 'S' && d != *(DISK*)bp) messcrash("Diskblockread : read a block not matching its address"); if(!isBat(d)) messerror("Diskblockread : read a block not registered in the BAT") ; chronoReturn() ; messcrashroutine = previousCrashRoutine ; return; } /* diskblockread */ /****************************************************************/ /* give partition size in #blocks number */ /****************************************************************/ static int PartitionSize(char *partitionName) { long size ; int iSize ; int fd; VoidRoutine previousCrashRoutine = messcrashroutine ; messcrashroutine = simpleCrashRoutine ; #if defined(MACINTOSH) if ( ! ( fd = FMfilopen ( (unsigned char*) partitionName, (unsigned char*) "", (unsigned char*) "r" ) ) ) messcrash("\nPartitionSize : partition %s unreachable (%s)", partitionName, messSysErrorText()); size = FMfilgeteof ( fd ); if (size < 0) messcrash ("PartitionSize : negative size %ld found for partition %s (%s)", partitionName, messSysErrorText()) ; FMfilclose( fd ); #else /* UNIX */ fd = open (partitionName, O_RDONLY); if (fd < 0) { messExit ("Error occurred when trying to open (for reading) partition file %s (%s)\n" "%s" "I will have to exit now ...", partitionName, messSysErrorText(), diskGetErrorText('o','r')) ; } size = lseek(fd, 0, SEEK_END); if (size < 0) { messExit ("Error occured when trying to access (for seek) partition file %s (%s)\n" "I will have to exit now ...", partitionName, messSysErrorText()) ; } close (fd); #endif /* UNIX */ iSize = size / BLOC_SIZE ; if (ps_debug) fprintf(stderr,"PartitionSize : %s = %d\n", partitionName, iSize); messcrashroutine = previousCrashRoutine ; return iSize ; } /* PartitionSize */ /************************************************************/ /* give total base size in #blocks number */ /************************************************************/ static int PhysicalBaseSize() { int i, totalSize = 0; for (i = 0 ; i <= lastPartition_L ; i++) { char *pnam = PartitionName(i); totalSize += PartitionSize(pnam); messfree(pnam); } if (ps_debug) fprintf(stderr, "total Base size : %d\n", totalSize); return totalSize; } /****************************************************/ /* donne l'adresse dans une partition a partir de */ /* l'adresse dans la base */ /****************************************************/ /* DISK casted to long or char * ?*/ static long PartitionAddress(DISK d, int *partition) { /* premiere version tres bete : on cherche sequentiellement */ long adresseDisque = (long)d; long adrDebutPartition = (long)0; PARTITION *pp = arrayp (pTable, 0, PARTITION) ; int part; for (part = 0; part <= lastPartition_L; part++, pp++) { long adrFinPartition = adrDebutPartition + pp->currentBlocks ; if (d < adrFinPartition) { if(ps_debug1) fprintf(stderr, "pour adr disque %ld adresse part : (%d,%ld)\n", (long)d, part, adresseDisque - adrDebutPartition); *partition = part; return adresseDisque - adrDebutPartition; } adrDebutPartition += pp->currentBlocks ; } messcrashroutine = simpleCrashRoutine ; messcrash ("impossible disk address %ld", (long) d) ; return 0 ; /* for compiler happiness */ } /* PartitionAddress */ /****************************************************/ /* give full partition pathname from #number */ /****************************************************/ static char *PartitionName(int num) { PARTITION *pp ; if (num < 0 || num >= arrayMax(pTable)) { messcrashroutine = simpleCrashRoutine ; messcrash ("PartitionName : invalid number %d\n", num) ; } pp = arrayp (pTable, num, PARTITION) ; if (*pp->fileSystem) return strnew(messprintf ("%s/%s", pp->fileSystem, pp->fileName), 0) ; else #if defined(MACINTOSH) return strnew(messprintf ( ":database:%s", pp->fileName ), 0) ; #else return dbPathMakeFilName("database", pp->fileName, "", 0); #endif } /****************************************************/ /* Close all open partitions of the DataBase */ /****************************************************/ static void CloseDataBase() { int fd, *fdp, part ; VoidRoutine previousCrashRoutine = messcrashroutine ; messcrashroutine = simpleCrashRoutine ; part = arrayMax(fdRead) ; fdp = arrp (fdRead, 0, int) - 1 ; while (fdp++, part--) { fd = *fdp - 1 ; if (fd >= 0) { #if defined(MACINTOSH) FMfilclose (fd) ; #else if (close (fd) != 0) messExit ("Error occurred when trying to close a block file (%s)", messSysErrorText()); #endif *fdp = 0 ; } } rPos = arrayReCreate (rPos, 2, myoff_t) ; part = arrayMax(fdWrite) ; fdp = arrp (fdWrite, 0, int) - 1 ; while (fdp++, part--) { fd = *fdp - 1 ; if (fd >= 0) { int rc; #if defined(MACINTOSH) FMfilclose (fd) ; #else seteuid(euid); rc = close (fd) ; seteuid(ruid); if (rc != 0) messExit ("Error occurred when trying to close a block file (%s)", messSysErrorText()); #endif *fdp = 0 ; } } wPos = arrayReCreate (wPos, 2, myoff_t) ; messcrashroutine = previousCrashRoutine ; return; } /* CloseDataBase */ /****************************************************/ /* Database map creation and update */ /****************************************************/ /* Parameters: * * new - if TRUE, we're initialising a new database. * that means we read the database definition from * wspec/database.wrm and we can't assume having read a previous * database map from database/database.map * if FALSE it's just a control for extending the database. * In this case, we can just extend the current map with * new partitions added at the end of the DataBase * description file. * Others modifications are ignored. * * skip - if TRUE there is already a database map, * find new partitions in definition file */ static BOOL DataBaseReadDefinition(BOOL new, BOOL skip) { char *defName ; ACEIN dbdef_in; char *cp ; PARTITION *pp = 0 ; int nbPartitions = 0; VoidRoutine previousCrashRoutine = messcrashroutine ; /* DataBase description file name : */ defName = dbPathStrictFilName("wspec", "database", "wrm", "r", 0); if (ps_debug) fprintf(stderr, "opening def file %s\n", defName); /* open DataBase description file. If no file and DataBase creation, * default to the standard single unix file DataBase */ if (defName) { dbdef_in = aceInCreateFromFile (defName, "r", "", 0); messfree(defName); } else dbdef_in = NULL; if (!dbdef_in) { /* can't open the database description file, * unless we're initialising a new database, * this is unacceptable */ if (!new) return FALSE ; messdump("No DataBase Definition, default to standard unix file"); pp = arrayp(pTable, 0, PARTITION) ; pp->type = UNIX_FILE_SYSTEM; strcpy(pp->hostname, "local"); strcpy(pp->fileSystem, "ACEDB"); strcpy(pp->fileName, "blocks.wrm"); /* can't exceed 1 Giga Bytes : */ pp->maxBlocks = MAX_DATABASE_SIZE; pp->currentBlocks = 0; /* complete memory map initialization : */ lastPartition_L = -1; totalNbPartitions_L = 1; return TRUE; } if (!new && skip) { int n = 0; /* there is already a DataBase and a map, we may have to * extend the map, if the user has added new partitions to * the definition-file */ /* skip existing partitions, * after this loop the file-pointer is positioned * at the end of the last partition-definition that we knew about * when we started it up last time. */ while (n < totalNbPartitions_L && aceInCard (dbdef_in)) { if (!(cp = aceInWord (dbdef_in))) continue ; /* skip empty lines and comments */ if (strcmp(cp, "PART") == 0) n++; } /* set nbPartitions to total calculated from database map, * so if the definition file has grown since the last map was made * (i.e. the user had added new partitions), we'll add new * the new definitions to the partition table, and therefore * increase the total, which will cause the map to be extended. */ nbPartitions = totalNbPartitions_L; } else { /* we're not skipping existing partitions, so we'are reading * the definition file from the start */ nbPartitions = 0; } /* create or extend partitions map : */ messcrashroutine = simpleCrashRoutine ; while (aceInCard (dbdef_in)) { if (!(cp = aceInWord(dbdef_in))) continue ; /* skip empty lines and comments */ if (strcmp(cp, "PART") != 0) /* only recognise PART keyword */ continue ; pp = arrayp(pTable, nbPartitions++, PARTITION) ; aceInStep (dbdef_in, ':') ; if (! aceInInt (dbdef_in, &pp->type) || ! (cp = aceInWord (dbdef_in)) || ! (strcpy (pp->hostname, cp), aceInWord (dbdef_in)) || ! (strcpy (pp->fileSystem, cp), aceInWord (dbdef_in)) || ! (strcpy (pp->fileName, cp)) || ! aceInInt (dbdef_in, &pp->maxBlocks) ) messExit("Parse error in database definition file %s at line %d", dbPathMakeFilName("wspec", "database", "wrm", 0), aceInStreamLine(dbdef_in)-1); /* current, maximum partition size in blocks and offset : */ if (strcmp("ACEDB", pp->fileSystem) == 0) *pp->fileSystem = 0 ; if (strcmp("NONE", pp->fileName) == 0) *pp->fileName = 0 ; else { /* this partition name cannot match an existing one !!!! */ int i; PARTITION *pp1 ; for (i = 0; i < nbPartitions-1; i++) { pp1 = arrp(pTable, i, PARTITION); if (strcmp(pp1->fileName, pp->fileName) == 0) messExit("Error in database definition file %s at line %d.\n" "Partition name %s already exists!", dbPathMakeFilName("wspec", "database", "wrm", 0), aceInStreamLine (dbdef_in)-1, pp->fileName); } /* delete partition if exists , but not old style blocks.wrm !! */ if (strcmp("blocks.wrm", pp->fileName) != 0) { char *fn = dbPathStrictFilName("database", pp->fileName, 0, "r", 0); if (fn) { unlink (fn) ; messfree(fn); } } } pp->currentBlocks = 0; } aceInDestroy (dbdef_in); /* complete memory map initialization : */ if (new) lastPartition_L = -1; /* set new total, as it may have changed if the def-file had grown */ totalNbPartitions_L = nbPartitions; /* in case of "old DataBase style" map and in case we have * more than this "blocks.wrm" partition : * we adjust max blocks.wrm partition to its current size */ pp = arrayp(pTable, 0, PARTITION) ; if (strcmp(pp->fileName, "blocks.wrm") == 0 && nbPartitions > 1) pp->maxBlocks = pp->currentBlocks ; messcrashroutine = previousCrashRoutine ; return TRUE; } /* DataBaseReadDefinition */ /* assume that DataBase memory map exists */ static BOOL DataBaseUpdateMap(void) { #if defined(MACINTOSH) BOOL wasUpdated = FALSE ; char *mapName ; int fpMap ; PARTITION *pp = arrayp(pTable, 0, PARTITION) ; char fileSystem[256], fileName[256], mapString[256] ; int nbPartitions = totalNbPartitions_L; mapName = dbPathStrictFilName("database", "database", "map", "w", 0) ; if (ps_debug1) fprintf(stderr, "opening map file %s\n", mapName); if ( mapName && ( fpMap = FMfilopen ( (unsigned char*) mapName, (unsigned char*) "", (unsigned char*) "w" ))) { sprintf( mapString, "%d %d\n", totalNbPartitions_L, lastPartition_L) ; FMfilwrite ( fpMap, (unsigned char*) mapString, strlen ( mapString ) ) ; while (nbPartitions--) { /* system and file names : */ if (!pp->type) continue ; strcpy(fileSystem, pp->fileSystem) ; strcpy(fileName, pp->fileName); if (!strlen(pp->fileSystem)) strcpy(fileSystem, "ACEDB"); if (!strlen(pp->fileName)) strcpy(fileName, "NONE"); sprintf( mapString, "%d %s %s %s %d %d %d\n", pp->type, pp->hostname, fileSystem, fileName, pp->maxBlocks, pp->currentBlocks, 0) ; /* last 0, for backward compatibility, was the unused offset variable */ FMfilwrite (fpMap, (unsigned char*) mapString, strlen (mapString) ) ; pp++; } FMfilclose (fpMap); wasUpdated = TRUE ; } if (mapName) messfree(mapName); return wasUpdated; #else /* !MACINTOSH */ /* DataBaseUpdateMap */ char *mapName; FILE *fil = 0 ; PARTITION *pp ; char fileSystem[256], fileName[256]; int i; seteuid(euid); mapName = dbPathMakeFilName("database","database", "map", 0); /* dbPathStrictFilname fails on file which need write access via euid */ fil = fopen(mapName, "w"); messfree(mapName); if (!fil) { messcrash("Unable to update database/database.map file (%s)", messSysErrorText()); return FALSE ; } /* write first line */ fprintf(fil, "%d %d\n", totalNbPartitions_L, lastPartition_L) ; /* write all partitions */ for (i = 0; i < totalNbPartitions_L; i++) { pp = arrayp (pTable, i, PARTITION); if (!pp->type) continue ; /* system and file names : */ strcpy(fileSystem, pp->fileSystem); strcpy(fileName, pp->fileName); if (strlen(pp->fileSystem) == 0) strcpy(fileSystem, "ACEDB"); if (strlen(pp->fileName) == 0) strcpy(fileName, "NONE"); if (fprintf(fil, "%d %s %s %s %d %d %d\n", pp->type, pp->hostname, fileSystem, fileName, pp->maxBlocks, pp->currentBlocks, 0) == EOF) { messdump("Unable to update database/database.map file (%s)", messSysErrorText()); return FALSE; } } fclose (fil); seteuid(ruid); return TRUE; #endif /* !MACINTOSH */ } /* DataBaseUpdateMap */ /*************************************************/ /* download in memory structure the database map */ /*************************************************/ /****************************************************/ /* try to build a DataBase map with first partition */ /* as old style dataBase file database/blocks.wrm */ /****************************************************/ static void CreateOldStyleDataBaseMap(void) { PARTITION *pp = arrayp (pTable, 0, PARTITION) ; char *oldBaseFileName = dbPathStrictFilName("database", "blocks","wrm","r", 0) ; if (!oldBaseFileName) messExit("Consistency error - " "No DataBase map and no old style DataBase " "(i.e. database/blocks.wrm)\n" "You may have to re-initialise the database") ; if (!(pp->currentBlocks = PartitionSize (oldBaseFileName))) messcrash("\n old DataBase (database/blocks.wrm) is empty !!\n"); messfree(oldBaseFileName); pp->type = UNIX_FILE_SYSTEM; strcpy(pp->hostname, "local"); strcpy(pp->fileSystem, ""); strcpy(pp->fileName, "blocks.wrm"); /* maximum size is current size (we only have one block) */ pp->maxBlocks = pp->currentBlocks; lastPartition_L = 0; totalNbPartitions_L = 1; DataBaseUpdateMap () ; /* add others partitions, */ DataBaseReadDefinition(FALSE, FALSE); /* new=FALSE,skip=FALSE */ if(totalNbPartitions_L == 1) { /* single file partition : extend max size to 1 Giga : */ pp->maxBlocks = MAX_DATABASE_SIZE; DataBaseUpdateMap (); } return; } /* CreateOldStyleDataBaseMap */ static void readPartitionMap (void) { char *mapName, *cp ; ACEIN dbmap_in = 0; PARTITION *pp ; int i, mapPartitionNum, nn ; VoidRoutine previousCrashRoutine = messcrashroutine ; STORE_HANDLE handle = handleCreate(); messcrashroutine = simpleCrashRoutine ; mapName = dbPathStrictFilName("database", "database", "map", "r", handle); if (mapName) /* map-file exists and is readable */ dbmap_in = aceInCreateFromFile (mapName, "r", "", 0); if (!dbmap_in) { /* if map-file not readable, attempt to create one * with old DataBase file blocks.wrm */ CreateOldStyleDataBaseMap () ; /* now, again to open the file */ dbmap_in = aceInCreateFromFile (mapName, "r", "", 0); } if (!dbmap_in) { totalNbPartitions_L = 1 ; /* default */ } else /* read the database.map file */ { /* skip first line */ if (!aceInCard (dbmap_in)) messExit ("Error in database map file %s : \n" "Unable to read first line. The file mustn't be empty !", mapName); nn = 0 ; i = 0 ; lastPartition_L = -1 ; totalNbPartitions_L = 0; while (aceInCard (dbmap_in)) { /* read map file line by line */ cp = aceInPos (dbmap_in) ; if (!*cp) continue ; pp = arrayp(pTable, i, PARTITION) ; totalNbPartitions_L++ ; if (!aceInInt (dbmap_in, &pp->type) || !(cp = aceInWord(dbmap_in)) || (strcpy(pp->hostname, cp), !(cp = aceInWord(dbmap_in))) || (strcpy(pp->fileSystem, cp), !(cp = aceInWord(dbmap_in))) || (strcpy(pp->fileName, cp), !aceInInt (dbmap_in, &pp->maxBlocks)) || !aceInInt (dbmap_in, &pp->currentBlocks)) messExit ("Parse error in database map file %s at line %d", mapName, aceInStreamLine(dbmap_in)-1); /* system and file names : */ if (strcmp(pp->fileSystem, "ACEDB") == 0) strcpy(pp->fileSystem, ""); if (strcmp(pp->fileName, "NONE") == 0) strcpy(pp->fileName, ""); pp->debut = nn ; nn += pp->currentBlocks ; pp->fin = nn ; if (pp->currentBlocks > 0) lastPartition_L = i ; i++ ; } aceInDestroy (dbmap_in); } /* now, control if new partitions are defined in the */ /* DataBase Description skip current definitions : */ mapPartitionNum = totalNbPartitions_L; if (DataBaseReadDefinition(FALSE, TRUE)) /* new=FALSE, skip=TRUE */ { if (totalNbPartitions_L > mapPartitionNum) /* the database definition file has grown, since the * map was built (having mapPartitionNum partitions), * so write a new map */ DataBaseUpdateMap(); } messcrashroutine = previousCrashRoutine ; handleDestroy(handle); return; } /* readPartitionMap */ /************************************************************/ static char *diskGetErrorText (char action, /* o = open * a = access */ char spec) /* r = read * w = write * a = append */ { static char error_text[5000] = ""; switch (errno) { case EACCES: /* Permission denied */ { char *purpose = (spec == 'r') ? "readable" : "writable"; char *what = (action == 'o') ? "directory" : "partition file"; sprintf(error_text, "Check that the database %s is %s by %s\n", what, purpose, getLogin (FALSE)); } break; case EFBIG: /* File too large */ sprintf(error_text, "The maximum file size has been exceeded. " "Please check the 'max_size' number in wspec/database.wrm.\n"); break; case EDQUOT: sprintf(error_text, "The quota of disk-blocks or i-nodes defined for the user " "on the filsystem containing the database has been exhausted.\n"); break; case EINTR: { char *attempt = (action == 'o') ? "open a new" : "access a"; sprintf(error_text, "The attempt to %s partition file was " "stopped by an interrupt signal\n", attempt); } break; case EMFILE: sprintf(error_text, "You have reached the soft limit for open files for this process. " "Type 'limit descriptors' in your shell to check the maximum allowed.\n"); break; case ENFILE: sprintf(error_text, "The system file table is full\n"); break; case ENOSPC: { char *what = (action == 'o') ? "directory" : "partition file"; sprintf(error_text, "The database disk is full " "and the %s could not be extended.\n",what); } break; case ERANGE: /* Result too large */ sprintf(error_text, "The disk access request size was " "outside the range supported by the file system\n"); break; case EROFS: { char *hint = (spec == 'r') ? "" : "It needs to be re-configured to be able to extend the database."; sprintf(error_text, "The database files are located on a read-only file-system.%s\n", hint); } break; case EREMOTE: sprintf(error_text, "It seems that the NFS server mounting the database disk has " "attempted to handle the request to extend the database by generating " "a request to another NFS server, which is not allowed.\n"); break; case ESTALE: sprintf(error_text, "The NFS server of the database " "disk has unmounted the database directory.\n"); break; case ETIMEDOUT: sprintf(error_text, "The network connection to the NFS " "server mounting the database directory " "has timed out. The server may be down or " "there is a network problem.\n"); break; default: strcpy(error_text, ""); } return &error_text[0]; } /* diskGetErrorText */ #endif /* ACEDB 4 only */ /**************************************************************/ /**************************************************************/