int _er90b_write(struct fdinfo *fio, bitptr bufptr, int nbytes, struct ffsw *retstat, int fulp, int *ubc) { int ret; int nbt = 0; /* number of bytes transferred so far */ int nbreq; /* number of bytes requested this request */ char *buf; ER90BYT *f; int zero = 0; struct ffsw dumstat; buf = BPTR2CP(bufptr); if ((BPBITOFF(bufptr) & 7) != 0 || *ubc != 0) ERETURN(retstat, FDC_ERR_UBC, 0); nbreq = nbytes; if (fio->rwflag == POSITIN) { f = (ER90BYT *)fio->lyr_info; if (f->tpos) { ret = _tape_tpwait(f->fd, &(f->tpos)); if (ret < 0) ERETURN(retstat, errno, 0); } } else if (fio->rwflag == READIN) { /* write after read requires position to zero */ ret = _er90b_pos(fio, FP_RSEEK, &zero, 1, &dumstat); if (ret < 0) { *retstat = dumstat; return(ERR); } } if (nbreq > 0) { again: /* It is not safe to reissue the write if it fails */ /* with EINTR. Some data may have been transferred */ ret= write(fio->realfd, buf, nbreq); if (ret < 0) ERETURN(retstat, errno, nbt); nbt += ret; /* * The assumption is made here that the system will never return * zero bytes on a non-zero request without an error! */ if (nbt < nbytes) { buf += ret; nbreq -= ret; goto again; } } else if (nbytes < 0) ERETURN(retstat, FDC_ERR_REQ, 0); SETSTAT(retstat, FFCNT, nbt); fio->rwflag = WRITIN; return (nbt); }
ssize_t _sys_read( struct fdinfo *fio, bitptr bufptr, size_t nbytes, struct ffsw *retstat, int fulp, int *ubc) { ssize_t ret = 0; char *buf; buf = BPTR2CP(bufptr); if ((BPBITOFF(bufptr) & 7) != 0 || *ubc != 0) ERETURN(retstat, FDC_ERR_UBC, 0); #ifdef __mips /* * If our last i/o was asynchronous, then our file position * won't be what we expect. Seek to the right position. We * could use a pread instead of seeking, but that would also * not update the file position. I'm doing this because it seems * to me most 'expected' for the system call layer. */ if (((struct sys_f *)fio->lyr_info)->needpos) { if (lseek( fio->realfd, ((struct sys_f *)fio->lyr_info)->curpos, 0) < 0) ERETURN(retstat, errno, 0); ((struct sys_f *)fio->lyr_info)->needpos = 0; } #endif if (nbytes > 0) { if (((struct sys_f *)fio->lyr_info)->nointrio) ret = read(fio->realfd, buf, nbytes); else { LOOP_SYSCALL(ret, read(fio->realfd, buf, nbytes)); } if (ret < 0) ERETURN(retstat, errno, 0); } if (ret == 0 && nbytes > 0) { SETSTAT(retstat, FFEOD, ret); } else { SETSTAT(retstat, FFCNT, ret); #ifdef __mips ((struct sys_f *)(fio->lyr_info))->curpos += ret; #endif } return (ret); }
int _er90b_writea(struct fdinfo *fio, bitptr bufptr, int nbytes, struct ffsw *retstat, int fulp, int *ubc) { int ret = 0; char *buf; ER90BYT *f; int zero = 0; struct ffsw dumstat; buf = BPTR2CP(bufptr); if ((BPBITOFF(bufptr) & 7) != 0 || *ubc != 0) ERETURN(retstat, FDC_ERR_UBC, 0); if (fio->rwflag == POSITIN) { f = (ER90BYT *)fio->lyr_info; if (f->tpos) { ret = _tape_tpwait(f->fd, &(f->tpos)); if (ret < 0) ERETURN(retstat, errno, 0); } } else if (fio->rwflag == READIN) { /* write after read requires position to zero */ ret = _er90b_pos(fio, FP_RSEEK, &zero, 1, &dumstat); if (ret < 0) { *retstat = dumstat; return(ERR); } } if (nbytes > 0) { CLRFFSTAT(*retstat); /* flag async in progress */ ret= writea(fio->realfd, buf, nbytes, (struct iosw *)retstat, 0); if (ret < 0) ERETURN(retstat, errno, 0); } else if (nbytes < 0) { ERETURN(retstat, FDC_ERR_REQ, 0); } else { /* nbytes == 0 */ retstat->sw_flag = 1; FFSTAT(*retstat) = FFCNT; /* I/O is done, and other stat */ /* fields are already set. */ } fio->rwflag = WRITIN; return (ret); }
/* * The f77 layer makes no particular effort to be aligned in any way * within the file, other than trying to write/read full buffers. * As long as we are writing/reading sequentially, we should always * be aligned on a buffer boundary. Any positioning will destroy that. * Even with no positioning, we may have to seek back and write a control * word. This routine just attempts to make sure that the user's buffer * is memory aligned. * Returns: * 1 if address 'p' is aligned * 0 other wise */ static int _f77_aligned(char *p) { #ifdef _CRAYMPP long pl; /* Want it to be aligned on an 8-word boundary */ #define CACHE_MASK 0x3f pl = (long)p; if ((pl & CACHE_MASK) == 0) return(1); return(0); #elif _CRAY1 /* want it be aligned on a word boundary */ if (BPBITOFF(CPTR2BP(p)) == 0) return (1); return (0); #elif defined(__mips) || defined(_LITTLE_ENDIAN) return (1); /* We know of no particular alignment requirements */ /* except when O_DIRECT is specified. This layer */ /* is not set up for O_DIRECT now. */ #else return (1); #endif }
/* * Read a tape file. * With this format, one tape block == one record. * Parameters: * fio Pointer to fdinfo block * bufptr bit pointer to where data is to go. * nbytes Number of bytes to be read * stat pointer to status return word * fulp full or partial read mode flag * ubc pointer to unused bit count (not used this class of file) * */ ssize_t _tmf_read( struct fdinfo *fio, bitptr bufptr, size_t nbytes, struct ffsw *stat, int fulp, int *ubc) { register int errn; /* Error code */ size_t bytes; /* Number of bytes actually read */ ssize_t ret; /* Return status */ struct tmfio *xf_info; xf_info = (struct tmfio *)fio->lyr_info; if (*ubc != 0) { errn = FDC_ERR_UBC; goto eret; } if ((BPBITOFF(bufptr) & 07) != 0) { errn = FDC_ERR_REQ; goto eret; } if (xf_info->rwflag == WRITIN && !xf_info->tmf_speov) { /* Read after write is OK in special processing */ /* otherwise, it is an error. */ errn = FDC_ERR_RAWR; goto eret; } if (xf_info->tmf_tpos) { if (_tmf_tpwait(xf_info) < 0) { errn = errno; goto eret; } } xf_info->rwflag = READIN; if (xf_info->tmf_speov) { /* If we ever do async i/o, then we want to read */ /* synchronously while in special processing. */ if (xf_info->spblocks == 0) { /* We're not reading from the tape. */ /* We're reading from buffer memory */ goto spprocread; } } /* * If the number of bytes to read is zero and reading in full * record mode, skip to the end of record. If in partial * record mode, the position remains as is. * Are we at the beginning of the record? */ if (xf_info->tmf_cnt == 0) { /* * We are at the beginning of the record. Must read * from tape. Even if the user has requested a FULL * record, we need to read it in to the buffer, unless * the request is for bufsiz bytes. * That is because, if the user requests less than * is actually in the tape block, the read would * give an error. */ if (nbytes == xf_info->tmf_bufsiz) { LOOP_SYSCALL(ret,read(xf_info->tmf_fd, BPTR2CP(bufptr), xf_info->tmf_bufsiz)); bytes = ret; } else { LOOP_SYSCALL(ret,read(xf_info->tmf_fd, xf_info->tmf_bufptr, xf_info->tmf_bufsiz)); if (ret > 0) { bytes = MIN(ret, nbytes); (void) memcpy(BPTR2CP(bufptr), xf_info->tmf_bufptr, bytes); } } if (ret > 0) { if (ret == nbytes) { SETSTAT(stat, FFEOR, bytes); if (xf_info->spblocks > 0) xf_info->spblocks--; return(bytes); } else if (fulp == FULL) { SETSTAT(stat, FFCNT, bytes); if (xf_info->spblocks > 0) xf_info->spblocks--; return(bytes); } else { SETSTAT(stat, FFCNT, bytes); xf_info->tmf_bufptr += bytes; xf_info->tmf_cnt = ret - bytes; } } else if (ret == 0) { if (ioctl(xf_info->tmf_fd,TMFC_EOD,0)) { SETSTAT(stat, FFEOF, 0); return(0); } SETSTAT(stat, FFEOD, 0); return(0); } else { /* Could be EOV, or an error */ if (errno == ENOSPC) { /* If we hit physical eov, */ /* return an error */ /* and set eovhit so that */ /* checktp will tell us we */ /* hit eov */ /* JAS - should document that ffread will return ENOSPC at eov */ /* also Fortran read will return an error */ if (xf_info->tmf_eovon) xf_info->tmf_eovhit = 1; ERETURN(stat, errno, 0); } else { ERETURN(stat, errno, 0); } } } else { /* * We are in the middle of a record. The entire record, * and nothing more, is already in our buffer. */ bytes = MIN(nbytes, xf_info->tmf_cnt); (void) memcpy(BPTR2CP(bufptr), xf_info->tmf_bufptr, bytes); xf_info->tmf_cnt -= bytes; xf_info->tmf_bufptr += bytes; if (xf_info->tmf_cnt == 0) { xf_info->tmf_bufptr = xf_info->tmf_base; if (xf_info->spblocks > 0) xf_info->spblocks--; SETSTAT(stat, FFEOR, bytes); } else { SETSTAT(stat, FFCNT, bytes); if (fulp == FULL) { if (xf_info->spblocks > 0) xf_info->spblocks--; xf_info->tmf_bufptr = xf_info->tmf_base; xf_info->tmf_cnt = 0; } } } return (bytes); eret: ERETURN(stat, errn, 0); spprocread: /* We're reading from buffer memory. */ /* Right now, we've got only 1 block in buffer memory. */ if (xf_info->tmf_tpmk) { xf_info->tmf_tpmk = 0; SETSTAT(stat, FFEOF, 0); return(0); } else { if (xf_info->eovbytes == 0) { SETSTAT(stat, FFEOD, 0); return(0); } bytes = MIN(xf_info->eovbytes, nbytes); (void) memcpy(BPTR2CP(bufptr), xf_info->eovbuf, bytes); if (fulp == FULL || (nbytes >= xf_info->eovbytes)) { xf_info->eovbytes = 0; SETSTAT(stat, FFEOR, bytes); } else { xf_info->eovbytes -= nbytes; xf_info->eovbuf += nbytes; SETSTAT(stat, FFCNT, bytes); } return(bytes); } }
/* * This is the tape layer for Irix systems. * When the tape is in variable block mode, each user's record * corresponds to a block on tape. * This is accomplished by writing the record with 1 write statement. * The tape layer's buffer is big enough to hold 1 record. * If we get a full write, and nothing else is in the buffer for * this record, we can skip copying to the library buffer, and write it * directly from the user's space. * If we get a partial write, we need to copy to the library buffer. * Parameters: * fio - Pointer to fdinfo block * bufptr - bit pointer to where data is to go. * nbytes - Number of bytes to be written * stat - pointer to status return word * fulp - full or partial write mode flag * ubc - pointer to unused bit count (not used for IBM) */ ssize_t _tmf_write( struct fdinfo *fio, bitptr bufptr, size_t nbytes, struct ffsw *stat, int fulp, int *ubc) { register int errn; ssize_t ret; struct tmfio *xfinfo; if ((BPBITOFF(bufptr) & 07) != 0) { errn = FDC_ERR_REQ; goto eret; } if (*ubc != 0) { errn = FDC_ERR_UBC; goto eret; } xfinfo = (struct tmfio *)fio->lyr_info; /* * If we've been reading, then try to switch the buffer into write mode. */ if (xfinfo->rwflag == READIN) { /* * Issue an error if we are not positioned at a record * boundary. ffweof would terminate the current record, but * _cos_write overwrites the current record. We need to * decide which is the proper approach before permitting this * here. */ if (xfinfo->tmf_base != xfinfo->tmf_bufptr) { errn = FDC_ERR_NOTREC; goto eret; } ret = _tmf_wrard(fio, stat); if (ret < 0) return(ERR); } if (xfinfo->tmf_tpos) { if (_tmf_tpwait(xfinfo) < 0) { ERETURN(stat, errno, 0); } } xfinfo->rwflag = WRITIN; if (xfinfo->tmf_speov) { /* We're in special processing. */ /* Reset counter of blocks on tape */ xfinfo->spblocks = 0; /* If we've read anything from buffer memory, then mark it */ /* all gone. - for now we don't need to worry about */ /* this, because we can only have 1 block in buffer memory. */ /* But if we every do async i/o, this could be a problem. */ } if ((xfinfo->tmf_bufptr == xfinfo->tmf_base) && fulp == FULL) { /* * This is the entire record, so just write it out */ LOOP_SYSCALL(ret, write(xfinfo->tmf_fd, BPTR2CP(bufptr), nbytes)); if (ret != nbytes) { if (xfinfo->tmf_eovon && !xfinfo->tmf_speov) { /* The user has enabled eov processing. */ /* Determine whether we hit EOV. */ int err; if (_tmf_ateov(fio,xfinfo, nbytes, BPTR2CP(bufptr), ret, stat, &err)) { /* This is eov */ /* We need to save away the */ /* unwritten part of the data, */ /* and set a flag so we can */ /* tell the user eov was reached. */ /* This user's write will return */ /* a good status. */ return(_tmf_eovseen(xfinfo, nbytes, BPTR2CP(bufptr), ret, stat)); } if (err != 0) { ERETURN(stat, err, ret); } /* We were able to rewrite the block. */ /* Carry on. */ } else { if (ret < 0) { ERETURN(stat, errno, 0); } else{ ERETURN(stat, FDC_ERR_WRTERR, ret); } } } SETSTAT(stat, FFEOR, ret); return(ret); } /* * This must not be the entire record. So, we need to copy it * to our library buffer. */ if (nbytes + xfinfo->tmf_cnt > xfinfo->tmf_bufsiz) { ERETURN(stat, FDC_ERR_MXBLK, 0); } memcpy(xfinfo->tmf_bufptr, BPTR2CP(bufptr), nbytes); xfinfo->tmf_cnt += nbytes; xfinfo->tmf_bufptr += nbytes; if (fulp == FULL) { LOOP_SYSCALL(ret, write(xfinfo->tmf_fd, xfinfo->tmf_base, xfinfo->tmf_cnt)); xfinfo->tmf_bufptr = xfinfo->tmf_base; if (ret != xfinfo->tmf_cnt) { if (xfinfo->tmf_eovon && !xfinfo->tmf_speov) { int err; if (_tmf_ateov(fio,xfinfo, xfinfo->tmf_cnt, xfinfo->tmf_base, ret, stat, &err)) { /* This is eov */ /* We need to save away the */ /* unwritten part of the data, */ /* and set a flag so we can */ /* tell the user eov was reached. */ /* This write will return OK */ return(_tmf_eovseen(xfinfo, xfinfo->tmf_cnt, xfinfo->tmf_base, ret, stat)); } if (err != 0) { ERETURN(stat, err, ret); } /* We were able to rewrite the block. */ /* Carry on. */ } else { xfinfo->tmf_cnt = 0; if (ret < 0) { ERETURN(stat, errno, 0); } else{ ERETURN(stat, FDC_ERR_WRTERR, ret); } } } xfinfo->tmf_cnt = 0; SETSTAT(stat, FFEOR, nbytes); return(nbytes); } else { SETSTAT(stat, FFCNT, nbytes ); return(nbytes); } eret: ERETURN(stat, errn, 0); }
/* * Write a f77 class file * Parameters: * fio - Pointer to fdinfo block * bufptr - bit pointer to where data is to go. * nbytes - Number of bytes to be written * stat - pointer to status return word * fulp - full or partial write mode flag * ubc - pointer to unused bit count (not used for IBM) */ ssize_t _f77_xwrite( struct fdinfo *fio, bitptr bufptr, size_t nbytes, struct ffsw *stat, int fulp, int *ubc) { ssize_t ret; size_t bytomove, moved, bytes; struct f77_xf *xfinfo; struct fflistreq list_array[1]; long left; char *cbufptr; long ii; char *cb; int ijk; int cwbytes; int ernum; int zero = 0; cbufptr = BPTR2CP(bufptr); if ((BPBITOFF(bufptr) & 07) != 0) { ernum = FDC_ERR_REQ; goto eret; } if (*ubc != 0){ ernum = FDC_ERR_UBC; goto eret; } xfinfo = (struct f77_xf *)fio->lyr_info; /* * If we've been reading, then try to switch the buffer into write mode. */ if (fio->rwflag == READIN) { /* * Issue an error if we are not positioned at a record * boundary. ffweof would terminate the current record, but * _cos_write overwrites the current record. We need to * decide which is the proper approach before permitting this * here. */ if (!(xfinfo->flag & ATEOR) && !(fio->ateod ) && !(fio->ateof)) { ernum = FDC_ERR_NOTREC; goto eret; } ret = f77_xwrard(fio, stat); if (ret < 0) return(ERR); } fio->rwflag = WRITIN; /* * initialize a new record, if needed. */ bytomove = nbytes; moved = 0; /* * Check for record size exceeded. */ if (bytomove > 0) { if ((xfinfo->maxrecsize > 0) && (xfinfo->recbytes + bytomove) > xfinfo->maxrecsize){ ernum = FDC_ERR_MXREC; goto eret; } } if (xfinfo->recbytes == 0) { /* This is the start of the record */ ii = bytomove; if (fio->rtype == TR_UX_MIPS) {SWAPB(ii);} if ((bytomove > 0) || (fulp == FULL)) { /* * Put our guess at a control word in the buffer. * This is the control word at the beginning of record. */ cwbytes = RDWLEN; cb = (char *)ⅈ #if !(defined(_LITTLE_ENDIAN) && defined(_LP64)) cb += sizeof(ii) - RDWLEN; /* The control word is only RDWLEN bytes long */ #endif if ((xfinfo->_cnt + RDWLEN) >= xfinfo->_ffbufsiz) { /* only part of the control word will fit */ /* in this buffer. Insert what will fit. */ for (ijk = 0; ijk < xfinfo->_ffbufsiz - xfinfo->_cnt; ijk++){ *(xfinfo->_ptr++) = *cb++; cwbytes--; } /* buffer is full. write it out. */ if (_f77_put_block(fio, stat, (size_t)xfinfo->_ffbufsiz) != 0) return(ERR); } for (ijk = 0; ijk < cwbytes; ijk++){ *(xfinfo->_ptr++) = *cb++; } xfinfo->_cnt += cwbytes; xfinfo->recbytes += RDWLEN; xfinfo->cwwritten = 1; } } else { /* This record has already been started. */ ii = (xfinfo->recbytes + bytomove - RDWLEN) ; if (fio->rtype == TR_UX_MIPS) {SWAPB(ii);} if (bytomove != 0) { /* * If the control word at the start of the * record is in the buffer, update it. */ if (xfinfo->recbytes <= xfinfo->_cnt){ char *tbptr; /* the whole control word is in the buffer */ cb = (char *)ⅈ #if !(defined(_LITTLE_ENDIAN) && defined(_LP64)) cb += sizeof(ii) - RDWLEN; /* The control word is only RDWLEN bytes long */ #endif tbptr = xfinfo->_ptr - xfinfo->recbytes; for (ijk = 0; ijk < RDWLEN; ijk++) *(tbptr++) = *cb++; xfinfo->cwwritten = 1; } else if ((xfinfo->recbytes - RDWLEN) <= xfinfo->_cnt){ char *tbptr; int istart; /* part of the control word is in the buffer */ /* Insert what will fit. */ cb = (char *)ⅈ #if !(defined(_LITTLE_ENDIAN) && defined(_LP64)) cb += sizeof(ii) - RDWLEN; /* The control word is only RDWLEN bytes long */ #endif istart = xfinfo->recbytes -xfinfo->_cnt; cb += istart; tbptr = xfinfo->_base; for (ijk = istart; ijk < RDWLEN; ijk++) *(tbptr++) = *cb++; xfinfo->cwwritten = 0; /* 0 because this is */ /* not the whole thing*/ } else xfinfo->cwwritten = 0; } } /* * loop putting data in buffer */ while (bytomove > 0) { /* * bytes tells when data has been moved. Set it to zero * unless someone moves some data in the loop */ /* * If enough room for bytes, put them in the buffer */ left = xfinfo->_ffbufsiz - xfinfo->_cnt; if (left == 0) { if (_f77_put_block(fio, stat, (size_t)xfinfo->_cnt) != 0) return(ERR); left = xfinfo->_ffbufsiz; #ifdef __CRAY #pragma _CRI inline _f77_aligned #elif defined(__mips) || defined(_LITTLE_ENDIAN) #pragma inline _f77_aligned #endif if ((bytomove >= left) && _f77_aligned(cbufptr)) { /* We write directly from the user's buffer */ bytes = bytomove - bytomove%xfinfo->_ffbufsiz; ret = XRCALL(fio->fioptr, writertn) fio->fioptr, CPTR2BP(cbufptr), bytes, stat, PARTIAL, &zero); if (ret != bytes){ return(ERR); } bytomove -= bytes; cbufptr += bytes; moved += bytes; } } bytes = (bytomove < left)? bytomove : left; memcpy(xfinfo->_ptr, cbufptr, bytes); xfinfo->_cnt += bytes; xfinfo->_ptr += bytes; cbufptr += bytes; bytomove -= bytes; moved += bytes; }
ssize_t _sys_write( struct fdinfo *fio, bitptr bufptr, size_t nbytes, struct ffsw *retstat, int fulp, int *ubc) { ssize_t ret; ssize_t nbt = 0; /* number of bytes transferred so far */ size_t nbreq; /* number of bytes requested this request */ char *buf; buf = BPTR2CP(bufptr); if ((BPBITOFF(bufptr) & 7) != 0 || *ubc != 0) ERETURN(retstat, FDC_ERR_UBC, 0); nbreq = nbytes; #ifdef __mips /* * If our last i/o was asynchronous, then our file position * won't be what we expect. Seek to the right position. We * could use a pwrite instead of seeking, but that would also * not update the file position. I'm doing this because it seems * to me most 'expected' for the system call layer. */ if (((struct sys_f *)fio->lyr_info)->needpos) { if (lseek( fio->realfd, ((struct sys_f *)fio->lyr_info)->curpos, 0) < 0) ERETURN(retstat, errno, nbt); ((struct sys_f *)fio->lyr_info)->needpos = 0; } #endif if (nbreq > 0) { #ifdef __mips if (((struct sys_f *)fio->lyr_info)->oappend) { ((struct sys_f *)fio->lyr_info)->curpos = ((struct sys_f *)fio->lyr_info)->endpos; } #endif again: if (((struct sys_f *)fio->lyr_info)->nointrio) ret = write(fio->realfd, buf, nbreq); else { LOOP_SYSCALL(ret, write(fio->realfd, buf, nbreq)); } if (ret < 0) ERETURN(retstat, errno, nbt); #ifdef __mips ((struct sys_f *)fio->lyr_info)->curpos += ret; if (((struct sys_f *)fio->lyr_info)->curpos > ((struct sys_f *)fio->lyr_info)->endpos) ((struct sys_f *)fio->lyr_info)->endpos = ((struct sys_f *)fio->lyr_info)->curpos; #endif nbt += ret; /* * The assumption is made here that the system will never return * zero bytes on a non-zero request without an error! */ if (nbt < nbytes) { buf += ret; nbreq -= ret; goto again; } } SETSTAT(retstat, FFCNT, nbt); return (nbt); }
/* * _cch_write * * Process write requests for the cache layer. * * Return value: * * The number of bytes transferred is returned upon successful completion. * If an error occurs, -1 is returned. * * The stat->sw_stat field is set to FFCNT upon normal return. */ ssize_t _cch_write( struct fdinfo *fio, /* ffio file descriptor. */ bitptr datptr, /* bit pointer to the user's data. */ size_t nbytes, /* Nuber of bytes to be written. */ struct ffsw *stat, /* pointer to status return word */ int fulp, /* full or partial write mode flag */ int *ubcp /* pointer to unused bit count. On return, */ /* *ubcp is updated to contain the unused bit */ /* count in the data returned. */ ) { off_t cpos; /* bit position in file */ int64 moved; /* number of bits transfered */ int64 bytes_moved; /* number of bytes transfered */ int64 morebits; /* bits moved in current iteration */ int64 numblocks; /* num of pages to process this iter */ int pgoff; off_t fileaddr; off_t eofaddr; int gb_rd; /* nonzero if pages must be read */ int valid; /* nonzero if CCH_VALIDBUFFER should */ /* be set */ int64 nbits; int64 i; int bs, nbu; off_t olpos, endpos, endoff; bitptr toptr; struct ffsw locstat; struct fdinfo *llfio; struct cch_f *cch_info; struct cch_buf *cubuf; int err; short firsteof = 0; short setfirst; CCH_DEBUG(("_cch_write EN: nbytes=%d fulp=%d ubc=%d\n",nbytes,fulp, *ubcp)); CLRSTAT(locstat); cch_info = (struct cch_f *)fio->lyr_info; nbits = BYTES2BITS(nbytes) - *ubcp; fio->rwflag = WRITIN; #if defined(__mips) || defined(_LITTLE_ENDIAN) /* Although this layer is capable of handling non-zero ubc */ /* and bitptrs that aren't on a byte boundary, we are not */ /* supporting this right now on mips systems. */ if (*ubcp != 0) { err = FDC_ERR_UBC; goto err1_ret; } if ((BPBITOFF(datptr) & 07) != 0) { err = FDC_ERR_REQ; goto err1_ret; } #endif if (nbits == 0) { /* quick return for nbits == 0*/ SETSTAT(stat, FFCNT, 0); return(0); } /* * Move data from user to buffer */ llfio = fio->fioptr; bs = cch_info->bsize; /* bit size of each buffer */ cpos = cch_info->cpos; /* current file position */ olpos = cpos; /* save original position */ fileaddr = CCHFLOOR(cpos,bs); /* bit offset within the file of the * start of the current page */ if (cpos > cch_info->fsize) { firsteof = 1; /* Is the page with eof in memory? */ /* If so, zero out the portion beyond eof. */ eofaddr = CCHFLOOR(cch_info->fsize, bs); CCH_FINDBLK(cch_info, eofaddr, cubuf); if (cubuf != NULL && (cubuf->flags & CCH_ZEROED) == 0) { #ifdef CCH_SDS_SUPPORTED if (cch_info->optflags & CCHOPT_SDS) { /* should never happen */ ERETURN(stat, FDC_ERR_INTERR, 0); } #endif pgoff = cch_info->fsize - eofaddr; /* offset of eof */ /* within the page */ SET_BPTR(toptr, INC_BPTR(cubuf->buf, pgoff)); morebits = bs - pgoff; if (morebits != 0) { CCH_MEMCLEAR(toptr, morebits); } cubuf->flags |= CCH_ZEROED; } } while (nbits > 0) { /* * Find the cache buffer assigned to the current page. If * no buffer is currently assigned, then _cch_getblk assigns * one. */ pgoff = cpos - fileaddr; /* offset within the page */ numblocks = 1; /* number of of pages to prcess * in this iteration */ CCH_FINDBLK(cch_info, fileaddr, cubuf); if (cubuf == NULL) { /* if data not buffer-resident*/ if (nbits > cch_info->bypasssize #ifdef CCH_SDS_SUPPORTED && !(cch_info->optflags & CCHOPT_SDS) #endif ) { /* Maybe we can bypass buffering */ if ((morebits= _cch_bypass(cch_info, nbits, cpos, datptr, fileaddr, 'w', llfio, &locstat))>0) goto adjust; else if (morebits < 0) { /* Is it right to return the count */ /* in locstat? Because we might */ /* have read some data... */ goto er1; } /* we weren't able to bypass buffering */ } morebits = nbits; endpos = cpos + morebits; /*1 bit past the end*/ endoff = endpos - CCHFLOOR(endpos,bs); if (endpos > fileaddr + bs) { numblocks = (endpos-fileaddr-1)/bs + 1; nbu = cch_info->nbufs; /* * Handle at most a cache full at a time */ if (numblocks > nbu) { numblocks = nbu; endpos = fileaddr + nbu * bs; endoff = 0; morebits = endpos - cpos; } } /* * It is possible that the first or last * page must be read because the transfer * fills only part of these pages. In each * iteration, _cch_getblk requires that * consecutive buffer pages must all be read, * or else all be assigned without pre-reading. * The following code breaks off the current * portion of the transfer when necessary to * accomplish this. */ if (numblocks > 1) { if (numblocks == 2) { if ((pgoff == 0) != (endoff == 0)) { /* process only first page */ numblocks = 1; endoff = 0; morebits = bs - pgoff; } } else { if (pgoff) { /* process only first page */ numblocks = 1; endoff = 0; morebits = bs - pgoff; } else if (endoff) { /* process all but last page */ numblocks -= 1; endoff = 0; morebits -= endoff; } } } /* * Request that _cch_getblk read in the file * pages if partial pages of data will be * written. */ gb_rd = (pgoff || endoff); /* The pages will be valid if we do not */ /* have to read them. That's because */ /* we will be writing to the entire page */ /* The page will also be valid if we do read it */ valid = 1; setfirst = 0; if (gb_rd && #ifdef CCH_SDS_SUPPORTED !(cch_info->optflags & CCHOPT_SDS) && #endif (numblocks == 1) && ((fileaddr+bs) < cch_info->feof) && (_CCH_ALIGN(pgoff) && _CCH_ALIGN(endoff))) { /* do we really need to read the page in? */ /* if pgoff and endoff are properly aligned, */ /* we do not */ /* Note that if any part of the page is */ /* beyond feof, we want to read it in. */ /* That's because code in _cch_rdabuf */ /* that handles having a partially dirty */ /* page expects to be able to read the */ /* data preceding the dirty data */ gb_rd = 0; valid = 0; /* the page will not be valid */ setfirst = 1; } cubuf = _cch_getblk(cch_info, llfio, fileaddr, &numblocks, gb_rd, valid, &locstat); if (cubuf == NULL) { goto er1; } if (setfirst) { cubuf->firstdata = pgoff; if (endoff == 0) cubuf->lastdata = bs; else cubuf->lastdata = endoff; } if (firsteof && pgoff != 0) { /* There is a gap between the eof and */ /* this data. Zero it if necessary. */ if ((cubuf->flags & CCH_ZEROED) == 0) { int zbits; #ifdef CCH_SDS_SUPPORTED if (cch_info->optflags & CCHOPT_SDS) { /* should never happen */ ERETURN(stat, FDC_ERR_INTERR, 0); } #endif if ((eofaddr == fileaddr)) { /* the eof is on this page */ zbits = bs - (cch_info->fsize - eofaddr); SET_BPTR(toptr, INC_BPTR(cubuf->buf, (cch_info->fsize - eofaddr))); } else { /* the eof is not on this page */ /* zero the entire page */ zbits = bs; toptr = cubuf->buf; } CCH_MEMCLEAR(toptr, zbits); cubuf->flags |= CCH_ZEROED; } } morebits = MIN(nbits, bs * numblocks - pgoff); /* remember the last buffer page for next time */ cch_info->cubuf = cubuf + numblocks - 1; } else { morebits = MIN(nbits, bs - pgoff); if (!(cubuf->flags & CCH_VALIDBUFFER)) { /* The buffer is there, but it */ /* is not entirely valid, because */ /* we never read into it. */ /* We can continue to just dirty it, */ /* provided that the dirty part is */ /* contiguous, and is properly aligned */ endoff = pgoff + morebits; if ((pgoff == cubuf->lastdata && _CCH_ALIGN(endoff))|| (endoff == cubuf->firstdata && _CCH_ALIGN(pgoff)) || (pgoff >= cubuf->firstdata && endoff <= cubuf->lastdata)) { cubuf->firstdata = MIN(pgoff, cubuf->firstdata); cubuf->lastdata = MAX(endoff, cubuf->lastdata); if (cubuf->firstdata == 0 && cubuf->lastdata == bs) { cubuf->lastdata = 0; cubuf->flags |=CCH_VALIDBUFFER; } } else { /* We can't just keep on putting */ /* stuff in the buffer without */ /* prereading it. So, we will call */ /* _cch_rdabuf, which has the */ /* smarts to read only the non-dirty */ /* parts */ if (_cch_rdabuf(cch_info, llfio, cubuf, BITS2BYTES(cch_info->bsize), BITS2BYTES(cubuf->filead), 1, 's', &locstat)) { goto er1; } } } } for (i=0; i<numblocks; i++) { /* adjust last access time */ CCH_CHRONOMETER(cubuf[i],cch_info); cubuf[i].flags |= CCH_DIRTY; } SET_BPTR(toptr, INC_BPTR(cubuf->buf, pgoff)); #ifdef CCH_SDS_SUPPORTED if (cch_info->optflags & CCHOPT_SDS) { if (_sds_fr_mem(toptr, datptr, morebits) == ERR) ERETURN(stat, errno, 0); } else _CCH_MOV_BITS(toptr, datptr, morebits); /* contiguous bufs */ #else _CCH_MOV_BITS(toptr, datptr, morebits); /* contiguous bufs */ #endif adjust: SET_BPTR(datptr, INC_BPTR(datptr, morebits)); cpos += morebits; nbits -= morebits; fileaddr = CCHFLOOR(cpos,bs); /* bit offset within the file of the page */ firsteof = 0; if (cpos > cch_info->fsize) { cch_info->fsize = cpos; } } cch_info->cpos = cpos; moved = cpos - olpos; fio->recbits += moved; bytes_moved = BITS2BYTES(moved); SETSTAT(stat, FFCNT, bytes_moved); return(bytes_moved); err1_ret: ERETURN(stat, err, 0); er1: *stat = locstat; return(ERR); }