FreeBSD/sys/fs/msdosfs/msdosfs_vnops.c
/*
 * Write data to a file or directory.
 */
Write data to a file or directory.
Write data to a file or directory .
static int
msdosfs_write(struct vop_write_args *ap)
{
	int n;
	int croffset;
	ssize_t resid;
	u_long osize;
	int error = 0;
	u_long count;
	int seqcount;
	daddr_t bn, lastcn;
	struct buf *bp;
	int ioflag = ap->a_ioflag;
	struct uio *uio = ap->a_uio;
	struct vnode *vp = ap->a_vp;
	struct vnode *thisvp;
	struct denode *dep = VTODE(vp);
	struct msdosfsmount *pmp = dep->de_pmp;
	struct ucred *cred = ap->a_cred;
#ifdef MSDOSFS_DEBUG
	printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
	    vp, uio, ioflag, cred);
	printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
#endif
	switch (vp->v_type) {
	case VREG:
		if (ioflag & IO_APPEND)
			uio->uio_offset = dep->de_FileSize;
		thisvp = vp;
		break;
	case VDIR:
		return EISDIR;
	default:
		panic("msdosfs_write(): bad file type");
	}
	if (uio->uio_resid == 0)
		return (0);
	if ((uoff_t)uio->uio_offset + uio->uio_resid > DOS_FILESIZE_MAX)
		return (EFBIG);
	if (vn_rlimit_fsize(vp, uio, uio->uio_td))
		return (EFBIG);
	if (uio->uio_offset > dep->de_FileSize) {
		error = deextend(dep, uio->uio_offset, cred);
		if (error)
			return (error);
	}
	resid = uio->uio_resid;
	osize = dep->de_FileSize;
	if (uio->uio_offset + resid > osize) {
		count = de_clcount(pmp, uio->uio_offset + resid) -
			de_clcount(pmp, osize);
		error = extendfile(dep, count, NULL, NULL, 0);
		if (error &&  (error != ENOSPC || (ioflag & IO_UNIT)))
			goto errexit;
		lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
	} else
		lastcn = de_clcount(pmp, osize) - 1;
	seqcount = ioflag >> IO_SEQSHIFT;
	do {
		if (de_cluster(pmp, uio->uio_offset) > lastcn) {
			error = ENOSPC;
			break;
		}
		croffset = uio->uio_offset & pmp->pm_crbomask;
		n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
		if (uio->uio_offset + n > dep->de_FileSize) {
			dep->de_FileSize = uio->uio_offset + n;
			vnode_pager_setsize(vp, dep->de_FileSize);
		}
		bn = de_cluster(pmp, uio->uio_offset);
		if ((uio->uio_offset & pmp->pm_crbomask) == 0
		    && (de_cluster(pmp, uio->uio_offset + uio->uio_resid)
			> de_cluster(pmp, uio->uio_offset)
			|| uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
			bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0, 0);
			vfs_bio_clrbuf(bp);
			if (bp->b_blkno == bp->b_lblkno) {
				error = pcbmap(dep, bp->b_lblkno, &bn, 0, 0);
				if (error)
					bp->b_blkno = -1;
				else
					bp->b_blkno = bn;
			}
			if (bp->b_blkno == -1) {
				brelse(bp);
				if (!error)
					error = EIO;		
				break;
			}
		} else {
			error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp);
			if (error) {
				brelse(bp);
				break;
			}
		}
		error = uiomove(bp->b_data + croffset, n, uio);
		if (error) {
			brelse(bp);
			break;
		}
		if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
			bp->b_flags |= B_CLUSTEROK;
		if (ioflag & IO_SYNC)
			(void)bwrite(bp);
		else if (vm_page_count_severe() || buf_dirty_count_severe())
			bawrite(bp);
		else if (n + croffset == pmp->pm_bpcluster) {
			if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
				cluster_write(vp, bp, dep->de_FileSize,
				    seqcount, 0);
			else
				bawrite(bp);
		} else
			bdwrite(bp);
		dep->de_flag |= DE_UPDATE;
	} while (error == 0 && uio->uio_resid > 0);
