Files
pcas/src/libCom/bfSubr.c
1994-07-13 02:29:43 +00:00

1585 lines
50 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* $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
*
* <bfDefs.h> <bfDefs.h>
* <stdio.h> <vxWorks.h>
* <sys/file.h> <ioLib.h>
* <stdioLib.h>
*
* 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, <bufDim, <>pBfDesc )
* returns pBuf or NULL
* int bfRingFputs(<pBuf, <>pBfDesc )
* returns last character or EOF
* int BfRingGetc(<>pDesc )
* returns character or EOF
* int BfRingPutc( <c, <>pBfDesc )
* returns character or EOF
*
*BF_BLKNUM bfAcquire( <>pBfDesc, <>pBfBlock )
* returns blockNum or ERROR
* int bfClose( <>ppBfDesc )
* BF_DESC *bfCreate( <name, <type, <nblk, <blkSize, <flags, <maxNblk )
* int bfDumpChar( <pBfDesc, <blockNum )
* int bfDumpHex( <pBfDesc, <blockNum )
* int bfInfo( <name )
* BF_DESC *bfOpen( <name, <mode, <type, <blkSize )
* int bfRead( <pBfDesc, <>pBfBlock, <blockNum )
* int bfRelease( <>pBfDesc, <>pBfBlock, <blockNum )
* int bfWrite( <pBfDesc, <>pBfBlock, <blockNum )
*---------------------------------------------------------------------------*/
#define BF_TEXT_GLBLSOURCE /* must precede incl bfDefs.h */
#include <genDefs.h>
#include <bfDefs.h>
#include <ctype.h>
#ifdef vxWorks
# include <vxWorks.h>
# include <ioLib.h>
# include <stdioLib.h>
typedef long off_t; /* for lseek() */
#else
# include <errno.h>
# include <stdio.h>
# include <sys/types.h>
# include <sys/file.h>
#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; i<nBytes; i++)
* ((char *)pBuf)[++pBuf->lastByte] = 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++<nBlocks; ) {
pBuf->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; i<pBuf->lastByte; 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
}
}