/* $Id$ * Author: Roger A. Cole * Date: 03-05-90 * * Experimental Physics and Industrial Control System (EPICS) * * Copyright 1991-92, the Regents of the University of California, * and the University of Chicago Board of Governors. * * This software was produced under U.S. Government contracts: * (W-7405-ENG-36) at the Los Alamos National Laboratory, * and (W-31-109-ENG-38) at Argonne National Laboratory. * * Initial development by: * The Controls and Automation Group (AT-8) * Ground Test Accelerator * Accelerator Technology Division * Los Alamos National Laboratory * * Co-developed with * The Controls and Computing Group * Accelerator Systems Division * Advanced Photon Source * Argonne National Laboratory * * Modification Log: * ----------------- * .01 03-05-90 rac initial version * .02 07-30-91 rac installed in SCCS * .03 09-14-92 rac discontinue using special malloc routines * .04 10-29-92 rac under Unix, cope with write getting EINTR status * * make options * -DvxWorks makes a version for VxWorks * -DNDEBUG don't compile assert() checking * -DDEBUG compile various debug code */ /*+/mod*********************************************************************** * TITLE bfSubr.c - 'block file' subroutines * * DESCRIPTION * These subroutines implement a fixed block size file system based on * the usual byte-oriented file system available to C. The operations * used by these routines are: * o open() with O_RDONLY, O_RDWR, and O_CREAT as options. The O_EXCL * option found in some operating systems isn't used. Instead, these * routines create a "fileName.lock" file to enforce a single writer. * o lseek() with L_SET as the option used. * o read(), write(), and close() * * On disk, the file system is a file containing fixed size blocks. These * routines maintain a singly-threaded list of 'free' blocks which are * available for use. User routines built on top of these routines * must maintain singly-threaded lists of 'in use' blocks. * * These routines maintain some information in a file descriptor in memory. * This information isn't written to the disk until the file is closed. * Users of these routines must take particular care to ensure that * bfClose() is called for all open files. * * Since information about the file is kept in the file descriptor, these * routines aren't reentrant with respect to a single file. Several file * descriptors can be used simultaneously, one per thread, without * interference. * * Although these routines do a reasonable amount of error checking, the * user must bear a significant amount of responsibility in abiding by * the rules. Some cautions for the user include: * o don't use pointers to file descriptors after bfClose() is called * o don't exceed the block size for the file. BF_MAX_BLOCK_SIZE is the * largest a block will ever be, but BfBlockSize(pDesc) returns the * actual block size for a particular file. * * Users are strongly encouraged to use the BfXxx() macros when dealing * with block0 or the file descriptor. * * INCLUDE FILES * In general, use of the block file routines requires using several * include files. The set used differs between SunOS and VxWorks: * * SunOs VxWorks * * * * * * * BUGS * o routines aren't reentrant with respect to a single file * o some operations require an operating system file I/O call when, with * appropriate enhancements, such I/O could be avoided, or at least * deferred * o a list of open block files should be kept for a task, and an exit * handler should be established for a task, so that cleanup is assured * o the issue of blocking signals during critical operations should be * addressed * o error handling is rudimentary: there are no individualized error * codes; most error messages don't include file name; error messages * are sent to stdout; etc. * o these routines use blocking I/O. Under SunOS, non-blocking I/O * might be better. * *-***************************************************************************/ /*---------------------------------------------------------------------------- * synopsis summaries * < indicates input arg to function * > indicates output arg from function * <> indicates arg both input to and output from function * * except as noted, 'int' functions return OK or ERROR * blockNum must be 'BF_BLKNUM' * * char *bfRingFgets(>pBuf, pBfDesc ) * returns pBuf or NULL * int bfRingFputs(pBfDesc ) * returns last character or EOF * int BfRingGetc(<>pDesc ) * returns character or EOF * int BfRingPutc( pBfDesc ) * returns character or EOF * *BF_BLKNUM bfAcquire( <>pBfDesc, <>pBfBlock ) * returns blockNum or ERROR * int bfClose( <>ppBfDesc ) * BF_DESC *bfCreate( pBfBlock, pBfDesc, <>pBfBlock, pBfBlock, #include #include #ifdef vxWorks # include # include # include typedef long off_t; /* for lseek() */ #else # include # include # include # include #endif #ifdef LINT char *glBfTypeKw[]; main() { char *pBuf; BF_DESC *pDesc; BF_BLOCK *pBlock; exit(1); pBuf = NULL; pDesc = NULL; pBlock = NULL; (void) bfRingFgets(pBuf, 0, pDesc); (void) bfRingFputs(pBuf, pDesc); (void) bfClose(&pDesc); pDesc = bfCreate(" ", 0, (BF_BLKNUM)0, 0, (long)0, (BF_BLKNUM)0); (void) bfInfo(" "); (void) bfRelease(pDesc, pBlock, (BF_BLKNUM)0); } #endif BF_DESC *bfMallocDesc() { return (BF_DESC *)malloc(sizeof(BF_DESC)); } void bfFreeDesc(ptr) BF_DESC *ptr; { free(ptr); } BF_BLOCK0 *bfMallocBlock0() { return (BF_BLOCK0 *)malloc(sizeof(BF_BLOCK0)); } void bfFreeBlock0(ptr) BF_BLOCK0 *ptr; { free(ptr); } /*+/subr********************************************************************** * NAME bfRingFgets - get a line from a ring file * * DESCRIPTION * Read the next line, including the newline, from the ring file into * the caller's buffer. The last character placed in the caller's buffer * is always followed by '\0'. * * At most bufDim-1 characters are transferred; if no newline was * encountered, then the remaining characters will be read with the * next call. * * Due to the nature of ring files, the first call to bfRingFgets() * may encounter a partial line. If this occurs, the partial line * is discarded and the first full line is returned. * * RETURNS * pBuf, or * NULL if an error occurs or EOF is encountered * *-*/ char * bfRingFgets(pBuf, bufDim, pDesc) char *pBuf; /* buffer to receive up to 'bufDim-1' characters */ int bufDim; /* dimension of buffer */ BF_DESC *pDesc; /* block file descriptor */ { int c; /* character from file */ char *pBuf1; /* temporary buffer pointer */ pBuf1 = pBuf; while (--bufDim > 0 && (c = BfRingGetc(pDesc)) != EOF) { if ((*pBuf1++ = c) == '\n') break; } *pBuf1 = '\0'; return ((c == EOF && pBuf1 == pBuf) ? NULL : pBuf); } /*+/subr********************************************************************** * NAME bfRingFillRbufAndGetc - get next block and return a character * * DESCRIPTION * Reads the next readable block from the ring file and returns the * first readable character from the block. (If the present block, if * any, contains unread characters, then those characters will be * skipped.) * * Due to the nature of ring files, the first call to bfRingFillRbuf() * may encounter a partial line. If this occurs, the partial line * is discarded and the character is obtained from the first full line. * * The macro BfRingGetc(pDesc) is the recommended method for reading * a ring file a character at a time. That macro calls this routine * as necessary. * * The routine bfRingFgets() is available for reading a ring file a line * at a time. * * RETURNS * character, or * EOF if an error occurs or EOF is encountered * *-*/ int bfRingFillRbufAndGetc(pDesc) BF_DESC *pDesc; /* IO block file descriptor for ring file */ { int c; /* character to return to caller */ int stat; /*---------------------------------------------------------------------------- * first, decide which block to read for more data. If this is the first * time a read has been done, use the ringHead block; otherwise, use the * flink from the present block (if the present block is ringTail, then * there is no next block). *---------------------------------------------------------------------------*/ if (pDesc->readCount < -9) pDesc->readBlock = BfB0RingHead(pDesc); else { if (pDesc->readBlock == BfB0RingTail(pDesc)) return EOF; else pDesc->readBlock = pDesc->pReadBuf->flink; } /*---------------------------------------------------------------------------- * if the block number to read is zero, then there is nothing to do but * return EOF. If the block number isn't zero, read the block; otherwise, * return EOF. Once the block has been read, check lastByte; if it indicates * no data, return EOF. * * When the first read is to be done, a buffer is obtained from malloc, * with the pointer stored in the descriptor. This buffer will be free'd * when the bfClose() is called. * *---------------------------------------------------------------------------*/ if (pDesc->readBlock > 0) { if (pDesc->pReadBuf == NULL) { if ((pDesc->pReadBuf = (BF_BLOCK *)malloc((unsigned)BfB0BlockSize(pDesc))) == NULL) { (void)fprintf(stderr, "bfRingFillRbufAndGetc: can't get memory\n"); return EOF; } } stat = bfRead(pDesc, pDesc->pReadBuf, pDesc->readBlock); if (stat != OK) c = EOF; else { if (pDesc->pReadBuf->lastByte < BF_BLOCK_DATA) c = EOF; else { /*---------------------------------------------------------------------------- * set the read pointer and readCount. If this is the first read for the * file, then start reading at firstByte; succeeding blocks start reading * at BF_BLOCK_DATA, in order to read any overflow bytes from previous * blocks. (If the block for the first read for the file contains only * overflow bytes, then EOF is returned.) * * If the file isn't organized by lines, then reading always starts at * BF_BLOCK_DATA. * *---------------------------------------------------------------------------*/ int i=0; if (pDesc->readCount < -9) { /* first read for file */ i = pDesc->pReadBuf->firstByte; if (i == 0) /* file not line-oriented */ i = BF_BLOCK_DATA; } else i = BF_BLOCK_DATA; if (i < BF_BLOCK_DATA) { /* only overflow bytes */ pDesc->pRead = NULL; pDesc->readCount = 0; c = EOF; } else { pDesc->pRead = (char *)pDesc->pReadBuf + i - 1; pDesc->readCount = pDesc->pReadBuf->lastByte - i + 1; c = (int)*(char *)(++pDesc->pRead); pDesc->readCount--; } } } } else c = EOF; return c; } /*+/subr********************************************************************** * NAME bfRingFputs - put a line into a ring file * * DESCRIPTION * Write the line, including the newline, into the ring file. The * '\0' at the end of the caller's line isn't written into the file. * * If this is the first line written to a block, then firstByte is * set to point to this line. * * RETURNS * last character scanned from 'line' (typically '\0'), or * EOF, if any error occurs * *-*/ int bfRingFputs(line, pDesc) char *line; /* I line to write */ BF_DESC *pDesc; /* IO pointer to descriptor */ { if (*line != '\0') { if (BfRingPutc(*line, pDesc) == EOF) return EOF; if (pDesc->pWriteBuf->firstByte == 0) { pDesc->pWriteBuf->firstByte = pDesc->pWrite - (char *)pDesc->pWriteBuf; } } else return *line; while (*(++line) != '\0') { if (BfRingPutc(*line, pDesc) == EOF) return EOF; } return *line; } /*+/subr********************************************************************** * NAME bfRingFlushWbufAndPutc - flush write buffer and write a character * * DESCRIPTION * Write a block to the ring file, get the next block for writing, and * put the character into the block. The file descriptor must have * a pointer to the memory buffer for the block at entry (except * immediately after bfOpen() ); at exit, a pointer to the memory buffer * for the new block has been placed in the file descriptor. * * For the first write following bfOpen(), this routine obtains a * memory buffer for the block and places the pointer into the descriptor. * In this case, there is no buffer to flush. Succeeding calls to this * routine will function as described above. * * When this routine is called the first time following bfOpen(), it * attempts to begin writing in the ringTail block, if there is one. * When additional blocks are needed for writing, attempts are made to * acquire them in the following order: * o try to get a block from the free block list * o try to get a block by expanding the file if it is BF_FLAG_GROW * o follow the current block's flink, if it isn't zero * o if the current block's flink is zero, then grab the ringHead * block for writing and move ringHead forward to its flink * * The macro BfRingFputc(c, pDesc) is the recommended method for * writing a ring file a character at a time. That macro calls this * routine as necessary. * * The routine bfRingFputs() is available for writing a ring file * a line at a time. * * RETURNS * character written, or * EOF if an error occurs * * BUGS * o does a malloc/free for every call * * EXAMPLE * *-*/ int bfRingFlushWbufAndPutc(c, pDesc) char c; /* I character to write */ BF_DESC *pDesc; /* IO pointer to ring file descriptor */ { int stat; /* status return from calls */ BF_BLKNUM flink; /* link to next write block */ BF_BLOCK *pBuf; /* next write block */ if ((pBuf = (BF_BLOCK *)malloc((unsigned)BfB0BlockSize(pDesc))) == NULL) { (void)fprintf(stderr, "bfRingFlushWbufAndPutc: can't get memory\n"); return EOF; } if (pDesc->writeBlock == 0) { /*---------------------------------------------------------------------------- * this is the first write for the file. Get a block for writing by * trying first the ringTail block and then, if there isn't a ringTail, * acquiring a block. If the ringTail block is full, make it look like * the 'old' block, so that a new block will be acquired by the normal * mechanism. *---------------------------------------------------------------------------*/ if ((flink = BfB0RingTail(pDesc)) > 0) { stat = bfRead(pDesc, pBuf, flink); if (stat != OK) { (void)free((char *)pBuf); return EOF; } if (pBuf->lastByte >= BfB0BlockSize(pDesc)-1) { pDesc->pWriteBuf = pBuf; /* masquerade as old */ pDesc->writeBlock = flink; } } else { flink = bfAcquire(pDesc, pBuf); if (flink == ERROR) { (void)free((char *)pBuf); return EOF; } } } if (pDesc->writeBlock != 0) { /*---------------------------------------------------------------------------- * the current block is full. Obtain a new block. Attempts are made * in the following order: * o try to get a block from bfAcquire(), which will either remove * a block from the free block list or which will expand the file. * If neither succeeds, then: * o follow the block's flink, if the flink isn't zero. If the flink * is zero, then: * o use the block which is currently the ringHead * In the latter two cases, the block must be made to appear empty by * setting firstByte and lastByte appropriately. *---------------------------------------------------------------------------*/ int readFlag; if ((flink = bfAcquire(pDesc, pBuf)) > 0) readFlag = 0; else { if ((flink = pDesc->pWriteBuf->flink) > 0) readFlag = 1; else { if ((flink = BfB0RingHead(pDesc)) > 0) readFlag = 1; else { free((char *)pBuf); return EOF; /* no new block available */ } } } if (readFlag) { /* only have flink; get the actual block */ stat = bfRead(pDesc, pBuf, flink); if (stat != OK) { free((char *)pBuf); return EOF; } pBuf->firstByte = 0; pBuf->lastByte = BF_BLOCK_DATA - 1; } } /*---------------------------------------------------------------------------- * 'flink' is the number of a new block; 'pBuf' has its memory address. Wrap * up processing on the 'old' block and then install the new one. * * first, install the proper flink into the current block. If the new block * was obtained via bfAcquire(), then it is necessary to splice the new * block into the flink chain. * * then, update the lastByte in the block to correspond with the pointer * in the descriptor. * * finally, write the current block. (In some cases there isn't a current * block to write; this will usually be the case on the first call after * a file has been opened.) After writing the current block, free the * buffer. *---------------------------------------------------------------------------*/ if (pDesc->pWriteBuf != NULL) { if (pDesc->pWriteBuf->flink == flink) ; /* old block's flink OK */ else if (pDesc->pWriteBuf->flink == 0) pDesc->pWriteBuf->flink = flink; /* link old to new */ else { pBuf->flink = pDesc->pWriteBuf->flink; /* splice flinks */ pDesc->pWriteBuf->flink = flink; } pDesc->pWriteBuf->lastByte = pDesc->pWrite - (char *)pDesc->pWriteBuf; stat = bfWrite(pDesc, pDesc->pWriteBuf, pDesc->writeBlock); if (stat != OK) { free((char *)pBuf); return EOF; } free((char *)pDesc->pWriteBuf); } /*---------------------------------------------------------------------------- * the current block has been dealt with. Install the new one into the * descriptor. (In the descriptor is the address of the memory that was * malloc'ed above.) *---------------------------------------------------------------------------*/ if (BfB0RingHead(pDesc) == 0) BfB0RingHead(pDesc) = flink; else if (BfB0RingHead(pDesc) == flink) BfB0RingHead(pDesc) = pBuf->flink; BfB0RingTail(pDesc) = flink; pDesc->writeBlock = flink; pDesc->pWriteBuf = pBuf; pDesc->writeCount = BfB0BlockSize(pDesc) - pBuf->lastByte - 1; pDesc->pWrite = (char *)pDesc->pWriteBuf + pBuf->lastByte; /*---------------------------------------------------------------------------- * FINALLY (whew!), store the character that was sent in! *---------------------------------------------------------------------------*/ *(++pDesc->pWrite) = c; --pDesc->writeCount; return c; } /*+/subr********************************************************************** * NAME bfAcquire - acquire a block for use * * DESCRIPTION * Acquires a block for use by the caller, placing the block in the * caller's buffer. * * bfAcquire() will acquire the block from the free block list, if any * free blocks are available. If no free blocks are available, then * the file is extended, if the BF_FLAG_GROW flag is set and the maximum * size won't be violated. Otherwise, no block is obtained. * * The block number returned by bfAcquire() is the value which must * be used with bfRead(), bfWrite(), etc. * * 'firstByte' is set to 0 . 'lastByte' points one character before * the first available byte location. 'flink' is set to 0 . * * The free block list on disk isn't updated by bfAcquire(), so it is * extremely important that bfClose() be called before exiting. * * RETURNS * block number or * ERROR (no error message is printed if reason for not being able to * acquire a block is either: no free block or can't grow file) * * BUGS * o should maintain an 'in memory' list of free blocks and replenish * the list as necessary. This would avoid I/O delays on bfAcquire() * calls. * o should be able to get a free block without needing to do disk I/O * o multiple, simultaneous calls to this routine could result in giving * the same block to several callers. The callers must presently * cooperate to prevent simultaneous calls. * * SEE ALSO * bfRelease() * * EXAMPLE * BF_DESC *pDesc; pointer to descriptor * BF_BLOCK *pBuf; pointer to buffer containing block * BF_BLKNUM blockNum; block number of acquired block * * pDesc = bfOpen("myFile", O_RDWR, BF_TYPE_UNDEF); * pBuf = (BF_BLOCK *)malloc(BfB0BlockSize(pDesc)); * blockNum = bfAcquire(pDesc, pBuf); * pBuf->firstByte = pBuf->lastByte + 1; * for (i=0; ilastByte] = myData[i]; * stat = bfWrite(pDesc, pBuf, blockNum, 0); * stat = bfClose(pDesc); * free((char *)pBuf); * *-*/ BF_BLKNUM bfAcquire(pDesc, pBuf) BF_DESC *pDesc; /* IO pointer to descriptor */ BF_BLOCK *pBuf; /* IO buffer ptr; buf size is BfB0BlockSize(pDesc) */ { int stat; /* status from calls */ int blockNum; /* number of acquired block */ assert(pDesc != NULL); assert(pBuf != NULL); if (!BfWriteable(pDesc)) { (void)fprintf(stderr, "bfAcquire: file is O_RDONLY\n"); return ERROR; } if (BfB0Flink(pDesc) == 0) { /*---------------------------------------------------------------------------- * NO FREE BLOCK -- try to extend the file *---------------------------------------------------------------------------*/ if ( BfB0CurrentSize(pDesc) >= BfB0MaxSize(pDesc) || (BfB0Flags(pDesc) & BF_FLAG_GROW) == 0 ) { (void)fprintf(stderr, "bfAcquire: file maxSize reached\n"); return ERROR; } else blockNum = BfB0CurrentSize(pDesc)++; } else { /*---------------------------------------------------------------------------- * USE A FREE BLOCK *---------------------------------------------------------------------------*/ off_t offset; /* offset into file, for lseek() */ off_t lseek(); /* lseek function */ blockNum = BfB0Flink(pDesc); offset = blockNum * BfB0BlockSize(pDesc); stat = lseek((int)pDesc->fd, offset, L_SET); if (stat == ERROR) { (void)fprintf(stderr, "bfAcquire: error on lseek for free block\n"); return ERROR; } stat = read((int)pDesc->fd, (char *)pBuf, (int)BfB0BlockSize(pDesc)); if (stat != BfB0BlockSize(pDesc)) { (void)fprintf(stderr, "bfAcquire: error reading free block\n"); return ERROR; } /*---------------------------------------------------------------------------- * move the free block's flink into block0 and decrement the free block count *---------------------------------------------------------------------------*/ BfB0Flink(pDesc) = pBuf->flink; BfB0CurrentFree(pDesc)--; } pBuf->flink = 0; pBuf->firstByte = 0; pBuf->lastByte = BF_BLOCK_DATA - 1; return blockNum; } /*+/subr********************************************************************** * NAME bfClose - close a block file * * DESCRIPTION * Close a file. The actions taken are: * o if pDesc->writeBlock > 0, write the block * o if pDesc->pReadBuf != NULL, free the buffer * o if pDesc->pWriteBuf != NULL, free the buffer * o write block0 * o if the file was opened O_RDWR, delete the write lock file * o free the descriptor and store null in the caller's pointer * * RETURNS * OK, or * ERROR * * BUGS * o writing the block and freeing the buffers are somewhat controversial. * If bfClose() free's the blocks, that may interfere with the caller's * memory management. If the caller free's the buffers in an exit * handler, that may interfere with bfClose() writing the block. There * is also a potential conflict between exit handlers. The caller won't * know how large to make the buffer until after bfOpen() has been * called, and thus may be inclined to wait until after bfOpen() to * register the block with the caller's exit handler, which might result * in the caller's exit handler having precedence over one possibly * established by bfOpen(); if this scenario were acted out, the buffer * would be free'd before bfClose() got a chance to write it. * * SEE ALSO * bfOpen() * *-*/ int bfClose(ppDesc) BF_DESC **ppDesc; /* IO pointer to pointer to descriptor */ { int stat; /* status from calls */ int retStat=OK; /* return status */ off_t offset; /* offset into file, for lseek() */ off_t lseek(); /* lseek function */ BF_DESC *pDesc; /* pointer to descripter */ assert(*ppDesc != NULL); pDesc = *ppDesc; /*---------------------------------------------------------------------------- * first, see if a partial block needs to be written; write if needed. If * read and/or write buffers are registered in descriptor, release them. *---------------------------------------------------------------------------*/ if (pDesc->writeBlock > 0) { pDesc->pWriteBuf->lastByte = pDesc->pWrite - (char *)pDesc->pWriteBuf; stat = bfWrite(pDesc, pDesc->pWriteBuf, pDesc->writeBlock); if (stat != OK) { (void)fprintf(stderr, "bfClose: error writing partial block\n"); retStat = ERROR; } } if (pDesc->pWriteBuf != NULL) free((char *)pDesc->pWriteBuf); if (pDesc->pReadBuf != NULL) free((char *)pDesc->pReadBuf); /*---------------------------------------------------------------------------- * next, position file at beginning and write block0 *---------------------------------------------------------------------------*/ if (BfWriteable(pDesc)) { offset = 0; stat = lseek((int)pDesc->fd, offset, L_SET); if (stat >= 0) { stat = write((int)pDesc->fd, (char *)pDesc->pBlock0, (int)BfB0BlockSize(pDesc)); if (stat != BfB0BlockSize(pDesc)) { perror("bfClose: error writing block0"); retStat = ERROR; } } else { perror("bfClose: error on lseek"); retStat = ERROR; } } /*---------------------------------------------------------------------------- * then close the file *---------------------------------------------------------------------------*/ stat = close((int)pDesc->fd); if (stat != 0) retStat = ERROR; /*---------------------------------------------------------------------------- * if the file wasn't read only, delete the lock file *---------------------------------------------------------------------------*/ if (BfWriteable(pDesc)) { char lockName[GEN_FNAME_DIM+5];/* name of lock file for write acc.*/ (void) sprintf(lockName, "%s.lock", pDesc->name); if ((stat = unlink(lockName)) != 0) { perror("bfClose: error deleting lock file"); retStat = ERROR; } } /*---------------------------------------------------------------------------- * and, finally, free the memory for the descriptor and put NULL in the ptr *---------------------------------------------------------------------------*/ if (pDesc->pBlock0 != NULL) bfFreeBlock0(pDesc->pBlock0); if (pDesc != NULL) bfFreeDesc(pDesc); *ppDesc = NULL; return retStat; } /*+/subr********************************************************************** * NAME bfCreate - create and initialize a block file * * DESCRIPTION * Creates and initializes a block file. If the file already exists, * nothing is done. * * The file is created with the specified number of blocks, with all * except the first one on the free block list. The specified type, * flags, block size, and maximum file size will be installed in block 0. * * File mode is set to -rw-rw-r--. Owner and group for the file are * set by the operating system. * * RETURNS * pointer to descriptor, or * NULL * * BUGS * o there is a potential race when creating the file. This routine * checks to see if the file already exists and balks if it does. But * the file could be created between the check and the create done here. * Under UNIX, O_CREAT | O_EXCL avoids the race, but VxWorks can't * handle O_EXCL. * o this routine creates a file by successive writes. Some file system * protocols require creating an empty file of the desired size before * any writing is done. * * EXAMPLE * BF_DESC *pDesc; pointer to descriptor * * pDesc = bfCreate("myFile", BF_TYPE_RING, 12, 1000, BF_FLAG_GROW, 100); * BfB0RingHead(pDesc) = 0; * BfB0RingTail(pDesc) = 0; * stat = bfClose(pDesc) * *-*/ BF_DESC * bfCreate(name, type, nBlocks, blockSize, fileFlags, maxBlocks) char *name; /* I name of file */ int type; /* I file type, one of the BF_TYPE_xxx */ BF_BLKNUM nBlocks; /* I number of blocks; must be < BF_MAX_BLOCKS */ unsigned blockSize; /* I size of blocks; must be >= BF_MIN_BLOCK_SIZE and <= BF_MAX_BLOCK_SIZE */ long fileFlags; /* I flags, one of the BF_FLAG_xxx */ BF_BLKNUM maxBlocks; /* I size limit in blocks; must be <= BF_MAX_BLOCKS */ { int stat; /* status from calls */ int retStat=OK; /* return status */ BF_DESC *pDesc; /* block file descriptor */ int i; /* temp for loops */ int fd; /* temp for file descriptor */ assert(type >= (int)BF_TYPE_UNDEF && type < (int)BF_TYPE_PAST); assert(nBlocks >= 1 && nBlocks <= BF_MAX_BLOCKS); assert(blockSize >= BF_MIN_BLOCK_SIZE && blockSize <= BF_MAX_BLOCK_SIZE); assert(maxBlocks >= BF_MIN_BLOCKS && maxBlocks <= BF_MAX_BLOCKS); if ((fd = open(name, O_RDWR, 0664)) >= 0) { (void)close(fd); (void)fprintf(stderr, "bfCreate: can't create--file already exists\n"); retStat = ERROR; } if (retStat == OK) { pDesc = bfOpen(name, O_RDWR | O_CREAT, type, (int)blockSize); if (pDesc == NULL) { perror("bfCreate: error creating file"); retStat = ERROR; } } if (retStat == OK) { /*---------------------------------------------------------------------------- * set up block 0; special fields are the caller's responsibility *---------------------------------------------------------------------------*/ if (nBlocks > 1) { BfB0Flink(pDesc) = 1; BfB0CurrentSize(pDesc) = nBlocks; BfB0CurrentFree(pDesc) = nBlocks - 1; } BfB0Flags(pDesc) = fileFlags; BfB0MaxSize(pDesc) = maxBlocks; /*---------------------------------------------------------------------------- * write out block 0. * * write out the free block list. Each block except the last has a flink * to the next; the last block has a flink of 0. * * If an error occurs, the write process is aborted and an error message is * printed. *---------------------------------------------------------------------------*/ stat = write((int)pDesc->fd, (char *)pDesc->pBlock0, (int)blockSize); if (stat != blockSize) { retStat = ERROR; perror("bfCreate: error writing block0"); } if (retStat == OK && nBlocks > 1) { BF_BLOCK *pBuf; pBuf = (BF_BLOCK *)malloc(blockSize); if (pBuf == NULL) { retStat = ERROR; (void)fprintf(stderr, "bfCreate: can't get memory for writing free blocks\n"); } else { pBuf->firstByte = 0; pBuf->lastByte = 0; for (i=1; i++flink = (i != nBlocks ? i : 0); stat = write((int)pDesc->fd, (char *)pBuf, (int)blockSize); if (stat != blockSize) { retStat = ERROR; perror("bfCreate: error writing free block list"); break; } free((char *)pBuf); } } } } if (retStat == OK) return pDesc; else return NULL; } /*+/subr********************************************************************** * NAME bfDumpChar - dump, as text, contents of block from block file * * DESCRIPTION * * RETURNS * * BUGS * o text * * SEE ALSO * * EXAMPLE * *-*/ int bfDumpChar(pBfDesc, blockNum) BF_DESC *pBfDesc; /* I pointer to descriptor */ BF_BLKNUM blockNum; /* I block number */ { int stat; /* status from calls */ int retStat=OK; /* return status to caller */ char *pBuf; /* buffer pointer */ if ((pBuf = (char *)malloc((unsigned)BfB0BlockSize(pBfDesc))) == NULL) { (void)printf("bfDumpHex: can't malloc buffer\n"); retStat = ERROR; } if (retStat == OK) { if ((stat = bfRead(pBfDesc, (BF_BLOCK *)pBuf, blockNum)) != OK) { (void)printf("bfDumpHex: error reading block\n"); retStat = ERROR; } } if (retStat == OK) { char *pBuf1; char c; int i,j,last; (void)printf("block:%d flink:%d firstByte:%d lastByte:%d\n", blockNum, ((BF_BLOCK *)pBuf)->flink, ((BF_BLOCK *)pBuf)->firstByte, ((BF_BLOCK *)pBuf)->lastByte); pBuf1 = pBuf; last = ((BF_BLOCK *)pBuf)->lastByte; for (i=0; i<=last; ) { (void)printf("%04x \"", i); for (j=0; j<64; j++) { (void)printf("%c", (isascii(c=pBuf1[i++])&&isprint(c))?c:'.'); if (i>last) break; } (void)printf("\"\n"); } } if (pBuf != NULL) (void)free(pBuf); return retStat; } /*+/subr********************************************************************** * NAME bfDumpHex - dump, in hex, contents of block from block file * * DESCRIPTION * * RETURNS * * BUGS * o text * * SEE ALSO * * EXAMPLE * *-*/ int bfDumpHex(pBfDesc, blockNum) BF_DESC *pBfDesc; /* I pointer to descriptor */ BF_BLKNUM blockNum; /* I block number */ { int stat; /* status from calls */ int retStat=OK; /* return status to caller */ char *pBuf; /* buffer pointer */ if ((pBuf = (char *)malloc((unsigned)BfB0BlockSize(pBfDesc))) == NULL) { (void)printf("bfDumpHex: can't malloc buffer\n"); retStat = ERROR; } if (retStat == OK) { if ((stat = bfRead(pBfDesc, (BF_BLOCK *)pBuf, blockNum)) != OK) { (void)printf("bfDumpHex: error reading block\n"); retStat = ERROR; } } if (retStat == OK) { char *pBuf1; int i,j,last; (void)printf("block:%d flink:%d firstByte:%d lastByte:%d\n", blockNum, ((BF_BLOCK *)pBuf)->flink, ((BF_BLOCK *)pBuf)->firstByte, ((BF_BLOCK *)pBuf)->lastByte); pBuf1 = pBuf; last = ((BF_BLOCK *)pBuf)->lastByte; for (i=0; i<=last; ) { (void)printf("%04x", i); for (j=0; j<8; j++) { (void)printf(" "); (void)printf("%02x", ((int)pBuf1[i++])&0xff); (void)printf("%02x", ((int)pBuf1[i++])&0xff); (void)printf("%02x", ((int)pBuf1[i++])&0xff); (void)printf("%02x", ((int)pBuf1[i++])&0xff); if (i>last) break; } (void)printf("\n"); } } if (pBuf != NULL) (void)free(pBuf); return retStat; } /*+/subr********************************************************************** * NAME bfInfo - print information from block0 of block file * * DESCRIPTION * * RETURNS * * BUGS * o text * * SEE ALSO * * EXAMPLE * *-*/ int bfInfo(name) char *name; /* I name of file */ { int stat; /* status from calls */ int retStat=OK; /* return status */ BF_DESC *pDesc; /* block file descriptor */ long blockSize; /* size of blocks in this file */ off_t offset; /* offset into file, for lseek() */ off_t lseek(); /* lseek function */ int fd; if ((pDesc = bfMallocDesc()) == NULL) { retStat = ERROR; (void)fprintf(stderr, "bfInfo: can't get memory for bfDesc\n"); } if (retStat == OK) { if ((pDesc->pBlock0 = bfMallocBlock0()) == NULL) { retStat = ERROR; (void)fprintf(stderr, "bfInfo: can't get memory for block0\n"); } } if (retStat == OK) { fd = open(name, O_RDWR, 0664); pDesc->fd = fd; if (pDesc->fd < 0) { perror("bfInfo: error opening file"); retStat = ERROR; } } /*---------------------------------------------------------------------------- * first, read only the first few bytes of the record, mostly to find out * how big the blocks are. *---------------------------------------------------------------------------*/ if (retStat == OK) { stat = read((int)pDesc->fd, (char *)pDesc->pBlock0, BF_MIN_BLOCK_SIZE); if (stat != BF_MIN_BLOCK_SIZE) { (void)fprintf(stderr, "bfInfo: error reading block0 preamble\n"); retStat = ERROR; } else { blockSize = BfB0BlockSize(pDesc); (void)printf("BLOCK 0: blockSize is %d\n", blockSize); } } if (retStat == OK) { offset = 0; stat = lseek((int)pDesc->fd, offset, L_SET); if (stat == ERROR) { perror("bfInfo: error on lseek"); retStat = ERROR; } if (retStat == OK) { stat = read((int)pDesc->fd, (char *)pDesc->pBlock0, (int)blockSize); if (stat != blockSize) { (void)fprintf(stderr, "bfInfo: error reading block0\n"); retStat = ERROR; } else { (void)printf("BLOCK 0: type, flink, fByte, lByte %s %d %d %d\n", BfTypeToText(BfB0FileType(pDesc)), BfB0Flink(pDesc), BfB0FirstByte(pDesc), BfB0LastByte(pDesc)); (void)printf("maxSize, currSize, currFree %d %d %d\n", BfB0MaxSize(pDesc), BfB0CurrentSize(pDesc), BfB0CurrentFree(pDesc)); (void)printf("free block list: "); while (BfB0Flink(pDesc) != 0) { (void)printf("%d ", BfB0Flink(pDesc)); offset = BfB0Flink(pDesc) * blockSize; stat = lseek((int)pDesc->fd, offset, L_SET); if (stat == ERROR) { perror("bfInfo: error on lseek"); retStat = ERROR; break; } else { stat = read((int)pDesc->fd, (char *)pDesc->pBlock0, (int)blockSize); if (stat != blockSize) { (void)fprintf(stderr, "bfInfo: error reading block%d\n", offset/blockSize); break; } } } (void)printf("\n"); } } } if (pDesc->fd >= 0) (void)close((int)pDesc->fd); if (pDesc->pBlock0 != NULL) bfFreeBlock0(pDesc->pBlock0); if (pDesc != NULL) bfFreeDesc(pDesc); return retStat; } /*+/subr********************************************************************** * NAME bfOpen - open or create a block file * * DESCRIPTION * Open a block file. The actions taken are: * o if the file type doesn't match the caller's type, return with NULL. * (If the caller's type is BF_TYPE_UNDEF, no type check is made.) * o if the file is being opened O_RDWR, a lock file is created. (If * the lock file already exists, the open isn't done.) The lock file * has the same 'name' as the file, but with the extension of .lock * added. * o a block of memory is obtained to hold a file descriptor and block0 * o block0 is read * o the file descriptor is set up * * This routine can also be used to create a block file, if the open * mode is (O_RDWR & O_CREAT). In this case, the 'blkSize' argument * must have a meaningful value. The actions taken are: * o a lock file is created, as described above * o the file is created, with flags of 0664 (rw-rw-r--) * o a block of memory is obtained to hold a file descriptor and block0 * o an 'empty' block0 is created, with no free blocks and 0 for flags * o the file descriptor is set up * * RETURNS * pointer to descriptor, or * NULL * * BUGS * o the flags used in creating the file with O_CREAT, and the flags * used in creating the lock file, may be system dependent * o this routine should probably grab a few of the free blocks and * store them into the descriptor, which should have a place to 'store' * them. * o there is a potential race when locking against multiple writers. * Another instance of bfOpen() could check for the existance of the * lock file at the same time this one does, both could find the lock * file non-existant, and both could 'create' it. * * SEE ALSO * bfClose(), bfCreate() * * EXAMPLE * BF_DESC *pDesc; pointer to file descriptor * * pDesc = bfOpen("myFile", O_RDONLY, BF_TYPE_UNDEF, 0); * if (pDesc == NULL) { * printf("error opening: myFile\n"); * exit(1); * } * *-*/ BF_DESC * bfOpen(name, mode, type, blkSize) char *name; /* I name of file */ int mode; /* I mode for open; O_RDWR or O_RDONLY. O_CREAT can be specified in conjunction with O_RDWR */ int type; /* I desired type of file; one of BF_TYPE_xxx */ int blkSize; /* I blockSize for new file if mode includes O_CREAT; ignored if mode doesn't include O_CREAT */ { int stat; /* status from calls */ BF_DESC *pDesc; /* block file descriptor */ char buffer[BF_MIN_BLOCK_SIZE]; /* 'starter' buffer */ long blockSize; /* size of blocks in this file */ int fd; /* temp for fd from open */ long flags; /* flags for block file descriptor */ off_t offset; /* offset into file, for lseek() */ off_t lseek(); /* lseek function */ char lockName[GEN_FNAME_DIM+5];/* name of lock file for write acc.*/ assert(type >= (int)BF_TYPE_UNDEF && type < (int)BF_TYPE_PAST); /*---------------------------------------------------------------------------- * first, open the file in the desired mode; if O_RDWR, set the flag *---------------------------------------------------------------------------*/ if (strlen(name) >= GEN_FNAME_DIM) { (void)fprintf(stderr, "bfOpen: file name too long %s\n", name); return NULL; } if ((mode & O_CREAT) != 0) assert(blkSize >= BF_MIN_BLOCK_SIZE && blkSize <= BF_MAX_BLOCK_SIZE); if (mode == O_RDONLY) flags = 0; else if (mode == O_RDWR || mode == (O_RDWR | O_CREAT) ) { flags = BF_DESC_WRITE; (void) sprintf(lockName, "%s.lock", name); if ((fd = open(lockName, O_RDONLY)) >= 0) { (void)close(fd); (void)fprintf(stderr, "bfOpen: %s locked against write access\n", name); return NULL; } else { if ((fd = open(lockName, O_CREAT, 0664)) < 0) { perror("bfOpen: error creating write lock file"); return NULL; } (void)close(fd); } } else { (void)fprintf(stderr, "bfOpen: illegal mode\n", name); return NULL; } if ((fd = open(name, mode, 0664)) < 0) goto errorExit; /*---------------------------------------------------------------------------- * now, find out how big blocks are so we'll know how big to make the buffer *---------------------------------------------------------------------------*/ if ((mode & O_CREAT) != 0) blockSize = blkSize; else { /*---------------------------------------------------------------------------- * read block0 from file *---------------------------------------------------------------------------*/ stat = read(fd, buffer, BF_MIN_BLOCK_SIZE); if (stat != BF_MIN_BLOCK_SIZE) { perror("bfOpen: error reading block0 preamble\n"); (void)close(fd); goto errorExit; } blockSize = ((BF_BLOCK0 *)buffer)->fileInfo.blockSize; } /*---------------------------------------------------------------------------- * finally, malloc for the descriptor and for block0 * set up the descriptor *---------------------------------------------------------------------------*/ if ((pDesc = bfMallocDesc()) == NULL) { (void)fprintf(stderr, "bfOpen: can't get memory for descriptor\n"); (void)close(fd); goto errorExit; } else { if ((pDesc->pBlock0 = bfMallocBlock0()) == NULL) { (void)fprintf(stderr, "bfOpen: can't get memory for block0\n"); bfFreeDesc(pDesc); (void)close(fd); goto errorExit; } } pDesc->fd = fd; pDesc->flags = flags; pDesc->readCount = -10; pDesc->readBlock = 0; pDesc->pReadBuf = NULL; pDesc->writeCount = -10; pDesc->writeBlock = 0; pDesc->pWriteBuf = NULL; (void)strcpy(pDesc->name, name); /* length already checked above */ /*---------------------------------------------------------------------------- * now, get block0 into memory. If we're using O_CREAT, then one must be * created; otherwise, read it from the file. *---------------------------------------------------------------------------*/ if ((mode & O_CREAT) != 0) { /*---------------------------------------------------------------------------- * set up block 0; special fields are the caller's responsibility *---------------------------------------------------------------------------*/ BfB0Flink(pDesc) = 0; BfB0FirstByte(pDesc) = BF_BLOCK_DATA; BfB0LastByte(pDesc) = blockSize - 1; BfB0FileType(pDesc) = type; BfB0BlockSize(pDesc) = blockSize; BfB0Flags(pDesc) = 0; BfB0MaxSize(pDesc) = 1; BfB0CurrentSize(pDesc) = 1; BfB0CurrentFree(pDesc) = 0; } else { /*---------------------------------------------------------------------------- * read block0 from file *---------------------------------------------------------------------------*/ offset = 0; stat = lseek((int)pDesc->fd, offset, L_SET); if (stat == ERROR) { perror("bfOpen: error on lseek"); goto errorExit0; } stat = read((int)pDesc->fd, (char *)pDesc->pBlock0, (int)blockSize); if (stat != blockSize) { perror("bfOpen: error reading block0\n"); goto errorExit0; } } /*---------------------------------------------------------------------------- * do additional checks *---------------------------------------------------------------------------*/ if (type != (int)BF_TYPE_UNDEF && type != BfB0FileType(pDesc)) { (void)fprintf(stderr, "bfOpen: file type mismatch for %s\n", name); goto errorExit0; } return pDesc; errorExit0: (void)close(fd); bfFreeBlock0(pDesc->pBlock0); bfFreeDesc(pDesc); errorExit: if ((flags & BF_DESC_WRITE) != 0) { if ((stat = unlink(lockName)) != 0) perror("bfOpen: error deleting lock file"); } return NULL; } /*+/subr********************************************************************** * NAME bfRead - read a block from a block file * * DESCRIPTION * Reads a block from the file into the caller's buffer. (None of the * read-related fields in the descriptor are updated; these fields * must be set by the caller, if desired.) * * For files of type BF_TYPE_RING, the macro BfRingGetc(pDesc) and the * function bfRingFgets(pBuf, bufDim, pDesc) are available as more * friendly tools for reading the data from blocks. * * RETURNS * OK, or * ERROR * * EXAMPLE * BF_DESC *pDesc; file descriptor * BF_BLOCK *pBuf; buffer pointer * * pDesc = bfOpen("dataFile", O_RDONLY, BF_TYPE_UNDEF); * pBuf = (BF_BLOCK *)malloc(BfB0BlockSize(pDesc)); * stat = bfRead(pDesc, pBuf, 1); * printf("contents of block 1:\n"); * for (i=BF_BLOCK_DATA; ilastByte; i++) * putc(((char *)pBuf)[i]); * stat = bfClose(pDesc); * free((char *)pBuf); * *-*/ int bfRead(pDesc, pBuf, blockNum) BF_DESC *pDesc; /* IO pointer to descriptor */ BF_BLOCK *pBuf; /* IO buffer pointer */ BF_BLKNUM blockNum; /* I block number */ { int stat; /* status from calls */ off_t offset; /* offset into file, for lseek() */ off_t lseek(); /* lseek function */ assert(pDesc != NULL); assert(pBuf != NULL); assert(blockNum >= 0 && blockNum <= BfB0CurrentSize(pDesc)); /*---------------------------------------------------------------------------- * read the block *---------------------------------------------------------------------------*/ offset = blockNum * BfB0BlockSize(pDesc); stat = lseek((int)pDesc->fd, offset, L_SET); if (stat == ERROR) { (void)fprintf(stderr, "bfRead: error on lseek\n"); return ERROR; } stat = read((int)pDesc->fd, (char *)pBuf, (int)BfB0BlockSize(pDesc)); if (stat != BfB0BlockSize(pDesc)) { (void)fprintf(stderr, "bfRead: error reading block\n"); return ERROR; } return OK; } /*+/subr********************************************************************** * NAME bfRelease - release a block to the free block list of a block file * * DESCRIPTION * Adds the caller's block to the head of the free block list. * * The information in the caller's buffer is destroyed. * * RETURNS * OK, or * ERROR * * BUGS * o should provide a mechanism whereby no disk write is involved, or * at least provide a way to delay the write. * o should maintain an 'in memory' list of free blocks and replenish * the list as necessary. This would avoid I/O delays on bfAcquire() * calls. * o multiple, simultaneous calls to this routine could result in various * kinds of scrambling of the free block list. Callers must presently * cooperate to prevent simultaneous calls. * * SEE ALSO * bfAcquire() * *-*/ int bfRelease(pDesc, pBuf, blockNum) BF_DESC *pDesc; /* IO pointer to descriptor */ BF_BLOCK *pBuf; /* IO buffer pointer */ BF_BLKNUM blockNum; /* I number of the block */ { int stat; /* status from calls */ off_t offset; /* offset into file, for lseek() */ off_t lseek(); /* lseek function */ assert(pDesc != NULL); assert(pBuf != NULL); assert(blockNum >= 0 && blockNum <= BfB0CurrentSize(pDesc)); if (!BfWriteable(pDesc)) { (void)fprintf(stderr, "bfRelease: file is O_RDONLY\n"); return ERROR; } /*---------------------------------------------------------------------------- * link this block into the free block list, set firstByte and lastByte * to 0, write the released block, and free the block's memory *---------------------------------------------------------------------------*/ pBuf->flink = BfB0Flink(pDesc); pBuf->firstByte = 0; pBuf->lastByte = 0; offset = blockNum * BfB0BlockSize(pDesc); stat = lseek((int)pDesc->fd, offset, L_SET); if (stat == ERROR) { perror("bfRelease: error on lseek"); return ERROR; } stat = write((int)pDesc->fd, (char *)pBuf, (int)BfB0BlockSize(pDesc)); if (stat != BfB0BlockSize(pDesc)) { (void)fprintf(stderr, "bfRelease: error writing released block\n"); return ERROR; } /*---------------------------------------------------------------------------- * adjust the free block information in block0 *---------------------------------------------------------------------------*/ BfB0CurrentFree(pDesc)++; BfB0Flink(pDesc) = blockNum; return OK; } /*+/subr********************************************************************** * NAME bfWrite - write a block to a block file * * DESCRIPTION * The block is written to the file, with the flink, firstByte, and * lastByte fields written as set by the caller. * * For files of type BF_TYPE_RING, the macro BfRingPutc(c, pDesc) and the * function bfRingFputs(pBuf, pDesc) are available as more * friendly tools for writing the data to blocks. * * RETURNS * OK, or * ERROR * * EXAMPLE * For an example of using bfWrite, see bfAcquire(). * *-*/ int bfWrite(pDesc, pBuf, blockNum) BF_DESC *pDesc; /* IO pointer to descriptor */ BF_BLOCK *pBuf; /* IO buffer pointer */ BF_BLKNUM blockNum; /* I number of the block */ { int stat; /* status from calls */ off_t offset; /* offset into file, for lseek() */ off_t lseek(); /* lseek function */ assert(pDesc != NULL); assert(pBuf != NULL); assert(blockNum >= 0 && blockNum <= BfB0CurrentSize(pDesc)); if (!BfWriteable(pDesc)) { (void)fprintf(stderr, "bfWrite: file is O_RDONLY\n"); return ERROR; } /*---------------------------------------------------------------------------- * write the block and, if keepFlag isn't set, free the buffer *---------------------------------------------------------------------------*/ offset = blockNum * BfB0BlockSize(pDesc); while (1) { stat = lseek((int)pDesc->fd, offset, L_SET); if (stat == ERROR) { perror("bfWrite: error on lseek"); return ERROR; } stat = write((int)pDesc->fd, (char *)pBuf, (int)BfB0BlockSize(pDesc)); if (stat == BfB0BlockSize(pDesc)) return OK; #ifdef vxWorks perror("bfWrite: error writing block\n"); return ERROR; #else if (errno != EINTR) { perror("bfWrite: error writing block\n"); return ERROR; } #endif } }