errexit:
	if (error) {
		if (ioflag & IO_UNIT) {
			detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
			uio->uio_offset -= resid - uio->uio_resid;
			uio->uio_resid = resid;
		} else {
			detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED);
			if (uio->uio_resid != resid)
				error = 0;
		}
	} else if (ioflag & IO_SYNC)
		error = deupdat(dep, 1);
	return (error);
}
static int msdosfs_write ( struct vop_write_args * ap ) { int n ; int croffset ; ssize_t resid ; u_long osize ; int error = 0 ; u_long count ; int seqcount ; daddr_t bn , lastcn ; struct buf * bp ; int ioflag = ap -> a_ioflag ; struct uio * uio = ap -> a_uio ; struct vnode * vp = ap -> a_vp ; struct vnode * thisvp ; struct denode * dep = VTODE ( vp ) ; struct msdosfsmount * pmp = dep -> de_pmp ; struct ucred * cred = ap -> a_cred ; #ifdef MSDOSFS_DEBUG printf ( " \n " , vp , uio , ioflag , cred ) ; printf ( " \n " , dep -> de_diroffset , dep -> de_dirclust , dep -> de_StartCluster ) ; #endif switch ( vp -> v_type ) { case VREG : if ( ioflag & IO_APPEND ) uio -> uio_offset = dep -> de_FileSize ; thisvp = vp ; break ; case VDIR : return EISDIR ; default : panic ( " " ) ; } if ( uio -> uio_resid == 0 ) return ( 0 ) ; if ( ( uoff_t ) uio -> uio_offset + uio -> uio_resid > DOS_FILESIZE_MAX ) return ( EFBIG ) ; if ( vn_rlimit_fsize ( vp , uio , uio -> uio_td ) ) return ( EFBIG ) ; if ( uio -> uio_offset > dep -> de_FileSize ) { error = deextend ( dep , uio -> uio_offset , cred ) ; if ( error ) return ( error ) ; } resid = uio -> uio_resid ; osize = dep -> de_FileSize ; if ( uio -> uio_offset + resid > osize ) { count = de_clcount ( pmp , uio -> uio_offset + resid ) - de_clcount ( pmp , osize ) ; error = extendfile ( dep , count , NULL , NULL , 0 ) ; if ( error && ( error != ENOSPC || ( ioflag & IO_UNIT ) ) ) goto errexit ; lastcn = dep -> de_fc [ FC_LASTFC ] . fc_frcn ; } else lastcn = de_clcount ( pmp , osize ) - 1 ; seqcount = ioflag >> IO_SEQSHIFT ; do { if ( de_cluster ( pmp , uio -> uio_offset ) > lastcn ) { error = ENOSPC ; break ; } croffset = uio -> uio_offset & pmp -> pm_crbomask ; n = min ( uio -> uio_resid , pmp -> pm_bpcluster - croffset ) ; if ( uio -> uio_offset + n > dep -> de_FileSize ) { dep -> de_FileSize = uio -> uio_offset + n ; vnode_pager_setsize ( vp , dep -> de_FileSize ) ; } bn = de_cluster ( pmp , uio -> uio_offset ) ; if ( ( uio -> uio_offset & pmp -> pm_crbomask ) == 0 && ( de_cluster ( pmp , uio -> uio_offset + uio -> uio_resid ) > de_cluster ( pmp , uio -> uio_offset ) || uio -> uio_offset + uio -> uio_resid >= dep -> de_FileSize ) ) { bp = getblk ( thisvp , bn , pmp -> pm_bpcluster , 0 , 0 , 0 ) ; vfs_bio_clrbuf ( bp ) ; if ( bp -> b_blkno == bp -> b_lblkno ) { error = pcbmap ( dep , bp -> b_lblkno , & bn , 0 , 0 ) ; if ( error ) bp -> b_blkno = -1 ; else bp -> b_blkno = bn ; } if ( bp -> b_blkno == -1 ) { brelse ( bp ) ; if ( ! error ) error = EIO ; break ; } } else { error = bread ( thisvp , bn , pmp -> pm_bpcluster , cred , & bp ) ; if ( error ) { brelse ( bp ) ; break ; } } error = uiomove ( bp -> b_data + croffset , n , uio ) ; if ( error ) { brelse ( bp ) ; break ; } if ( ( vp -> v_mount -> mnt_flag & MNT_NOCLUSTERW ) == 0 ) bp -> b_flags |= B_CLUSTEROK ; if ( ioflag & IO_SYNC ) ( void ) bwrite ( bp ) ; else if ( vm_page_count_severe ( ) || buf_dirty_count_severe ( ) ) bawrite ( bp ) ; else if ( n + croffset == pmp -> pm_bpcluster ) { if ( ( vp -> v_mount -> mnt_flag & MNT_NOCLUSTERW ) == 0 ) cluster_write ( vp , bp , dep -> de_FileSize , seqcount , 0 ) ; else bawrite ( bp ) ; } else bdwrite ( bp ) ; dep -> de_flag |= DE_UPDATE ; } while ( error == 0 && uio -> uio_resid > 0 ) ; errexit : if ( error ) { if ( ioflag & IO_UNIT ) { detrunc ( dep , osize , ioflag & IO_SYNC , NOCRED ) ; uio -> uio_offset -= resid - uio -> uio_resid ; uio -> uio_resid = resid ; } else { detrunc ( dep , dep -> de_FileSize , ioflag & IO_SYNC , NOCRED ) ; if ( uio -> uio_resid != resid ) error = 0 ; } } else if ( ioflag & IO_SYNC ) error = deupdat ( dep , 1 ) ; return ( error ) ; }
Write data to a file or directory.
Write data to a file or directory .
/*
	 * This is needed (unlike in ffs_write()) because we extend the
	 * file outside of the loop but we don't want to extend the file
	 * for writes of 0 bytes.
	 */ /*
	 * The caller is supposed to ensure that
	 * uio->uio_offset >= 0 and uio->uio_resid >= 0.
	 */ /*
	 * If they've exceeded their filesize limit, tell them about it.
	 */ /*
	 * If the offset we are starting the write at is beyond the end of
	 * the file, then they've done a seek.  Unix filesystems allow
	 * files with holes in them, DOS doesn't so we must fill the hole
	 * with zeroed blocks.
	 */ /*
	 * Remember some values in case the write fails.
	 */ /*
	 * If we write beyond the end of the file, extend it to its ultimate
	 * size ahead of the time to hopefully get a contiguous area.
	 */ /* The object size needs to be set before buffer is allocated */ /*
			 * If either the whole cluster gets written,
			 * or we write the cluster from its start beyond EOF,
			 * then no need to read data from disk.
			 */ /*
			 * Do the bmap now, since pcbmap needs buffers
			 * for the fat table. (see msdosfs_strategy)
			 */ /* XXX */ /*
			 * The block we need to write into exists, so read it in.
			 */ /*
		 * Should these vnode_pager_* functions be done on dir
		 * files?
		 */ /*
		 * Copy the data from user space into the buf header.
		 */ /* Prepare for clustered writes in some else clauses. */ /*
		 * If IO_SYNC, then each buffer is written synchronously.
		 * Otherwise, if we have a severe page deficiency then
		 * write the buffer asynchronously.  Otherwise, if on a
		 * cluster boundary then write the buffer asynchronously,
		 * combining it with contiguous clusters if permitted and
		 * possible, since we don't expect more writes into this
		 * buffer soon.  Otherwise, do a delayed write because we
		 * expect more writes into this buffer soon.
		 */ /*
	 * If the write failed and they want us to, truncate the file back
	 * to the size it was before the write was attempted.
	 */