static int join_branch ( tree_s *tree, branch_s *parent, branch_s *child, int k) { branch_s *sibling; sibling = bget(tree->t_dev, parent->br_key[k+1].k_block); if (!sibling) { return qERR_NOT_FOUND; } if (child->br_num+sibling->br_num < KEYS_PER_BRANCH-1) { FN; child->br_key[child->br_num].k_key = parent->br_key[k+1].k_key; child->br_key[child->br_num].k_block = sibling->br_first; ++child->br_num; memmove( &child->br_key[child->br_num], sibling->br_key, sibling->br_num * sizeof(sibling->br_key[0])); child->br_num += sibling->br_num; bdirty(child); memmove( &parent->br_key[k+1], &parent->br_key[k+2], sizeof(parent->br_key[0]) * (parent->br_num - (k+2))); --parent->br_num; bdirty(parent); } bput(sibling); // Sibling should be freed. return 0; }
/* Find a free entity in the bitmap starting at block BMAP-START on device DEV which contains BMAP-LEN bits. Sets the free bit it finds then returns the bit number, or NO_FREE_BLOCK if an error or all bits are set. */ blkno bmap_alloc(struct fs_device *dev, blkno bmap_start, u_long bmap_len) { blkno bmap_blk = bmap_start; while(bmap_len > 0) { int len, bit; struct buf_head *buf = bread(dev, bmap_blk); if(buf == NULL) return -1; len = min(bmap_len, FS_BLKSIZ * 8); FORBID(); bit = find_zero_bit(buf->buf.bmap, len); if(bit != -1) { set_bit(buf->buf.bmap, bit); PERMIT(); bdirty(buf, TRUE); brelse(buf); return ((bmap_blk - bmap_start) * FS_BLKSIZ * 8) + bit; } PERMIT(); brelse(buf); bmap_len -= len; bmap_blk++; } ERRNO = E_NOSPC; return NO_FREE_BLOCK; }
bool split_leaf (void *self, branch_s *parent, int k, unint len) { leaf_s *child = self; leaf_s *sibling; int upper; u64 upper_key; FN; if (!leaf_is_full(child, len)) { return FALSE; } sibling = new_leaf(bdev(parent)); upper = (child->l_num + 1) / 2; upper_key = child->l_rec[upper].r_key; copy_recs(sibling, child, upper); child->l_num = upper; child->l_total += (BLK_SIZE - sizeof(leaf_s)) - sibling->l_total; bdirty(child); bput(child); insert_sibling(parent, sibling, k, upper_key); bput(sibling); return TRUE; }
/* * ino_dirty() * Purge a directory entry back to the fs * * Whenever we have completed a series of modifications to some inode * data we need to write them back to the disk in the directory. */ void ino_dirty(struct inode *i) { struct dirent *d; void *handle; /* * First off grab the directory info off the disk */ handle = bget(i->i_dirblk); if (handle == NULL ) { perror("bfs ino_dirty"); exit(1); } d = (struct dirent *)((char *)bdata(handle) + i->i_diroff); /* * Then write the new information into the buffer */ memcpy(d->d_name, i->i_name, BFSNAMELEN); d->d_inum = i->i_num; d->d_start = i->i_start; d->d_len = i->i_fsize; /* * Then mark the buffer dirty and free the space */ bdirty(handle); bfree(handle); }
bool split_branch (void *self, branch_s *parent, int k) { branch_s *child = self; branch_s *sibling; int upper; int midpoint; u64 midpoint_key; FN; if (!branch_is_full(child)) return FALSE; sibling = new_branch(bdev(child)); midpoint = child->br_num / 2; upper = midpoint + 1; sibling->br_first = child->br_key[midpoint].k_block; midpoint_key = child->br_key[midpoint].k_key; memmove(sibling->br_key, &child->br_key[upper], sizeof(key_s) * (child->br_num - upper)); sibling->br_num = child->br_num - upper; child->br_num = midpoint; bdirty(child); bput(child); insert_sibling(parent, sibling, k, midpoint_key); bput(sibling); return TRUE; }
static int join_leaf ( tree_s *tree, branch_s *parent, leaf_s *child, int k) { leaf_s *sibling; sibling = bget(tree->t_dev, parent->br_key[k+1].k_block); if (!sibling) return qERR_NOT_FOUND; if (child->l_total + sibling->l_total > MAX_FREE) { FN; compact(child); compact(sibling); copy_recs(child, sibling, 0); memmove( &parent->br_key[k+1], &parent->br_key[k+2], sizeof(parent->br_key[0]) * (parent->br_num - (k+2))); --parent->br_num; bdirty(parent); } //verify_leaf(child, WHERE); bput(sibling); // Should free sibling return 0; }
buf_s *alloc_block (dev_s *dev) { super_s *super; buf_s *buf; u64 blkno; FN; buf = bget(dev, SUPER_BLOCK); super = buf->b_data; if (super->sp_magic != SUPER_MAGIC) { eprintf("no super block"); } blkno = super->sp_next++; PRd(blkno); bdirty(buf); bput(buf); buf = bnew(dev, blkno); bdirty(buf); return buf; }
/* * blk_alloc() * Request an existing file have its allocation increased * * Returns 0 on success, 1 on failure. */ int blk_alloc(struct inode *i, uint newsize) { /* * If the start is 0, it's "truncated" or new, so we update * it to the current place our free blocks start, and put it into * the block allocation list. */ if (i->i_start == 0) { ASSERT_DEBUG(i->i_fsize == 0, "blk_alloc: trunc with length"); i->i_start = spc_inode->i_start + BLOCKS(spc_inode->i_fsize); ino_addblklist(i); } /* * We now decide what alterations need to be made to the block * allocations within the inode. First we check that we have enough * space available to allocate the required data */ if (BLOCKS(newsize) > i->i_blocks) { /* * We really ought to be far more intelligent about what * we do here - my ideas so far are to first try and * grab some space from a near neighbour - if one has some. * Next we try moving the file (or if a neighbour's smaller * and freeing the space will be enough we move the * neighbour) to the largest free slot available (if that's * big enough) and finally if all else fails we'll compact * the whole fs down! */ return 1; } /* * OK, so there's enough space to allocate the required blocks. * Now we juggle the block details within the inode to reflect the * changes */ i->i_fsize = newsize; /* * Mark the inode dirty. Superblock probably also dirty; mark it so. */ ino_dirty(i); bdirty(shandle); return 0; }
void delete_leaf (leaf_s *leaf, u64 key) { rec_s *r; FN; r = find_rec(leaf, key); if (r) { leaf->l_total += sizeof(rec_s) + r->r_len; memmove(r, r + 1, (char *)&leaf->l_rec[--leaf->l_num] - (char *)r); bdirty(leaf); return; } printf("Key not found %llu\n", key); exit(2); }
void insert_sibling (branch_s *parent, void *sibling, int k, u64 key) { int slot; FN; /* make a hole -- only if not at end */ slot = k + 1; if (slot < parent->br_num) { memmove( &parent->br_key[slot+1], &parent->br_key[slot], sizeof(key_s) * (parent->br_num - slot)); } parent->br_key[slot].k_key = key; parent->br_key[slot].k_block = bblkno(sibling); ++parent->br_num; bdirty(parent); }
static void init_super_block (tree_s *tree) { super_s *super; buf_s *buf; buf = bget(tree->t_dev, SUPER_BLOCK); super = buf->b_data; if (super->sp_magic != SUPER_MAGIC) { super->sp_magic = SUPER_MAGIC; super->sp_root = 0; super->sp_next = SUPER_BLOCK + 1; bdirty(buf); } bput(buf); }
int change_root_string (tree_s *tree, buf_s *root) { super_s *super; buf_s *buf; FN; buf = bget(tree->t_dev, SUPER_BLOCK); if (!buf) { eprintf("change_root_string: no super block"); return qERR_NOT_FOUND; } super = buf->b_data; super->sp_root = root->b_blknum; bdirty(buf); bput(buf); return 0; }
void compact (leaf_s *leaf) { static char block[BLK_SIZE]; leaf_s *p; FN; // Need a spin lock here p = (leaf_s *)block; bzero(p, BLK_SIZE); p->l_type = leaf->l_type; p->l_end = BLK_SIZE; p->l_total = MAX_FREE; copy_recs(p, leaf, 0); memmove(leaf, p, BLK_SIZE); aver(leaf->l_total == free_space(leaf)); bdirty(leaf); }
u64 alloc_ino (dev_s *dev) { super_s *super; buf_s *buf; u64 ino; FN; buf = bget(dev, SUPER_BLOCK); super = buf->b_data; if (super->sp_magic != SUPER_MAGIC) { eprintf("no super block"); } ino = super->sp_ino++; bdirty(buf); bput(buf); return ino; }
static void init_super_block (void) { super_s *super; buf_s *buf; FN; buf = bget(Inode_tree.t_dev, SUPER_BLOCK); super = buf->b_data; if (super->sp_magic != SUPER_MAGIC) { super->sp_magic = SUPER_MAGIC; super->sp_root = 0; super->sp_next = SUPER_BLOCK + 1; super->sp_ino = ROOT_INO + 1; bdirty(buf); } bput(buf); init_root_inode(); }
static int redo_alloc_block (log_s *log, logrec_s *logrec) { super_s *super; buf_s *buf; FN; buf = bget(log->lg_sys, SUPER_BLOCK); if (!buf) { eprintf("redo_alloc_block: no super block"); return qERR_NOT_FOUND; } super = buf->b_data; if (logrec->lr_lsn <= super->sp_lsn) { bput(buf); return 0; } super->sp_lsn = logrec->lr_lsn; super->sp_next = logrec->lr_user[0]; bdirty(buf); bput(buf); return 0; }
/* Mark the entity at bit number BIT of the bitmap starting at block BMAP-START of device DEV. Return TRUE if no errors occurred. */ bool bmap_free(struct fs_device *dev, blkno bmap_start, u_long bit) { blkno bmap_blk = (bit / (FS_BLKSIZ * 8)) + bmap_start; struct buf_head *buf = bread(dev, bmap_blk); if(buf == NULL) return FALSE; bit = bit % (FS_BLKSIZ * 8); if(!test_bit(buf->buf.bmap, bit)) { kprintf("fs: Oops, freeing a free bit (%u) in bitmap %u\n", bit, bmap_start); } else { clear_bit(buf->buf.bmap, bit); bdirty(buf, TRUE); } brelse(buf); return TRUE; }
int insert_leaf (tree_s *tree, leaf_s *leaf, u64 key, void *rec, unint len) { rec_s *r; int total = len + sizeof(rec_s); FN; r = leaf_search(leaf, key); if (found(leaf, r, key)) { bput(leaf); return qERR_DUP; } memmove(r + 1, r, (char *)&leaf->l_rec[leaf->l_num] - (char *)r); ++leaf->l_num; leaf->l_total -= total; leaf->l_end -= len; r->r_start = leaf->l_end; r->r_len = len; r->r_key = key; pack_rec(tree, (u8 *)leaf + r->r_start, rec, len); bdirty(leaf); bput(leaf); return 0; }
/* * blk_trunc() * Remove the blocks from under the named file * * The freed blocks are allocated back onto the previous inode's managed * list. Note that we don't mark the inode dirty. This is because the * caller will often *further* dirty the inode, so no need to do it twice. */ void blk_trunc(struct inode *i) { int blocks; /* * Add blocks worth of storage back onto free count */ blocks = BLOCKS(i->i_fsize); sblock->s_free += blocks; /* * Flag start/len as 0. blk_alloc() will notice this * and update where the free blocks start. */ i->i_start = i->i_fsize = 0; bdirty(shandle); /* * Sort out all references to our former block neighbours */ if (i->i_prev != I_FREE) { ilist[i->i_prev]->i_next = i->i_next; ilist[i->i_prev]->i_blocks += i->i_blocks; if (i == spc_inode) { spc_inode = ilist[i->i_prev]; } } if (i->i_next != I_FREE ) { ilist[i->i_next]->i_prev = i->i_prev; } /* * Finally remove references to our former neighbours */ i->i_next = I_FREE; i->i_prev = I_FREE; }
/* * Do an I/O operation to/from a cache block. */ int smbfs_doio(struct vnode *vp, struct bio *bio, struct ucred *cr, struct thread *td) { struct buf *bp = bio->bio_buf; struct smbmount *smp = VFSTOSMBFS(vp->v_mount); struct smbnode *np = VTOSMB(vp); struct uio uio, *uiop = &uio; struct iovec io; struct smb_cred scred; int error = 0; uiop->uio_iov = &io; uiop->uio_iovcnt = 1; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_td = td; smb_makescred(&scred, td, cr); if (bp->b_cmd == BUF_CMD_READ) { io.iov_len = uiop->uio_resid = (size_t)bp->b_bcount; io.iov_base = bp->b_data; uiop->uio_rw = UIO_READ; switch (vp->v_type) { case VREG: uiop->uio_offset = bio->bio_offset; error = smb_read(smp->sm_share, np->n_fid, uiop, &scred); if (error) break; if (uiop->uio_resid) { size_t left = uiop->uio_resid; size_t nread = (size_t)bp->b_bcount - left; if (left > 0) bzero((char *)bp->b_data + nread, left); } break; default: kprintf("smbfs_doio: type %x unexpected\n",vp->v_type); break; }; if (error) { bp->b_error = error; bp->b_flags |= B_ERROR; } } else { /* write */ KKASSERT(bp->b_cmd == BUF_CMD_WRITE); if (bio->bio_offset + bp->b_dirtyend > np->n_size) bp->b_dirtyend = np->n_size - bio->bio_offset; if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = (size_t)(bp->b_dirtyend - bp->b_dirtyoff); uiop->uio_offset = bio->bio_offset + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; error = smb_write(smp->sm_share, np->n_fid, uiop, &scred); /* * For an interrupted write, the buffer is still valid * and the write hasn't been pushed to the server yet, * so we can't set BIO_ERROR and report the interruption * by setting B_EINTR. For the async case, B_EINTR * is not relevant, so the rpc attempt is essentially * a noop. For the case of a V3 write rpc not being * committed to stable storage, the block is still * dirty and requires either a commit rpc or another * write rpc with iomode == NFSV3WRITE_FILESYNC before * the block is reused. This is indicated by setting * the B_DELWRI and B_NEEDCOMMIT flags. */ if (error == EINTR || (!error && (bp->b_flags & B_NEEDCOMMIT))) { crit_enter(); bp->b_flags &= ~(B_INVAL|B_NOCACHE); if ((bp->b_flags & B_PAGING) == 0) bdirty(bp); bp->b_flags |= B_EINTR; crit_exit(); } else { if (error) { bp->b_flags |= B_ERROR; bp->b_error = error; } bp->b_dirtyoff = bp->b_dirtyend = 0; } } else { bp->b_resid = 0; biodone(bio); return 0; } } bp->b_resid = uiop->uio_resid; biodone(bio); return error; }
int fuse_io_strategy(struct vnode *vp, struct buf *bp) { struct fuse_filehandle *fufh; struct fuse_vnode_data *fvdat = VTOFUD(vp); struct ucred *cred; struct uio *uiop; struct uio uio; struct iovec io; int error = 0; const int biosize = fuse_iosize(vp); MPASS(vp->v_type == VREG || vp->v_type == VDIR); MPASS(bp->b_iocmd == BIO_READ || bp->b_iocmd == BIO_WRITE); FS_DEBUG("inode=%ju offset=%jd resid=%ld\n", (uintmax_t)VTOI(vp), (intmax_t)(((off_t)bp->b_blkno) * biosize), bp->b_bcount); error = fuse_filehandle_getrw(vp, (bp->b_iocmd == BIO_READ) ? FUFH_RDONLY : FUFH_WRONLY, &fufh); if (error) { printf("FUSE: strategy: filehandles are closed\n"); bp->b_ioflags |= BIO_ERROR; bp->b_error = error; return (error); } cred = bp->b_iocmd == BIO_READ ? bp->b_rcred : bp->b_wcred; uiop = &uio; uiop->uio_iov = &io; uiop->uio_iovcnt = 1; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_td = curthread; /* * clear BIO_ERROR and B_INVAL state prior to initiating the I/O. We * do this here so we do not have to do it in all the code that * calls us. */ bp->b_flags &= ~B_INVAL; bp->b_ioflags &= ~BIO_ERROR; KASSERT(!(bp->b_flags & B_DONE), ("fuse_io_strategy: bp %p already marked done", bp)); if (bp->b_iocmd == BIO_READ) { io.iov_len = uiop->uio_resid = bp->b_bcount; io.iov_base = bp->b_data; uiop->uio_rw = UIO_READ; uiop->uio_offset = ((off_t)bp->b_blkno) * biosize; error = fuse_read_directbackend(vp, uiop, cred, fufh); if ((!error && uiop->uio_resid) || (fsess_opt_brokenio(vnode_mount(vp)) && error == EIO && uiop->uio_offset < fvdat->filesize && fvdat->filesize > 0 && uiop->uio_offset >= fvdat->cached_attrs.va_size)) { /* * If we had a short read with no error, we must have * hit a file hole. We should zero-fill the remainder. * This can also occur if the server hits the file EOF. * * Holes used to be able to occur due to pending * writes, but that is not possible any longer. */ int nread = bp->b_bcount - uiop->uio_resid; int left = uiop->uio_resid; if (error != 0) { printf("FUSE: Fix broken io: offset %ju, " " resid %zd, file size %ju/%ju\n", (uintmax_t)uiop->uio_offset, uiop->uio_resid, fvdat->filesize, fvdat->cached_attrs.va_size); error = 0; } if (left > 0) bzero((char *)bp->b_data + nread, left); uiop->uio_resid = 0; } if (error) { bp->b_ioflags |= BIO_ERROR; bp->b_error = error; } } else { /* * If we only need to commit, try to commit */ if (bp->b_flags & B_NEEDCOMMIT) { FS_DEBUG("write: B_NEEDCOMMIT flags set\n"); } /* * Setup for actual write */ if ((off_t)bp->b_blkno * biosize + bp->b_dirtyend > fvdat->filesize) bp->b_dirtyend = fvdat->filesize - (off_t)bp->b_blkno * biosize; if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff; uiop->uio_offset = (off_t)bp->b_blkno * biosize + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; error = fuse_write_directbackend(vp, uiop, cred, fufh, 0); if (error == EINTR || error == ETIMEDOUT || (!error && (bp->b_flags & B_NEEDCOMMIT))) { bp->b_flags &= ~(B_INVAL | B_NOCACHE); if ((bp->b_flags & B_PAGING) == 0) { bdirty(bp); bp->b_flags &= ~B_DONE; } if ((error == EINTR || error == ETIMEDOUT) && (bp->b_flags & B_ASYNC) == 0) bp->b_flags |= B_EINTR; } else { if (error) { bp->b_ioflags |= BIO_ERROR; bp->b_flags |= B_INVAL; bp->b_error = error; } bp->b_dirtyoff = bp->b_dirtyend = 0; } } else { bp->b_resid = 0; bufdone(bp); return (0); } } bp->b_resid = uiop->uio_resid; bufdone(bp); return (error); }
/* * Do an I/O operation to/from a cache block. */ int nwfs_doio(struct vnode *vp, struct bio *bio, struct ucred *cr, struct thread *td) { struct buf *bp = bio->bio_buf; struct uio *uiop; struct nwnode *np; struct nwmount *nmp; int error = 0; struct uio uio; struct iovec io; np = VTONW(vp); nmp = VFSTONWFS(vp->v_mount); uiop = &uio; uiop->uio_iov = &io; uiop->uio_iovcnt = 1; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_td = td; if (bp->b_cmd == BUF_CMD_READ) { io.iov_len = uiop->uio_resid = (size_t)bp->b_bcount; io.iov_base = bp->b_data; uiop->uio_rw = UIO_READ; switch (vp->v_type) { case VREG: uiop->uio_offset = bio->bio_offset; error = ncp_read(NWFSTOCONN(nmp), &np->n_fh, uiop, cr); if (error) break; if (uiop->uio_resid) { size_t left = uiop->uio_resid; size_t nread = bp->b_bcount - left; if (left > 0) bzero((char *)bp->b_data + nread, left); } break; /* case VDIR: nfsstats.readdir_bios++; uiop->uio_offset = bio->bio_offset; if (nmp->nm_flag & NFSMNT_RDIRPLUS) { error = nfs_readdirplusrpc(vp, uiop, cr); if (error == NFSERR_NOTSUPP) nmp->nm_flag &= ~NFSMNT_RDIRPLUS; } if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0) error = nfs_readdirrpc(vp, uiop, cr); if (error == 0 && uiop->uio_resid == (size_t)bp->b_bcount) bp->b_flags |= B_INVAL; break; */ default: kprintf("nwfs_doio: type %x unexpected\n",vp->v_type); break; } if (error) { bp->b_flags |= B_ERROR; bp->b_error = error; } } else { /* write */ KKASSERT(bp->b_cmd == BUF_CMD_WRITE); if (bio->bio_offset + bp->b_dirtyend > np->n_size) bp->b_dirtyend = np->n_size - bio->bio_offset; if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = (size_t)(bp->b_dirtyend - bp->b_dirtyoff); uiop->uio_offset = bio->bio_offset + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; error = ncp_write(NWFSTOCONN(nmp), &np->n_fh, uiop, cr); /* * For an interrupted write, the buffer is still valid * and the write hasn't been pushed to the server yet, * so we can't set B_ERROR and report the interruption * by setting B_EINTR. For the async case, B_EINTR * is not relevant, so the rpc attempt is essentially * a noop. For the case of a V3 write rpc not being * committed to stable storage, the block is still * dirty and requires either a commit rpc or another * write rpc with iomode == NFSV3WRITE_FILESYNC before * the block is reused. This is indicated by setting * the B_DELWRI and B_NEEDCOMMIT flags. */ if (error == EINTR || (!error && (bp->b_flags & B_NEEDCOMMIT))) { crit_enter(); bp->b_flags &= ~(B_INVAL|B_NOCACHE); if ((bp->b_flags & B_PAGING) == 0) bdirty(bp); bp->b_flags |= B_EINTR; crit_exit(); } else { if (error) { bp->b_flags |= B_ERROR; bp->b_error /*= np->n_error */= error; /* np->n_flag |= NWRITEERR;*/ } bp->b_dirtyoff = bp->b_dirtyend = 0; } } else { bp->b_resid = 0; biodone(bio); return (0); } } bp->b_resid = (int)uiop->uio_resid; biodone(bio); return (error); }
/* * Do an I/O operation to/from a cache block. */ int smbfs_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td) { struct smbmount *smp = VFSTOSMBFS(vp->v_mount); struct smbnode *np = VTOSMB(vp); struct uio *uiop; struct iovec io; struct smb_cred *scred; int error = 0; uiop = malloc(sizeof(struct uio), M_SMBFSDATA, M_WAITOK); uiop->uio_iov = &io; uiop->uio_iovcnt = 1; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_td = td; scred = smbfs_malloc_scred(); smb_makescred(scred, td, cr); if (bp->b_iocmd == BIO_READ) { io.iov_len = uiop->uio_resid = bp->b_bcount; io.iov_base = bp->b_data; uiop->uio_rw = UIO_READ; switch (vp->v_type) { case VREG: uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; error = smb_read(smp->sm_share, np->n_fid, uiop, scred); if (error) break; if (uiop->uio_resid) { int left = uiop->uio_resid; int nread = bp->b_bcount - left; if (left > 0) bzero((char *)bp->b_data + nread, left); } break; default: printf("smbfs_doio: type %x unexpected\n",vp->v_type); break; }; if (error) { bp->b_error = error; bp->b_ioflags |= BIO_ERROR; } } else { /* write */ if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size) bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE); if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff; uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; error = smb_write(smp->sm_share, np->n_fid, uiop, scred); /* * For an interrupted write, the buffer is still valid * and the write hasn't been pushed to the server yet, * so we can't set BIO_ERROR and report the interruption * by setting B_EINTR. For the B_ASYNC case, B_EINTR * is not relevant, so the rpc attempt is essentially * a noop. For the case of a V3 write rpc not being * committed to stable storage, the block is still * dirty and requires either a commit rpc or another * write rpc with iomode == NFSV3WRITE_FILESYNC before * the block is reused. This is indicated by setting * the B_DELWRI and B_NEEDCOMMIT flags. */ if (error == EINTR || (!error && (bp->b_flags & B_NEEDCOMMIT))) { int s; s = splbio(); bp->b_flags &= ~(B_INVAL|B_NOCACHE); if ((bp->b_flags & B_ASYNC) == 0) bp->b_flags |= B_EINTR; if ((bp->b_flags & B_PAGING) == 0) { bdirty(bp); bp->b_flags &= ~B_DONE; } if ((bp->b_flags & B_ASYNC) == 0) bp->b_flags |= B_EINTR; splx(s); } else { if (error) { bp->b_ioflags |= BIO_ERROR; bp->b_error = error; } bp->b_dirtyoff = bp->b_dirtyend = 0; } } else { bp->b_resid = 0; bufdone(bp); free(uiop, M_SMBFSDATA); smbfs_free_scred(scred); return 0; } } bp->b_resid = uiop->uio_resid; bufdone(bp); free(uiop, M_SMBFSDATA); smbfs_free_scred(scred); return error; }
int puffs_doio(struct vnode *vp, struct bio *bio, struct thread *td) { struct buf *bp = bio->bio_buf; struct ucred *cred; struct uio *uiop; struct uio uio; struct iovec io; size_t n; int error = 0; if (td != NULL && td->td_proc != NULL) cred = td->td_proc->p_ucred; else cred = proc0.p_ucred; uiop = &uio; uiop->uio_iov = &io; uiop->uio_iovcnt = 1; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_td = td; /* * clear B_ERROR and B_INVAL state prior to initiating the I/O. We * do this here so we do not have to do it in all the code that * calls us. */ bp->b_flags &= ~(B_ERROR | B_INVAL); KASSERT(bp->b_cmd != BUF_CMD_DONE, ("puffs_doio: bp %p already marked done!", bp)); if (bp->b_cmd == BUF_CMD_READ) { io.iov_len = uiop->uio_resid = (size_t)bp->b_bcount; io.iov_base = bp->b_data; uiop->uio_rw = UIO_READ; uiop->uio_offset = bio->bio_offset; error = puffs_directread(vp, uiop, 0, cred); if (error == 0 && uiop->uio_resid) { n = (size_t)bp->b_bcount - uiop->uio_resid; bzero(bp->b_data + n, bp->b_bcount - n); uiop->uio_resid = 0; } if (error) { bp->b_flags |= B_ERROR; bp->b_error = error; } bp->b_resid = uiop->uio_resid; } else { KKASSERT(bp->b_cmd == BUF_CMD_WRITE); if (bio->bio_offset + bp->b_dirtyend > puffs_meta_getsize(vp)) bp->b_dirtyend = puffs_meta_getsize(vp) - bio->bio_offset; if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff; uiop->uio_offset = bio->bio_offset + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; error = puffs_directwrite(vp, uiop, 0, cred); if (error == EINTR || (!error && (bp->b_flags & B_NEEDCOMMIT))) { crit_enter(); bp->b_flags &= ~(B_INVAL|B_NOCACHE); if ((bp->b_flags & B_PAGING) == 0) bdirty(bp); if (error) bp->b_flags |= B_EINTR; crit_exit(); } else { if (error) { bp->b_flags |= B_ERROR; bp->b_error = error; } bp->b_dirtyoff = bp->b_dirtyend = 0; } bp->b_resid = uiop->uio_resid; } else { bp->b_resid = 0; } } biodone(bio); KKASSERT(bp->b_cmd == BUF_CMD_DONE); if (bp->b_flags & B_EINTR) return (EINTR); if (bp->b_flags & B_ERROR) return (bp->b_error ? bp->b_error : EIO); return (0); }