150477Speter/* $FreeBSD$ */
233548Sjkh/*	$NetBSD: msdosfs_vnops.c,v 1.68 1998/02/10 14:10:04 mrg Exp $	*/
32893Sdfr
42893Sdfr/*-
533548Sjkh * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
633548Sjkh * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
72893Sdfr * All rights reserved.
82893Sdfr * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
92893Sdfr *
102893Sdfr * Redistribution and use in source and binary forms, with or without
112893Sdfr * modification, are permitted provided that the following conditions
122893Sdfr * are met:
132893Sdfr * 1. Redistributions of source code must retain the above copyright
142893Sdfr *    notice, this list of conditions and the following disclaimer.
152893Sdfr * 2. Redistributions in binary form must reproduce the above copyright
162893Sdfr *    notice, this list of conditions and the following disclaimer in the
172893Sdfr *    documentation and/or other materials provided with the distribution.
182893Sdfr * 3. All advertising materials mentioning features or use of this software
192893Sdfr *    must display the following acknowledgement:
202893Sdfr *	This product includes software developed by TooLs GmbH.
212893Sdfr * 4. The name of TooLs GmbH may not be used to endorse or promote products
222893Sdfr *    derived from this software without specific prior written permission.
232893Sdfr *
242893Sdfr * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
252893Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
262893Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
272893Sdfr * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
282893Sdfr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
292893Sdfr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
302893Sdfr * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
312893Sdfr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
322893Sdfr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
332893Sdfr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
342893Sdfr */
35139776Simp/*-
362893Sdfr * Written by Paul Popelka (paulp@uts.amdahl.com)
378876Srgrimes *
382893Sdfr * You can do anything you want with this software, just don't say you wrote
392893Sdfr * it, and don't remove this notice.
408876Srgrimes *
412893Sdfr * This software is provided "as is".
428876Srgrimes *
432893Sdfr * The author supplies this software to be publicly redistributed on the
442893Sdfr * understanding that the author is not responsible for the correct
452893Sdfr * functioning of this software in any circumstances and is not liable for
462893Sdfr * any damages caused by this software.
478876Srgrimes *
482893Sdfr * October 1992
492893Sdfr */
502893Sdfr
512893Sdfr#include <sys/param.h>
522893Sdfr#include <sys/systm.h>
53171755Sbde#include <sys/bio.h>
54171755Sbde#include <sys/buf.h>
55171755Sbde#include <sys/clock.h>
56171755Sbde#include <sys/dirent.h>
57171749Sbde#include <sys/lock.h>
58123964Sbde#include <sys/lockf.h>
59171755Sbde#include <sys/malloc.h>
60171755Sbde#include <sys/mount.h>
61171749Sbde#include <sys/mutex.h>
622893Sdfr#include <sys/namei.h>
63164033Srwatson#include <sys/priv.h>
64171755Sbde#include <sys/stat.h>
6518020Sbde#include <sys/unistd.h>
662893Sdfr#include <sys/vnode.h>
672893Sdfr
683152Sphk#include <vm/vm.h>
6912662Sdg#include <vm/vm_extern.h>
703152Sphk
7177162Sru#include <fs/msdosfs/bpb.h>
7277162Sru#include <fs/msdosfs/direntry.h>
7377162Sru#include <fs/msdosfs/denode.h>
7477162Sru#include <fs/msdosfs/fat.h>
75171755Sbde#include <fs/msdosfs/msdosfsmount.h>
7611921Sphk
7755308Sbp#define	DOS_FILESIZE_MAX	0xffffffff
7855308Sbp
792893Sdfr/*
8011921Sphk * Prototypes for MSDOSFS vnode operations
8111921Sphk */
82138270Sphkstatic vop_create_t	msdosfs_create;
83138270Sphkstatic vop_mknod_t	msdosfs_mknod;
84140965Speadarstatic vop_open_t	msdosfs_open;
85138270Sphkstatic vop_close_t	msdosfs_close;
86138270Sphkstatic vop_access_t	msdosfs_access;
87138270Sphkstatic vop_getattr_t	msdosfs_getattr;
88138270Sphkstatic vop_setattr_t	msdosfs_setattr;
89138270Sphkstatic vop_read_t	msdosfs_read;
90138270Sphkstatic vop_write_t	msdosfs_write;
91138270Sphkstatic vop_fsync_t	msdosfs_fsync;
92138270Sphkstatic vop_remove_t	msdosfs_remove;
93138270Sphkstatic vop_link_t	msdosfs_link;
94138270Sphkstatic vop_rename_t	msdosfs_rename;
95138270Sphkstatic vop_mkdir_t	msdosfs_mkdir;
96138270Sphkstatic vop_rmdir_t	msdosfs_rmdir;
97138270Sphkstatic vop_symlink_t	msdosfs_symlink;
98138270Sphkstatic vop_readdir_t	msdosfs_readdir;
99138270Sphkstatic vop_bmap_t	msdosfs_bmap;
100138270Sphkstatic vop_strategy_t	msdosfs_strategy;
101138270Sphkstatic vop_print_t	msdosfs_print;
102138270Sphkstatic vop_pathconf_t	msdosfs_pathconf;
103166774Spjdstatic vop_vptofh_t	msdosfs_vptofh;
10411921Sphk
10511921Sphk/*
1062893Sdfr * Some general notes:
1078876Srgrimes *
1082893Sdfr * In the ufs filesystem the inodes, superblocks, and indirect blocks are
1092893Sdfr * read/written using the vnode for the filesystem. Blocks that represent
1102893Sdfr * the contents of a file are read/written using the vnode for the file
1112893Sdfr * (including directories when they are read/written as files). This
1122893Sdfr * presents problems for the dos filesystem because data that should be in
1132893Sdfr * an inode (if dos had them) resides in the directory itself.  Since we
1142893Sdfr * must update directory entries without the benefit of having the vnode
1152893Sdfr * for the directory we must use the vnode for the filesystem.  This means
1162893Sdfr * that when a directory is actually read/written (via read, write, or
1172893Sdfr * readdir, or seek) we must use the vnode for the filesystem instead of
1182893Sdfr * the vnode for the directory as would happen in ufs. This is to insure we
1192893Sdfr * retreive the correct block from the buffer cache since the hash value is
1202893Sdfr * based upon the vnode address and the desired block number.
1212893Sdfr */
1222893Sdfr
1232893Sdfr/*
1242893Sdfr * Create a regular file. On entry the directory to contain the file being
1252893Sdfr * created is locked.  We must release before we return. We must also free
1262893Sdfr * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
1272893Sdfr * only if the SAVESTART bit in cn_flags is clear on success.
1282893Sdfr */
12911921Sphkstatic int
1302893Sdfrmsdosfs_create(ap)
1312893Sdfr	struct vop_create_args /* {
1322893Sdfr		struct vnode *a_dvp;
1332893Sdfr		struct vnode **a_vpp;
1342893Sdfr		struct componentname *a_cnp;
1352893Sdfr		struct vattr *a_vap;
1362893Sdfr	} */ *ap;
1372893Sdfr{
1382893Sdfr	struct componentname *cnp = ap->a_cnp;
1392893Sdfr	struct denode ndirent;
1402893Sdfr	struct denode *dep;
1412893Sdfr	struct denode *pdep = VTODE(ap->a_dvp);
1422893Sdfr	struct timespec ts;
1432893Sdfr	int error;
1442893Sdfr
1452893Sdfr#ifdef MSDOSFS_DEBUG
14633548Sjkh	printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
1472893Sdfr#endif
1482893Sdfr
1492893Sdfr	/*
15033548Sjkh	 * If this is the root directory and there is no space left we
15133548Sjkh	 * can't do anything.  This is because the root directory can not
15233548Sjkh	 * change size.
15333548Sjkh	 */
15433548Sjkh	if (pdep->de_StartCluster == MSDOSFSROOT
15533548Sjkh	    && pdep->de_fndoffset >= pdep->de_FileSize) {
15633548Sjkh		error = ENOSPC;
15733548Sjkh		goto bad;
15833548Sjkh	}
15933548Sjkh
16033548Sjkh	/*
1612893Sdfr	 * Create a directory entry for the file, then call createde() to
1622893Sdfr	 * have it installed. NOTE: DOS files are always executable.  We
1632893Sdfr	 * use the absence of the owner write bit to make the file
1642893Sdfr	 * readonly.
1652893Sdfr	 */
1662893Sdfr#ifdef DIAGNOSTIC
16733548Sjkh	if ((cnp->cn_flags & HASBUF) == 0)
1682893Sdfr		panic("msdosfs_create: no name");
1692893Sdfr#endif
1702893Sdfr	bzero(&ndirent, sizeof(ndirent));
17133548Sjkh	error = uniqdosname(pdep, cnp, ndirent.de_Name);
17233548Sjkh	if (error)
17333548Sjkh		goto bad;
17433548Sjkh
175254627Sken	ndirent.de_Attributes = ATTR_ARCHIVE;
17641275Sdt	ndirent.de_LowerCase = 0;
1772893Sdfr	ndirent.de_StartCluster = 0;
1782893Sdfr	ndirent.de_FileSize = 0;
17933548Sjkh	ndirent.de_pmp = pdep->de_pmp;
18033548Sjkh	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
18134901Sphk	getnanotime(&ts);
18233548Sjkh	DETIMES(&ndirent, &ts, &ts, &ts);
18333548Sjkh	error = createde(&ndirent, pdep, &dep, cnp);
18433548Sjkh	if (error)
18533548Sjkh		goto bad;
18633548Sjkh	*ap->a_vpp = DETOV(dep);
18733548Sjkh	return (0);
18833548Sjkh
18933548Sjkhbad:
19033548Sjkh	return (error);
1912893Sdfr}
1922893Sdfr
19311921Sphkstatic int
1942893Sdfrmsdosfs_mknod(ap)
1952893Sdfr	struct vop_mknod_args /* {
1962893Sdfr		struct vnode *a_dvp;
1972893Sdfr		struct vnode **a_vpp;
1982893Sdfr		struct componentname *a_cnp;
1992893Sdfr		struct vattr *a_vap;
2002893Sdfr	} */ *ap;
2012893Sdfr{
202171758Sbde
20379996Sassar    return (EINVAL);
2042893Sdfr}
2052893Sdfr
20611921Sphkstatic int
207140965Speadarmsdosfs_open(ap)
208140965Speadar	struct vop_open_args /* {
209140965Speadar		struct vnode *a_vp;
210140965Speadar		int a_mode;
211140965Speadar		struct ucred *a_cred;
212140965Speadar		struct thread *a_td;
213193924Skib		struct file *a_fp;
214140965Speadar	} */ *ap;
215140965Speadar{
216140965Speadar	struct denode *dep = VTODE(ap->a_vp);
217140965Speadar	vnode_create_vobject(ap->a_vp, dep->de_FileSize, ap->a_td);
218140965Speadar	return 0;
219140965Speadar}
220140965Speadar
221140965Speadarstatic int
2222893Sdfrmsdosfs_close(ap)
2232893Sdfr	struct vop_close_args /* {
2242893Sdfr		struct vnode *a_vp;
2252893Sdfr		int a_fflag;
2262893Sdfr		struct ucred *a_cred;
22783366Sjulian		struct thread *a_td;
2282893Sdfr	} */ *ap;
2292893Sdfr{
2302893Sdfr	struct vnode *vp = ap->a_vp;
2312893Sdfr	struct denode *dep = VTODE(vp);
23233548Sjkh	struct timespec ts;
2332893Sdfr
234103936Sjeff	VI_LOCK(vp);
23533548Sjkh	if (vp->v_usecount > 1) {
23634901Sphk		getnanotime(&ts);
23733548Sjkh		DETIMES(dep, &ts, &ts, &ts);
23833548Sjkh	}
239103936Sjeff	VI_UNLOCK(vp);
2402893Sdfr	return 0;
2412893Sdfr}
2422893Sdfr
24311921Sphkstatic int
24411644Sdgmsdosfs_access(ap)
24511644Sdg	struct vop_access_args /* {
24611644Sdg		struct vnode *a_vp;
247184413Strasz		accmode_t a_accmode;
24811644Sdg		struct ucred *a_cred;
24983366Sjulian		struct thread *a_td;
25011644Sdg	} */ *ap;
2512893Sdfr{
25211644Sdg	struct vnode *vp = ap->a_vp;
25311644Sdg	struct denode *dep = VTODE(ap->a_vp);
25411644Sdg	struct msdosfsmount *pmp = dep->de_pmp;
255184413Strasz	mode_t file_mode;
256184413Strasz	accmode_t accmode = ap->a_accmode;
2578876Srgrimes
258254627Sken	file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
259118837Strhodes	file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
26011644Sdg
26111644Sdg	/*
262171771Sbde	 * Disallow writing to directories and regular files if the
263171771Sbde	 * filesystem is read-only.
26411644Sdg	 */
265184413Strasz	if (accmode & VWRITE) {
26611644Sdg		switch (vp->v_type) {
267254627Sken		case VREG:
26811644Sdg		case VDIR:
26911644Sdg			if (vp->v_mount->mnt_flag & MNT_RDONLY)
27011644Sdg				return (EROFS);
27111644Sdg			break;
27243305Sdillon		default:
27343305Sdillon			break;
27411644Sdg		}
27511644Sdg	}
27611644Sdg
27764865Sphk	return (vaccess(vp->v_type, file_mode, pmp->pm_uid, pmp->pm_gid,
278184413Strasz	    ap->a_accmode, ap->a_cred, NULL));
2792893Sdfr}
2802893Sdfr
28111921Sphkstatic int
2822893Sdfrmsdosfs_getattr(ap)
2832893Sdfr	struct vop_getattr_args /* {
2842893Sdfr		struct vnode *a_vp;
2852893Sdfr		struct vattr *a_vap;
2862893Sdfr		struct ucred *a_cred;
2872893Sdfr	} */ *ap;
2882893Sdfr{
2892893Sdfr	struct denode *dep = VTODE(ap->a_vp);
29033548Sjkh	struct msdosfsmount *pmp = dep->de_pmp;
2912893Sdfr	struct vattr *vap = ap->a_vap;
29233548Sjkh	mode_t mode;
29333548Sjkh	struct timespec ts;
29433548Sjkh	u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
295131523Stjr	uint64_t fileid;
2962893Sdfr
29734901Sphk	getnanotime(&ts);
29833548Sjkh	DETIMES(dep, &ts, &ts, &ts);
299189120Sjhb	vap->va_fsid = dev2udev(pmp->pm_dev);
3002893Sdfr	/*
3012893Sdfr	 * The following computation of the fileid must be the same as that
3022893Sdfr	 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
3032893Sdfr	 * doesn't work.
3042893Sdfr	 */
3052893Sdfr	if (dep->de_Attributes & ATTR_DIRECTORY) {
306131523Stjr		fileid = (uint64_t)cntobn(pmp, dep->de_StartCluster) *
307131523Stjr		    dirsperblk;
30833548Sjkh		if (dep->de_StartCluster == MSDOSFSROOT)
30933548Sjkh			fileid = 1;
3102893Sdfr	} else {
311131523Stjr		fileid = (uint64_t)cntobn(pmp, dep->de_dirclust) *
312131523Stjr		    dirsperblk;
31333548Sjkh		if (dep->de_dirclust == MSDOSFSROOT)
314131523Stjr			fileid = (uint64_t)roottobn(pmp, 0) * dirsperblk;
315171758Sbde		fileid += (uoff_t)dep->de_diroffset / sizeof(struct direntry);
3162893Sdfr	}
317166340Srodrigc
318166340Srodrigc	if (pmp->pm_flags & MSDOSFS_LARGEFS)
319166340Srodrigc		vap->va_fileid = msdosfs_fileno_map(pmp->pm_mountp, fileid);
320166340Srodrigc	else
321166340Srodrigc		vap->va_fileid = (long)fileid;
322166340Srodrigc
323254627Sken	mode = S_IRWXU|S_IRWXG|S_IRWXO;
324118837Strhodes	vap->va_mode = mode &
325171758Sbde	    (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
32633548Sjkh	vap->va_uid = pmp->pm_uid;
32733548Sjkh	vap->va_gid = pmp->pm_gid;
3282893Sdfr	vap->va_nlink = 1;
329183214Skib	vap->va_rdev = NODEV;
3302893Sdfr	vap->va_size = dep->de_FileSize;
331163647Sphk	fattime2timespec(dep->de_MDate, dep->de_MTime, 0, 0, &vap->va_mtime);
332164855Smaxim	vap->va_ctime = vap->va_mtime;
33333548Sjkh	if (pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
334163647Sphk		fattime2timespec(dep->de_ADate, 0, 0, 0, &vap->va_atime);
335163647Sphk		fattime2timespec(dep->de_CDate, dep->de_CTime, dep->de_CHun,
336164855Smaxim		    0, &vap->va_birthtime);
33733548Sjkh	} else {
33833548Sjkh		vap->va_atime = vap->va_mtime;
339164855Smaxim		vap->va_birthtime.tv_sec = -1;
340164855Smaxim		vap->va_birthtime.tv_nsec = 0;
34133548Sjkh	}
34233548Sjkh	vap->va_flags = 0;
343254627Sken	if (dep->de_Attributes & ATTR_ARCHIVE)
344254627Sken		vap->va_flags |= UF_ARCHIVE;
345254627Sken	if (dep->de_Attributes & ATTR_HIDDEN)
346254627Sken		vap->va_flags |= UF_HIDDEN;
347254627Sken	if (dep->de_Attributes & ATTR_READONLY)
348254627Sken		vap->va_flags |= UF_READONLY;
349254627Sken	if (dep->de_Attributes & ATTR_SYSTEM)
350254627Sken		vap->va_flags |= UF_SYSTEM;
3512893Sdfr	vap->va_gen = 0;
35233548Sjkh	vap->va_blocksize = pmp->pm_bpcluster;
35333548Sjkh	vap->va_bytes =
35433548Sjkh	    (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
3552893Sdfr	vap->va_type = ap->a_vp->v_type;
3569862Sdfr	vap->va_filerev = dep->de_modrev;
35733548Sjkh	return (0);
3582893Sdfr}
3592893Sdfr
36011921Sphkstatic int
3612893Sdfrmsdosfs_setattr(ap)
3622893Sdfr	struct vop_setattr_args /* {
3632893Sdfr		struct vnode *a_vp;
3642893Sdfr		struct vattr *a_vap;
3652893Sdfr		struct ucred *a_cred;
3662893Sdfr	} */ *ap;
3672893Sdfr{
36811644Sdg	struct vnode *vp = ap->a_vp;
3692893Sdfr	struct denode *dep = VTODE(ap->a_vp);
37033548Sjkh	struct msdosfsmount *pmp = dep->de_pmp;
3712893Sdfr	struct vattr *vap = ap->a_vap;
3722893Sdfr	struct ucred *cred = ap->a_cred;
373182371Sattilio	struct thread *td = curthread;
37411644Sdg	int error = 0;
3758876Srgrimes
37633548Sjkh#ifdef MSDOSFS_DEBUG
377182371Sattilio	printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
378182371Sattilio	    ap->a_vp, vap, cred);
37933548Sjkh#endif
38033548Sjkh
38111644Sdg	/*
38211644Sdg	 * Check for unsettable attributes.
38311644Sdg	 */
38411644Sdg	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
38511644Sdg	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
38611644Sdg	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
38711644Sdg	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
38833548Sjkh#ifdef MSDOSFS_DEBUG
38933548Sjkh		printf("msdosfs_setattr(): returning EINVAL\n");
39033548Sjkh		printf("    va_type %d, va_nlink %x, va_fsid %lx, va_fileid %lx\n",
39133548Sjkh		    vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid);
39233548Sjkh		printf("    va_blocksize %lx, va_rdev %x, va_bytes %qx, va_gen %lx\n",
39333548Sjkh		    vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
39433548Sjkh		printf("    va_uid %x, va_gid %x\n",
39533548Sjkh		    vap->va_uid, vap->va_gid);
39633548Sjkh#endif
39711644Sdg		return (EINVAL);
3982893Sdfr	}
399254627Sken
400254627Sken	/*
401254627Sken	 * We don't allow setting attributes on the root directory.
402254627Sken	 * The special case for the root directory is because before
403254627Sken	 * FAT32, the root directory didn't have an entry for itself
404254627Sken	 * (and was otherwise special).  With FAT32, the root
405254627Sken	 * directory is not so special, but still doesn't have an
406254627Sken	 * entry for itself.
407254627Sken	 */
408254627Sken	if (vp->v_vflag & VV_ROOT)
409254627Sken		return (EINVAL);
410254627Sken
41111644Sdg	if (vap->va_flags != VNOVAL) {
41211644Sdg		if (vp->v_mount->mnt_flag & MNT_RDONLY)
41311644Sdg			return (EROFS);
414164033Srwatson		if (cred->cr_uid != pmp->pm_uid) {
415170587Srwatson			error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
416164033Srwatson			if (error)
417164033Srwatson				return (error);
418164033Srwatson		}
41911644Sdg		/*
42011644Sdg		 * We are very inconsistent about handling unsupported
42136811Sdt		 * attributes.  We ignored the access time and the
42211644Sdg		 * read and execute bits.  We were strict for the other
42311644Sdg		 * attributes.
42411644Sdg		 */
425254627Sken		if (vap->va_flags & ~(UF_ARCHIVE | UF_HIDDEN | UF_READONLY |
426254627Sken		    UF_SYSTEM))
42736839Speter			return EOPNOTSUPP;
428254627Sken		if (vap->va_flags & UF_ARCHIVE)
429254627Sken			dep->de_Attributes |= ATTR_ARCHIVE;
430254627Sken		else
43111644Sdg			dep->de_Attributes &= ~ATTR_ARCHIVE;
432254627Sken		if (vap->va_flags & UF_HIDDEN)
433254627Sken			dep->de_Attributes |= ATTR_HIDDEN;
434254627Sken		else
435254627Sken			dep->de_Attributes &= ~ATTR_HIDDEN;
436254627Sken		/* We don't allow changing the readonly bit on directories. */
437254627Sken		if (vp->v_type != VDIR) {
438254627Sken			if (vap->va_flags & UF_READONLY)
439254627Sken				dep->de_Attributes |= ATTR_READONLY;
440254627Sken			else
441254627Sken				dep->de_Attributes &= ~ATTR_READONLY;
442254627Sken		}
443254627Sken		if (vap->va_flags & UF_SYSTEM)
444254627Sken			dep->de_Attributes |= ATTR_SYSTEM;
445254627Sken		else
446254627Sken			dep->de_Attributes &= ~ATTR_SYSTEM;
44711644Sdg		dep->de_flag |= DE_MODIFIED;
44811644Sdg	}
4492893Sdfr
45033548Sjkh	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
45133548Sjkh		uid_t uid;
45233548Sjkh		gid_t gid;
453111742Sdes
45411644Sdg		if (vp->v_mount->mnt_flag & MNT_RDONLY)
45511644Sdg			return (EROFS);
45633548Sjkh		uid = vap->va_uid;
45733548Sjkh		if (uid == (uid_t)VNOVAL)
45833548Sjkh			uid = pmp->pm_uid;
45933548Sjkh		gid = vap->va_gid;
46033548Sjkh		if (gid == (gid_t)VNOVAL)
46133548Sjkh			gid = pmp->pm_gid;
462164033Srwatson		if (cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid ||
463164033Srwatson		    (gid != pmp->pm_gid && !groupmember(gid, cred))) {
464170587Srwatson			error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0);
465164033Srwatson			if (error)
466164033Srwatson				return (error);
467164033Srwatson		}
46833548Sjkh		if (uid != pmp->pm_uid || gid != pmp->pm_gid)
46936858Sdt			return EINVAL;
4705241Sbde	}
47133548Sjkh
4722893Sdfr	if (vap->va_size != VNOVAL) {
47311644Sdg		switch (vp->v_type) {
47411644Sdg		case VDIR:
47511644Sdg			return (EISDIR);
47611644Sdg		case VREG:
477171771Sbde			/*
478171771Sbde			 * Truncation is only supported for regular files,
479171771Sbde			 * Disallow it if the filesystem is read-only.
480171771Sbde			 */
48111644Sdg			if (vp->v_mount->mnt_flag & MNT_RDONLY)
48211644Sdg				return (EROFS);
48311644Sdg			break;
48443305Sdillon		default:
485171771Sbde			/*
486171771Sbde			 * According to POSIX, the result is unspecified
487171771Sbde			 * for file types other than regular files,
488171771Sbde			 * directories and shared memory objects.  We
489171771Sbde			 * don't support any file types except regular
490171771Sbde			 * files and directories in this file system, so
491171771Sbde			 * this (default) case is unreachable and can do
492171771Sbde			 * anything.  Keep falling through to detrunc()
493171771Sbde			 * for now.
494171771Sbde			 */
49543305Sdillon			break;
49611644Sdg		}
497234605Strasz		error = detrunc(dep, vap->va_size, 0, cred);
4983152Sphk		if (error)
4992893Sdfr			return error;
5002893Sdfr	}
50133548Sjkh	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
50211644Sdg		if (vp->v_mount->mnt_flag & MNT_RDONLY)
50311644Sdg			return (EROFS);
504164033Srwatson		if (vap->va_vaflags & VA_UTIMES_NULL) {
505182371Sattilio			error = VOP_ACCESS(vp, VADMIN, cred, td);
506164033Srwatson			if (error)
507182371Sattilio				error = VOP_ACCESS(vp, VWRITE, cred, td);
508164033Srwatson		} else
509182371Sattilio			error = VOP_ACCESS(vp, VADMIN, cred, td);
510254627Sken		if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
511254627Sken		    vap->va_atime.tv_sec != VNOVAL) {
512254627Sken			dep->de_flag &= ~DE_ACCESS;
513254627Sken			timespec2fattime(&vap->va_atime, 0,
514254627Sken			    &dep->de_ADate, NULL, NULL);
515254627Sken		}
516254627Sken		if (vap->va_mtime.tv_sec != VNOVAL) {
517254627Sken			dep->de_flag &= ~DE_UPDATE;
518254627Sken			timespec2fattime(&vap->va_mtime, 0,
519254627Sken			    &dep->de_MDate, &dep->de_MTime, NULL);
520254627Sken		}
521254627Sken		/*
522254627Sken		 * We don't set the archive bit when modifying the time of
523254627Sken		 * a directory to emulate the Windows/DOS behavior.
524254627Sken		 */
525254627Sken		if (vp->v_type != VDIR)
52633548Sjkh			dep->de_Attributes |= ATTR_ARCHIVE;
527254627Sken		dep->de_flag |= DE_MODIFIED;
5282893Sdfr	}
5292893Sdfr	/*
5305241Sbde	 * DOS files only have the ability to have their writability
5312893Sdfr	 * attribute set, so we use the owner write bit to set the readonly
5322893Sdfr	 * attribute.
5332893Sdfr	 */
53433548Sjkh	if (vap->va_mode != (mode_t)VNOVAL) {
53511644Sdg		if (vp->v_mount->mnt_flag & MNT_RDONLY)
53611644Sdg			return (EROFS);
537164033Srwatson		if (cred->cr_uid != pmp->pm_uid) {
538170587Srwatson			error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
539164033Srwatson			if (error)
540164033Srwatson				return (error);
541164033Srwatson		}
54233548Sjkh		if (vp->v_type != VDIR) {
54333548Sjkh			/* We ignore the read and execute bits. */
54433548Sjkh			if (vap->va_mode & VWRITE)
54533548Sjkh				dep->de_Attributes &= ~ATTR_READONLY;
54633548Sjkh			else
54733548Sjkh				dep->de_Attributes |= ATTR_READONLY;
54893883Sbde			dep->de_Attributes |= ATTR_ARCHIVE;
54933548Sjkh			dep->de_flag |= DE_MODIFIED;
55033548Sjkh		}
5512893Sdfr	}
552172741Sbde	return (deupdat(dep, 0));
5532893Sdfr}
5542893Sdfr
55511921Sphkstatic int
5562893Sdfrmsdosfs_read(ap)
5572893Sdfr	struct vop_read_args /* {
5582893Sdfr		struct vnode *a_vp;
5592893Sdfr		struct uio *a_uio;
5602893Sdfr		int a_ioflag;
5612893Sdfr		struct ucred *a_cred;
5622893Sdfr	} */ *ap;
5632893Sdfr{
5642893Sdfr	int error = 0;
56533548Sjkh	int blsize;
5662893Sdfr	int isadir;
567231949Skib	ssize_t orig_resid;
56855308Sbp	u_int n;
56955308Sbp	u_long diff;
57055308Sbp	u_long on;
5712893Sdfr	daddr_t lbn;
57234002Smsmith	daddr_t rablock;
5732893Sdfr	int rasize;
57451486Sdillon	int seqcount;
5752893Sdfr	struct buf *bp;
5762893Sdfr	struct vnode *vp = ap->a_vp;
5772893Sdfr	struct denode *dep = VTODE(vp);
5782893Sdfr	struct msdosfsmount *pmp = dep->de_pmp;
5792893Sdfr	struct uio *uio = ap->a_uio;
5802893Sdfr
5812893Sdfr	/*
5822893Sdfr	 * If they didn't ask for any data, then we are done.
5832893Sdfr	 */
58449075Sbde	orig_resid = uio->uio_resid;
585171774Sbde	if (orig_resid == 0)
58633548Sjkh		return (0);
5872893Sdfr
588171774Sbde	/*
589171774Sbde	 * The caller is supposed to ensure that
590171774Sbde	 * uio->uio_offset >= 0 and uio->uio_resid >= 0.
591171774Sbde	 * We don't need to check for large offsets as in ffs because
592171774Sbde	 * dep->de_FileSize <= DOS_FILESIZE_MAX < OFF_MAX, so large
593171774Sbde	 * offsets cannot cause overflow even in theory.
594171774Sbde	 */
595171774Sbde
596108357Sdillon	seqcount = ap->a_ioflag >> IO_SEQSHIFT;
59751486Sdillon
5982893Sdfr	isadir = dep->de_Attributes & ATTR_DIRECTORY;
5992893Sdfr	do {
60055189Sbp		if (uio->uio_offset >= dep->de_FileSize)
60155189Sbp			break;
60233548Sjkh		lbn = de_cluster(pmp, uio->uio_offset);
603171522Sbde		rablock = lbn + 1;
604171522Sbde		blsize = pmp->pm_bpcluster;
605171522Sbde		on = uio->uio_offset & pmp->pm_crbomask;
6062893Sdfr		/*
6072893Sdfr		 * If we are operating on a directory file then be sure to
6082893Sdfr		 * do i/o with the vnode for the filesystem instead of the
6092893Sdfr		 * vnode for the directory.
6102893Sdfr		 */
6112893Sdfr		if (isadir) {
61255308Sbp			/* convert cluster # to block # */
61355308Sbp			error = pcbmap(dep, lbn, &lbn, 0, &blsize);
61455594Sbp			if (error == E2BIG) {
61555594Sbp				error = EINVAL;
61655308Sbp				break;
61755594Sbp			} else if (error)
61855594Sbp				break;
61933548Sjkh			error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp);
620171522Sbde		} else if (de_cn2off(pmp, rablock) >= dep->de_FileSize) {
621171522Sbde			error = bread(vp, lbn, blsize, NOCRED, &bp);
622171523Sbde		} else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) {
623171523Sbde			error = cluster_read(vp, dep->de_FileSize, lbn, blsize,
624248282Skib			    NOCRED, on + uio->uio_resid, seqcount, 0, &bp);
625171522Sbde		} else if (seqcount > 1) {
626171522Sbde			rasize = blsize;
627171522Sbde			error = breadn(vp, lbn,
628171522Sbde			    blsize, &rablock, &rasize, 1, NOCRED, &bp);
6292893Sdfr		} else {
630171522Sbde			error = bread(vp, lbn, blsize, NOCRED, &bp);
6312893Sdfr		}
6322893Sdfr		if (error) {
6332893Sdfr			brelse(bp);
63449075Sbde			break;
6352893Sdfr		}
63655308Sbp		diff = pmp->pm_bpcluster - on;
63755308Sbp		n = diff > uio->uio_resid ? uio->uio_resid : diff;
63855308Sbp		diff = dep->de_FileSize - uio->uio_offset;
63955308Sbp		if (diff < n)
64055308Sbp			n = diff;
64155308Sbp		diff = blsize - bp->b_resid;
64255308Sbp		if (diff < n)
64355308Sbp			n = diff;
6442893Sdfr		error = uiomove(bp->b_data + on, (int) n, uio);
6452893Sdfr		brelse(bp);
6462893Sdfr	} while (error == 0 && uio->uio_resid > 0 && n != 0);
64749075Sbde	if (!isadir && (error == 0 || uio->uio_resid != orig_resid) &&
64849075Sbde	    (vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
64941416Sdt		dep->de_flag |= DE_ACCESS;
65033548Sjkh	return (error);
6512893Sdfr}
6522893Sdfr
6532893Sdfr/*
6542893Sdfr * Write data to a file or directory.
6552893Sdfr */
65611921Sphkstatic int
6572893Sdfrmsdosfs_write(ap)
6582893Sdfr	struct vop_write_args /* {
6592893Sdfr		struct vnode *a_vp;
6602893Sdfr		struct uio *a_uio;
6612893Sdfr		int a_ioflag;
6622893Sdfr		struct ucred *a_cred;
6632893Sdfr	} */ *ap;
6642893Sdfr{
6652893Sdfr	int n;
6662893Sdfr	int croffset;
667231949Skib	ssize_t resid;
66833548Sjkh	u_long osize;
6692893Sdfr	int error = 0;
6702893Sdfr	u_long count;
671171523Sbde	int seqcount;
6722893Sdfr	daddr_t bn, lastcn;
6732893Sdfr	struct buf *bp;
6742893Sdfr	int ioflag = ap->a_ioflag;
6752893Sdfr	struct uio *uio = ap->a_uio;
6762893Sdfr	struct vnode *vp = ap->a_vp;
6772893Sdfr	struct vnode *thisvp;
6782893Sdfr	struct denode *dep = VTODE(vp);
6792893Sdfr	struct msdosfsmount *pmp = dep->de_pmp;
6802893Sdfr	struct ucred *cred = ap->a_cred;
6818876Srgrimes
6822893Sdfr#ifdef MSDOSFS_DEBUG
68333548Sjkh	printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
68433548Sjkh	    vp, uio, ioflag, cred);
68533548Sjkh	printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
68633548Sjkh	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
6872893Sdfr#endif
6882893Sdfr
6892893Sdfr	switch (vp->v_type) {
6902893Sdfr	case VREG:
6912893Sdfr		if (ioflag & IO_APPEND)
6922893Sdfr			uio->uio_offset = dep->de_FileSize;
6932893Sdfr		thisvp = vp;
6942893Sdfr		break;
6952893Sdfr	case VDIR:
69633548Sjkh		return EISDIR;
6972893Sdfr	default:
6982893Sdfr		panic("msdosfs_write(): bad file type");
6992893Sdfr	}
7002893Sdfr
701171774Sbde	/*
702171774Sbde	 * This is needed (unlike in ffs_write()) because we extend the
703171774Sbde	 * file outside of the loop but we don't want to extend the file
704171774Sbde	 * for writes of 0 bytes.
705171774Sbde	 */
7062893Sdfr	if (uio->uio_resid == 0)
70733548Sjkh		return (0);
7082893Sdfr
7092893Sdfr	/*
710171774Sbde	 * The caller is supposed to ensure that
711171774Sbde	 * uio->uio_offset >= 0 and uio->uio_resid >= 0.
712171774Sbde	 */
713171774Sbde	if ((uoff_t)uio->uio_offset + uio->uio_resid > DOS_FILESIZE_MAX)
714171774Sbde		return (EFBIG);
715171774Sbde
716171774Sbde	/*
7172893Sdfr	 * If they've exceeded their filesize limit, tell them about it.
7182893Sdfr	 */
719207719Strasz	if (vn_rlimit_fsize(vp, uio, uio->uio_td))
720207662Strasz		return (EFBIG);
7212893Sdfr
7222893Sdfr	/*
7232893Sdfr	 * If the offset we are starting the write at is beyond the end of
7242893Sdfr	 * the file, then they've done a seek.  Unix filesystems allow
7252893Sdfr	 * files with holes in them, DOS doesn't so we must fill the hole
7262893Sdfr	 * with zeroed blocks.
7272893Sdfr	 */
7282893Sdfr	if (uio->uio_offset > dep->de_FileSize) {
7292893Sdfr		error = deextend(dep, uio->uio_offset, cred);
7302893Sdfr		if (error)
73133548Sjkh			return (error);
7322893Sdfr	}
7332893Sdfr
7342893Sdfr	/*
7352893Sdfr	 * Remember some values in case the write fails.
7362893Sdfr	 */
7372893Sdfr	resid = uio->uio_resid;
7382893Sdfr	osize = dep->de_FileSize;
7392893Sdfr
7402893Sdfr	/*
7412893Sdfr	 * If we write beyond the end of the file, extend it to its ultimate
7422893Sdfr	 * size ahead of the time to hopefully get a contiguous area.
7432893Sdfr	 */
7442893Sdfr	if (uio->uio_offset + resid > osize) {
74533548Sjkh		count = de_clcount(pmp, uio->uio_offset + resid) -
74633548Sjkh			de_clcount(pmp, osize);
74733548Sjkh		error = extendfile(dep, count, NULL, NULL, 0);
74833548Sjkh		if (error &&  (error != ENOSPC || (ioflag & IO_UNIT)))
7492893Sdfr			goto errexit;
7502893Sdfr		lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
7512893Sdfr	} else
7522893Sdfr		lastcn = de_clcount(pmp, osize) - 1;
7538876Srgrimes
754171523Sbde	seqcount = ioflag >> IO_SEQSHIFT;
7552893Sdfr	do {
75633548Sjkh		if (de_cluster(pmp, uio->uio_offset) > lastcn) {
7572893Sdfr			error = ENOSPC;
7582893Sdfr			break;
7592893Sdfr		}
7608876Srgrimes
76118640Sdyson		croffset = uio->uio_offset & pmp->pm_crbomask;
76218640Sdyson		n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
76318640Sdyson		if (uio->uio_offset + n > dep->de_FileSize) {
76418640Sdyson			dep->de_FileSize = uio->uio_offset + n;
76518640Sdyson			/* The object size needs to be set before buffer is allocated */
76618640Sdyson			vnode_pager_setsize(vp, dep->de_FileSize);
76718640Sdyson		}
76818640Sdyson
76934002Smsmith		bn = de_cluster(pmp, uio->uio_offset);
7702893Sdfr		if ((uio->uio_offset & pmp->pm_crbomask) == 0
771111742Sdes		    && (de_cluster(pmp, uio->uio_offset + uio->uio_resid)
772111742Sdes			> de_cluster(pmp, uio->uio_offset)
7732893Sdfr			|| uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
7742893Sdfr			/*
7752893Sdfr			 * If either the whole cluster gets written,
7762893Sdfr			 * or we write the cluster from its start beyond EOF,
7772893Sdfr			 * then no need to read data from disk.
7782893Sdfr			 */
779111856Sjeff			bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0, 0);
780171522Sbde			vfs_bio_clrbuf(bp);
7812893Sdfr			/*
7822893Sdfr			 * Do the bmap now, since pcbmap needs buffers
7832893Sdfr			 * for the fat table. (see msdosfs_strategy)
7842893Sdfr			 */
78533548Sjkh			if (bp->b_blkno == bp->b_lblkno) {
78692363Smckusick				error = pcbmap(dep, bp->b_lblkno, &bn, 0, 0);
78733548Sjkh				if (error)
78833548Sjkh					bp->b_blkno = -1;
78992363Smckusick				else
79092363Smckusick					bp->b_blkno = bn;
7912893Sdfr			}
79233548Sjkh			if (bp->b_blkno == -1) {
79333548Sjkh				brelse(bp);
79433548Sjkh				if (!error)
79533548Sjkh					error = EIO;		/* XXX */
79633548Sjkh				break;
79733548Sjkh			}
7982893Sdfr		} else {
7992893Sdfr			/*
8002893Sdfr			 * The block we need to write into exists, so read it in.
8012893Sdfr			 */
8023152Sphk			error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp);
80333548Sjkh			if (error) {
80433548Sjkh				brelse(bp);
8052893Sdfr				break;
80633548Sjkh			}
8072893Sdfr		}
8082893Sdfr
8092893Sdfr		/*
8102893Sdfr		 * Should these vnode_pager_* functions be done on dir
8112893Sdfr		 * files?
8122893Sdfr		 */
8132893Sdfr
8142893Sdfr		/*
8152893Sdfr		 * Copy the data from user space into the buf header.
8162893Sdfr		 */
8172893Sdfr		error = uiomove(bp->b_data + croffset, n, uio);
81855190Sbp		if (error) {
81955190Sbp			brelse(bp);
82055190Sbp			break;
82155190Sbp		}
8222893Sdfr
823171523Sbde		/* Prepare for clustered writes in some else clauses. */
824171523Sbde		if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
825171523Sbde			bp->b_flags |= B_CLUSTEROK;
826171523Sbde
8272893Sdfr		/*
828171522Sbde		 * If IO_SYNC, then each buffer is written synchronously.
829171523Sbde		 * Otherwise, if we have a severe page deficiency then
830171523Sbde		 * write the buffer asynchronously.  Otherwise, if on a
831171522Sbde		 * cluster boundary then write the buffer asynchronously,
832171523Sbde		 * combining it with contiguous clusters if permitted and
833171523Sbde		 * possible, since we don't expect more writes into this
834171522Sbde		 * buffer soon.  Otherwise, do a delayed write because we
835171522Sbde		 * expect more writes into this buffer soon.
8362893Sdfr		 */
8372893Sdfr		if (ioflag & IO_SYNC)
838171522Sbde			(void)bwrite(bp);
839171523Sbde		else if (vm_page_count_severe() || buf_dirty_count_severe())
8402893Sdfr			bawrite(bp);
841171523Sbde		else if (n + croffset == pmp->pm_bpcluster) {
842171523Sbde			if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
843171523Sbde				cluster_write(vp, bp, dep->de_FileSize,
844248282Skib				    seqcount, 0);
845171523Sbde			else
846171523Sbde				bawrite(bp);
847171523Sbde		} else
8482893Sdfr			bdwrite(bp);
8492893Sdfr		dep->de_flag |= DE_UPDATE;
8502893Sdfr	} while (error == 0 && uio->uio_resid > 0);
8512893Sdfr
8522893Sdfr	/*
8532893Sdfr	 * If the write failed and they want us to, truncate the file back
8542893Sdfr	 * to the size it was before the write was attempted.
8552893Sdfr	 */
8562893Sdfrerrexit:
8572893Sdfr	if (error) {
8582893Sdfr		if (ioflag & IO_UNIT) {
859234605Strasz			detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
8602893Sdfr			uio->uio_offset -= resid - uio->uio_resid;
8612893Sdfr			uio->uio_resid = resid;
8622893Sdfr		} else {
863234605Strasz			detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED);
8642893Sdfr			if (uio->uio_resid != resid)
8652893Sdfr				error = 0;
8662893Sdfr		}
86733548Sjkh	} else if (ioflag & IO_SYNC)
86833548Sjkh		error = deupdat(dep, 1);
86933548Sjkh	return (error);
8702893Sdfr}
8712893Sdfr
8722893Sdfr/*
8732893Sdfr * Flush the blocks of a file to disk.
8742893Sdfr */
87511921Sphkstatic int
8762893Sdfrmsdosfs_fsync(ap)
8772893Sdfr	struct vop_fsync_args /* {
8782893Sdfr		struct vnode *a_vp;
8792893Sdfr		struct ucred *a_cred;
8802893Sdfr		int a_waitfor;
88183366Sjulian		struct thread *a_td;
8822893Sdfr	} */ *ap;
8832893Sdfr{
884250193Skib	struct vnode *devvp;
885250193Skib	int allerror, error;
886171758Sbde
887110584Sjeff	vop_stdfsync(ap);
888250193Skib
889250193Skib	/*
890250193Skib	* If the syncing request comes from fsync(2), sync the entire
891250193Skib	* FAT and any other metadata that happens to be on devvp.  We
892250193Skib	* need this mainly for the FAT.  We write the FAT sloppily, and
893250193Skib	* syncing it all now is the best we can easily do to get all
894250193Skib	* directory entries associated with the file (not just the file)
895250193Skib	* fully synced.  The other metadata includes critical metadata
896250193Skib	* for all directory entries, but only in the MNT_ASYNC case.  We
897250193Skib	* will soon sync all metadata in the file's directory entry.
898250193Skib	* Non-critical metadata for associated directory entries only
899250193Skib	* gets synced accidentally, as in most file systems.
900250193Skib	*/
901250193Skib	if (ap->a_waitfor == MNT_WAIT) {
902250193Skib		devvp = VTODE(ap->a_vp)->de_pmp->pm_devvp;
903250193Skib		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
904250193Skib		allerror = VOP_FSYNC(devvp, MNT_WAIT, ap->a_td);
905250193Skib		VOP_UNLOCK(devvp, 0);
906250193Skib	} else
907250193Skib		allerror = 0;
908250193Skib
909250193Skib	error = deupdat(VTODE(ap->a_vp), ap->a_waitfor == MNT_WAIT);
910250193Skib	if (allerror == 0)
911250193Skib		allerror = error;
912250193Skib	return (allerror);
9132893Sdfr}
9142893Sdfr
91511921Sphkstatic int
9162893Sdfrmsdosfs_remove(ap)
9172893Sdfr	struct vop_remove_args /* {
9182893Sdfr		struct vnode *a_dvp;
9192893Sdfr		struct vnode *a_vp;
9202893Sdfr		struct componentname *a_cnp;
9212893Sdfr	} */ *ap;
9222893Sdfr{
9232893Sdfr	struct denode *dep = VTODE(ap->a_vp);
9242893Sdfr	struct denode *ddep = VTODE(ap->a_dvp);
92535823Smsmith	int error;
9262893Sdfr
92733548Sjkh	if (ap->a_vp->v_type == VDIR)
92833548Sjkh		error = EPERM;
92933548Sjkh	else
93033548Sjkh		error = removede(ddep, dep);
9312893Sdfr#ifdef MSDOSFS_DEBUG
93233548Sjkh	printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep, ap->a_vp->v_usecount);
9332893Sdfr#endif
93433548Sjkh	return (error);
9352893Sdfr}
9362893Sdfr
9372893Sdfr/*
93867438Sbp * DOS filesystems don't know what links are.
9392893Sdfr */
94011921Sphkstatic int
9412893Sdfrmsdosfs_link(ap)
9422893Sdfr	struct vop_link_args /* {
9439842Sdg		struct vnode *a_tdvp;
9442893Sdfr		struct vnode *a_vp;
9452893Sdfr		struct componentname *a_cnp;
9462893Sdfr	} */ *ap;
9472893Sdfr{
94835823Smsmith	return (EOPNOTSUPP);
9492893Sdfr}
9502893Sdfr
9512893Sdfr/*
9522893Sdfr * Renames on files require moving the denode to a new hash queue since the
9532893Sdfr * denode's location is used to compute which hash queue to put the file
9542893Sdfr * in. Unless it is a rename in place.  For example "mv a b".
9558876Srgrimes *
9562893Sdfr * What follows is the basic algorithm:
9578876Srgrimes *
9582893Sdfr * if (file move) {
9592893Sdfr *	if (dest file exists) {
9602893Sdfr *		remove dest file
9612893Sdfr *	}
9622893Sdfr *	if (dest and src in same directory) {
9632893Sdfr *		rewrite name in existing directory slot
9642893Sdfr *	} else {
9652893Sdfr *		write new entry in dest directory
9662893Sdfr *		update offset and dirclust in denode
9672893Sdfr *		move denode to new hash chain
9682893Sdfr *		clear old directory entry
9692893Sdfr *	}
9702893Sdfr * } else {
9712893Sdfr *	directory move
9722893Sdfr *	if (dest directory exists) {
9732893Sdfr *		if (dest is not empty) {
9742893Sdfr *			return ENOTEMPTY
9752893Sdfr *		}
9762893Sdfr *		remove dest directory
9772893Sdfr *	}
9782893Sdfr *	if (dest and src in same directory) {
9792893Sdfr *		rewrite name in existing entry
9802893Sdfr *	} else {
9812893Sdfr *		be sure dest is not a child of src directory
9822893Sdfr *		write entry in dest directory
9832893Sdfr *		update "." and ".." in moved directory
9842893Sdfr *		clear old directory entry for moved directory
9852893Sdfr *	}
9862893Sdfr * }
9878876Srgrimes *
9882893Sdfr * On entry:
9892893Sdfr *	source's parent directory is unlocked
9902893Sdfr *	source file or directory is unlocked
9912893Sdfr *	destination's parent directory is locked
9922893Sdfr *	destination file or directory is locked if it exists
9938876Srgrimes *
9942893Sdfr * On exit:
9952893Sdfr *	all denodes should be released
9962893Sdfr */
99711921Sphkstatic int
9982893Sdfrmsdosfs_rename(ap)
9992893Sdfr	struct vop_rename_args /* {
10002893Sdfr		struct vnode *a_fdvp;
10012893Sdfr		struct vnode *a_fvp;
10022893Sdfr		struct componentname *a_fcnp;
10032893Sdfr		struct vnode *a_tdvp;
10042893Sdfr		struct vnode *a_tvp;
10052893Sdfr		struct componentname *a_tcnp;
10062893Sdfr	} */ *ap;
10072893Sdfr{
100833548Sjkh	struct vnode *tdvp = ap->a_tdvp;
100933548Sjkh	struct vnode *fvp = ap->a_fvp;
101033548Sjkh	struct vnode *fdvp = ap->a_fdvp;
101133548Sjkh	struct vnode *tvp = ap->a_tvp;
101233548Sjkh	struct componentname *tcnp = ap->a_tcnp;
101333548Sjkh	struct componentname *fcnp = ap->a_fcnp;
101433548Sjkh	struct denode *ip, *xp, *dp, *zp;
1015180252Skib	u_char toname[12], oldname[11];
101633548Sjkh	u_long from_diroffset, to_diroffset;
101733548Sjkh	u_char to_count;
101833548Sjkh	int doingdirectory = 0, newparent = 0;
10192893Sdfr	int error;
1020246217Skib	u_long cn, pcl;
10212893Sdfr	daddr_t bn;
10222893Sdfr	struct denode *fddep;	/* from file's parent directory	 */
10232893Sdfr	struct msdosfsmount *pmp;
10242893Sdfr	struct direntry *dotdotp;
10252893Sdfr	struct buf *bp;
10262893Sdfr
10272893Sdfr	fddep = VTODE(ap->a_fdvp);
10282893Sdfr	pmp = fddep->de_pmp;
10292893Sdfr
103033548Sjkh	pmp = VFSTOMSDOSFS(fdvp->v_mount);
10312893Sdfr
103233548Sjkh#ifdef DIAGNOSTIC
103333548Sjkh	if ((tcnp->cn_flags & HASBUF) == 0 ||
103433548Sjkh	    (fcnp->cn_flags & HASBUF) == 0)
103533548Sjkh		panic("msdosfs_rename: no name");
103633548Sjkh#endif
10372893Sdfr	/*
103833548Sjkh	 * Check for cross-device rename.
10392893Sdfr	 */
1040171758Sbde	if (fvp->v_mount != tdvp->v_mount ||
1041171758Sbde	    (tvp && fvp->v_mount != tvp->v_mount)) {
104233548Sjkh		error = EXDEV;
104333548Sjkhabortit:
104433548Sjkh		if (tdvp == tvp)
104533548Sjkh			vrele(tdvp);
104633548Sjkh		else
104733548Sjkh			vput(tdvp);
104833548Sjkh		if (tvp)
104933548Sjkh			vput(tvp);
105033548Sjkh		vrele(fdvp);
105133548Sjkh		vrele(fvp);
105233548Sjkh		return (error);
105333548Sjkh	}
10542893Sdfr
10552893Sdfr	/*
105633548Sjkh	 * If source and dest are the same, do nothing.
10572893Sdfr	 */
105833548Sjkh	if (tvp == fvp) {
105933548Sjkh		error = 0;
106033548Sjkh		goto abortit;
106133548Sjkh	}
10622893Sdfr
1063175202Sattilio	error = vn_lock(fvp, LK_EXCLUSIVE);
106433548Sjkh	if (error)
106533548Sjkh		goto abortit;
106633548Sjkh	dp = VTODE(fdvp);
106733548Sjkh	ip = VTODE(fvp);
106833548Sjkh
10692893Sdfr	/*
10702893Sdfr	 * Be sure we are not renaming ".", "..", or an alias of ".". This
10712893Sdfr	 * leads to a crippled directory tree.  It's pretty tough to do a
10722893Sdfr	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
10732893Sdfr	 * doesn't work if the ".." entry is missing.
10742893Sdfr	 */
107533548Sjkh	if (ip->de_Attributes & ATTR_DIRECTORY) {
107633548Sjkh		/*
107733548Sjkh		 * Avoid ".", "..", and aliases of "." for obvious reasons.
107833548Sjkh		 */
107933548Sjkh		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
108033548Sjkh		    dp == ip ||
108133548Sjkh		    (fcnp->cn_flags & ISDOTDOT) ||
108233548Sjkh		    (tcnp->cn_flags & ISDOTDOT) ||
108333548Sjkh		    (ip->de_flag & DE_RENAME)) {
1084175294Sattilio			VOP_UNLOCK(fvp, 0);
108533548Sjkh			error = EINVAL;
108633548Sjkh			goto abortit;
10872893Sdfr		}
108833548Sjkh		ip->de_flag |= DE_RENAME;
108933548Sjkh		doingdirectory++;
10902893Sdfr	}
10912893Sdfr
10922893Sdfr	/*
109333548Sjkh	 * When the target exists, both the directory
109433548Sjkh	 * and target vnodes are returned locked.
10952893Sdfr	 */
109633548Sjkh	dp = VTODE(tdvp);
109733548Sjkh	xp = tvp ? VTODE(tvp) : NULL;
109833548Sjkh	/*
109933548Sjkh	 * Remember direntry place to use for destination
110033548Sjkh	 */
110133548Sjkh	to_diroffset = dp->de_fndoffset;
110233548Sjkh	to_count = dp->de_fndcnt;
110333548Sjkh
110433548Sjkh	/*
110533548Sjkh	 * If ".." must be changed (ie the directory gets a new
110633548Sjkh	 * parent) then the source directory must not be in the
1107204111Suqs	 * directory hierarchy above the target, as this would
110833548Sjkh	 * orphan everything below the source directory. Also
110933548Sjkh	 * the user must have write permission in the source so
111033548Sjkh	 * as to be able to change "..". We must repeat the call
111133548Sjkh	 * to namei, as the parent directory is unlocked by the
111233548Sjkh	 * call to doscheckpath().
111333548Sjkh	 */
111483366Sjulian	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
1115175294Sattilio	VOP_UNLOCK(fvp, 0);
111633548Sjkh	if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
11172893Sdfr		newparent = 1;
111833548Sjkh	if (doingdirectory && newparent) {
111933548Sjkh		if (error)	/* write access check above */
11202893Sdfr			goto bad;
112133548Sjkh		if (xp != NULL)
112233548Sjkh			vput(tvp);
112333548Sjkh		/*
112433548Sjkh		 * doscheckpath() vput()'s dp,
112533548Sjkh		 * so we have to do a relookup afterwards
112633548Sjkh		 */
112733548Sjkh		error = doscheckpath(ip, dp);
11283152Sphk		if (error)
112933548Sjkh			goto out;
113033548Sjkh		if ((tcnp->cn_flags & SAVESTART) == 0)
113133548Sjkh			panic("msdosfs_rename: lost to startdir");
113233548Sjkh		error = relookup(tdvp, &tvp, tcnp);
113333548Sjkh		if (error)
113433548Sjkh			goto out;
113533548Sjkh		dp = VTODE(tdvp);
113633548Sjkh		xp = tvp ? VTODE(tvp) : NULL;
11372893Sdfr	}
11382893Sdfr
113933548Sjkh	if (xp != NULL) {
114033548Sjkh		/*
114133548Sjkh		 * Target must be empty if a directory and have no links
114233548Sjkh		 * to it. Also, ensure source and target are compatible
114333548Sjkh		 * (both directories, or both not directories).
114433548Sjkh		 */
114533548Sjkh		if (xp->de_Attributes & ATTR_DIRECTORY) {
114633548Sjkh			if (!dosdirempty(xp)) {
11472893Sdfr				error = ENOTEMPTY;
11482893Sdfr				goto bad;
11492893Sdfr			}
115033548Sjkh			if (!doingdirectory) {
115133548Sjkh				error = ENOTDIR;
11522893Sdfr				goto bad;
11532893Sdfr			}
115433548Sjkh			cache_purge(tdvp);
115533548Sjkh		} else if (doingdirectory) {
115633548Sjkh			error = EISDIR;
115733548Sjkh			goto bad;
11582893Sdfr		}
115933548Sjkh		error = removede(dp, xp);
11603152Sphk		if (error)
11612893Sdfr			goto bad;
116233548Sjkh		vput(tvp);
116333548Sjkh		xp = NULL;
11642893Sdfr	}
11652893Sdfr
11662893Sdfr	/*
116733548Sjkh	 * Convert the filename in tcnp into a dos filename. We copy this
116833548Sjkh	 * into the denode and directory entry for the destination
116933548Sjkh	 * file/directory.
11702893Sdfr	 */
117133548Sjkh	error = uniqdosname(VTODE(tdvp), tcnp, toname);
117233548Sjkh	if (error)
117333548Sjkh		goto abortit;
117433548Sjkh
117533548Sjkh	/*
117633548Sjkh	 * Since from wasn't locked at various places above,
117733548Sjkh	 * have to do a relookup here.
117833548Sjkh	 */
117933548Sjkh	fcnp->cn_flags &= ~MODMASK;
118033548Sjkh	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
118133548Sjkh	if ((fcnp->cn_flags & SAVESTART) == 0)
118233548Sjkh		panic("msdosfs_rename: lost from startdir");
118333548Sjkh	if (!newparent)
1184175294Sattilio		VOP_UNLOCK(tdvp, 0);
118563141Sdwmalone	if (relookup(fdvp, &fvp, fcnp) == 0)
118663141Sdwmalone		vrele(fdvp);
118733548Sjkh	if (fvp == NULL) {
11882893Sdfr		/*
118933548Sjkh		 * From name has disappeared.
11902893Sdfr		 */
119133548Sjkh		if (doingdirectory)
119233548Sjkh			panic("rename: lost dir entry");
119333548Sjkh		if (newparent)
1194175294Sattilio			VOP_UNLOCK(tdvp, 0);
119533548Sjkh		vrele(tdvp);
1196155160Sjeff		vrele(ap->a_fvp);
119733548Sjkh		return 0;
119833548Sjkh	}
119933548Sjkh	xp = VTODE(fvp);
120033548Sjkh	zp = VTODE(fdvp);
120133548Sjkh	from_diroffset = zp->de_fndoffset;
120233548Sjkh
120333548Sjkh	/*
120433548Sjkh	 * Ensure that the directory entry still exists and has not
120533548Sjkh	 * changed till now. If the source is a file the entry may
120633548Sjkh	 * have been unlinked or renamed. In either case there is
120733548Sjkh	 * no further work to be done. If the source is a directory
120833548Sjkh	 * then it cannot have been rmdir'ed or renamed; this is
120933548Sjkh	 * prohibited by the DE_RENAME flag.
121033548Sjkh	 */
121133548Sjkh	if (xp != ip) {
121233548Sjkh		if (doingdirectory)
121333548Sjkh			panic("rename: lost dir entry");
1214175294Sattilio		VOP_UNLOCK(fvp, 0);
121533548Sjkh		if (newparent)
1216175294Sattilio			VOP_UNLOCK(fdvp, 0);
1217155160Sjeff		vrele(ap->a_fvp);
121833548Sjkh		xp = NULL;
12192893Sdfr	} else {
122033548Sjkh		vrele(fvp);
122133548Sjkh		xp = NULL;
12222893Sdfr
12232893Sdfr		/*
122433548Sjkh		 * First write a new entry in the destination
122533548Sjkh		 * directory and mark the entry in the source directory
122633548Sjkh		 * as deleted.  Then move the denode to the correct hash
12272893Sdfr		 * chain for its new location in the filesystem.  And, if
12282893Sdfr		 * we moved a directory, then update its .. entry to point
122933548Sjkh		 * to the new parent directory.
12302893Sdfr		 */
123133548Sjkh		bcopy(ip->de_Name, oldname, 11);
123233548Sjkh		bcopy(toname, ip->de_Name, 11);	/* update denode */
123333548Sjkh		dp->de_fndoffset = to_diroffset;
123433548Sjkh		dp->de_fndcnt = to_count;
123533548Sjkh		error = createde(ip, dp, (struct denode **)0, tcnp);
12362893Sdfr		if (error) {
123733548Sjkh			bcopy(oldname, ip->de_Name, 11);
123833548Sjkh			if (newparent)
1239175294Sattilio				VOP_UNLOCK(fdvp, 0);
1240175294Sattilio			VOP_UNLOCK(fvp, 0);
12412893Sdfr			goto bad;
12422893Sdfr		}
124333548Sjkh		ip->de_refcnt++;
124433548Sjkh		zp->de_fndoffset = from_diroffset;
124533548Sjkh		error = removede(zp, ip);
12463152Sphk		if (error) {
1247154730Strhodes			/* XXX should downgrade to ro here, fs is corrupt */
124833548Sjkh			if (newparent)
1249175294Sattilio				VOP_UNLOCK(fdvp, 0);
1250175294Sattilio			VOP_UNLOCK(fvp, 0);
12512893Sdfr			goto bad;
12522893Sdfr		}
125333548Sjkh		if (!doingdirectory) {
125433548Sjkh			error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
125533548Sjkh				       &ip->de_dirclust, 0);
125633548Sjkh			if (error) {
1257154730Strhodes				/* XXX should downgrade to ro here, fs is corrupt */
125833548Sjkh				if (newparent)
1259175294Sattilio					VOP_UNLOCK(fdvp, 0);
1260175294Sattilio				VOP_UNLOCK(fvp, 0);
126133548Sjkh				goto bad;
126233548Sjkh			}
126339128Sdt			if (ip->de_dirclust == MSDOSFSROOT)
126439128Sdt				ip->de_diroffset = to_diroffset;
126539128Sdt			else
126633548Sjkh				ip->de_diroffset = to_diroffset & pmp->pm_crbomask;
12672893Sdfr		}
126833548Sjkh		reinsert(ip);
126933548Sjkh		if (newparent)
1270175294Sattilio			VOP_UNLOCK(fdvp, 0);
12712893Sdfr	}
12722893Sdfr
12732893Sdfr	/*
12742893Sdfr	 * If we moved a directory to a new parent directory, then we must
12752893Sdfr	 * fixup the ".." entry in the moved directory.
12762893Sdfr	 */
127733548Sjkh	if (doingdirectory && newparent) {
127833548Sjkh		cn = ip->de_StartCluster;
12792893Sdfr		if (cn == MSDOSFSROOT) {
12802893Sdfr			/* this should never happen */
12817170Sdg			panic("msdosfs_rename(): updating .. in root directory?");
128233548Sjkh		} else
12832893Sdfr			bn = cntobn(pmp, cn);
12842893Sdfr		error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
12852893Sdfr			      NOCRED, &bp);
12862893Sdfr		if (error) {
1287154730Strhodes			/* XXX should downgrade to ro here, fs is corrupt */
128833548Sjkh			brelse(bp);
1289175294Sattilio			VOP_UNLOCK(fvp, 0);
12902893Sdfr			goto bad;
12912893Sdfr		}
129233548Sjkh		dotdotp = (struct direntry *)bp->b_data + 1;
1293246217Skib		pcl = dp->de_StartCluster;
1294246217Skib		if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1295246217Skib			pcl = MSDOSFSROOT;
1296246217Skib		putushort(dotdotp->deStartCluster, pcl);
129733548Sjkh		if (FAT32(pmp))
1298246217Skib			putushort(dotdotp->deHighClust, pcl >> 16);
1299231998Skib		if (DOINGASYNC(fvp))
1300172798Sbde			bdwrite(bp);
1301172798Sbde		else if ((error = bwrite(bp)) != 0) {
1302154730Strhodes			/* XXX should downgrade to ro here, fs is corrupt */
1303175294Sattilio			VOP_UNLOCK(fvp, 0);
13042893Sdfr			goto bad;
13052893Sdfr		}
130633548Sjkh	}
130733548Sjkh
1308213543Skib	/*
1309213543Skib	 * The msdosfs lookup is case insensitive. Several aliases may
1310213543Skib	 * be inserted for a single directory entry. As a consequnce,
1311213543Skib	 * name cache purge done by lookup for fvp when DELETE op for
1312213543Skib	 * namei is specified, might be not enough to expunge all
1313213543Skib	 * namecache entries that were installed for this direntry.
1314213543Skib	 */
1315213508Skib	cache_purge(fvp);
1316175294Sattilio	VOP_UNLOCK(fvp, 0);
131733548Sjkhbad:
131833548Sjkh	if (xp)
131933548Sjkh		vput(tvp);
132033548Sjkh	vput(tdvp);
132133548Sjkhout:
132233548Sjkh	ip->de_flag &= ~DE_RENAME;
132333548Sjkh	vrele(fdvp);
132433548Sjkh	vrele(fvp);
132533548Sjkh	return (error);
132633548Sjkh
13272893Sdfr}
13282893Sdfr
132911921Sphkstatic struct {
13302893Sdfr	struct direntry dot;
13312893Sdfr	struct direntry dotdot;
133233548Sjkh} dosdirtemplate = {
1333203827Skib	{	".          ",				/* the . entry */
133433548Sjkh		ATTR_DIRECTORY,				/* file attribute */
1335111742Sdes		0,					/* reserved */
133633548Sjkh		0, { 0, 0 }, { 0, 0 },			/* create time & date */
133733548Sjkh		{ 0, 0 },				/* access date */
133833548Sjkh		{ 0, 0 },				/* high bits of start cluster */
133933548Sjkh		{ 210, 4 }, { 210, 4 },			/* modify time & date */
134033548Sjkh		{ 0, 0 },				/* startcluster */
1341111742Sdes		{ 0, 0, 0, 0 }				/* filesize */
134233548Sjkh	},
1343203827Skib	{	"..         ",				/* the .. entry */
134433548Sjkh		ATTR_DIRECTORY,				/* file attribute */
1345111742Sdes		0,					/* reserved */
134633548Sjkh		0, { 0, 0 }, { 0, 0 },			/* create time & date */
134733548Sjkh		{ 0, 0 },				/* access date */
134833548Sjkh		{ 0, 0 },				/* high bits of start cluster */
134933548Sjkh		{ 210, 4 }, { 210, 4 },			/* modify time & date */
135033548Sjkh		{ 0, 0 },				/* startcluster */
135133548Sjkh		{ 0, 0, 0, 0 }				/* filesize */
135233548Sjkh	}
13532893Sdfr};
13542893Sdfr
135511921Sphkstatic int
13562893Sdfrmsdosfs_mkdir(ap)
13572893Sdfr	struct vop_mkdir_args /* {
13582893Sdfr		struct vnode *a_dvp;
1359171758Sbde		struct vnode **a_vpp;
13602893Sdfr		struvt componentname *a_cnp;
13612893Sdfr		struct vattr *a_vap;
13622893Sdfr	} */ *ap;
13632893Sdfr{
136433548Sjkh	struct componentname *cnp = ap->a_cnp;
136533548Sjkh	struct denode *dep;
136633548Sjkh	struct denode *pdep = VTODE(ap->a_dvp);
13672893Sdfr	struct direntry *denp;
136833548Sjkh	struct msdosfsmount *pmp = pdep->de_pmp;
13692893Sdfr	struct buf *bp;
137035823Smsmith	u_long newcluster, pcl;
137135823Smsmith	int bn;
137235823Smsmith	int error;
137335823Smsmith	struct denode ndirent;
13742893Sdfr	struct timespec ts;
13752893Sdfr
13762893Sdfr	/*
13772893Sdfr	 * If this is the root directory and there is no space left we
13782893Sdfr	 * can't do anything.  This is because the root directory can not
13792893Sdfr	 * change size.
13802893Sdfr	 */
138133548Sjkh	if (pdep->de_StartCluster == MSDOSFSROOT
138233548Sjkh	    && pdep->de_fndoffset >= pdep->de_FileSize) {
138333548Sjkh		error = ENOSPC;
138433548Sjkh		goto bad2;
13852893Sdfr	}
13862893Sdfr
13872893Sdfr	/*
13882893Sdfr	 * Allocate a cluster to hold the about to be created directory.
13892893Sdfr	 */
13903152Sphk	error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
139133548Sjkh	if (error)
139233548Sjkh		goto bad2;
13932893Sdfr
139433548Sjkh	bzero(&ndirent, sizeof(ndirent));
139533548Sjkh	ndirent.de_pmp = pmp;
139633548Sjkh	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
139734901Sphk	getnanotime(&ts);
139833548Sjkh	DETIMES(&ndirent, &ts, &ts, &ts);
139933548Sjkh
14002893Sdfr	/*
14012893Sdfr	 * Now fill the cluster with the "." and ".." entries. And write
14022893Sdfr	 * the cluster to disk.  This way it is there for the parent
14032893Sdfr	 * directory to be pointing at if there were a crash.
14042893Sdfr	 */
14052893Sdfr	bn = cntobn(pmp, newcluster);
14062893Sdfr	/* always succeeds */
1407111856Sjeff	bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0);
14082893Sdfr	bzero(bp->b_data, pmp->pm_bpcluster);
14092893Sdfr	bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
141033548Sjkh	denp = (struct direntry *)bp->b_data;
141133548Sjkh	putushort(denp[0].deStartCluster, newcluster);
141233548Sjkh	putushort(denp[0].deCDate, ndirent.de_CDate);
141333548Sjkh	putushort(denp[0].deCTime, ndirent.de_CTime);
141433548Sjkh	denp[0].deCHundredth = ndirent.de_CHun;
141533548Sjkh	putushort(denp[0].deADate, ndirent.de_ADate);
141633548Sjkh	putushort(denp[0].deMDate, ndirent.de_MDate);
141733548Sjkh	putushort(denp[0].deMTime, ndirent.de_MTime);
141833548Sjkh	pcl = pdep->de_StartCluster;
1419246217Skib	/*
1420246217Skib	 * Although the root directory has a non-magic starting cluster
1421246217Skib	 * number for FAT32, chkdsk and fsck_msdosfs still require
1422246217Skib	 * references to it in dotdot entries to be magic.
1423246217Skib	 */
142433548Sjkh	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1425246217Skib		pcl = MSDOSFSROOT;
142633548Sjkh	putushort(denp[1].deStartCluster, pcl);
142733548Sjkh	putushort(denp[1].deCDate, ndirent.de_CDate);
142833548Sjkh	putushort(denp[1].deCTime, ndirent.de_CTime);
142933548Sjkh	denp[1].deCHundredth = ndirent.de_CHun;
143033548Sjkh	putushort(denp[1].deADate, ndirent.de_ADate);
143133548Sjkh	putushort(denp[1].deMDate, ndirent.de_MDate);
143233548Sjkh	putushort(denp[1].deMTime, ndirent.de_MTime);
143333548Sjkh	if (FAT32(pmp)) {
143433548Sjkh		putushort(denp[0].deHighClust, newcluster >> 16);
1435246217Skib		putushort(denp[1].deHighClust, pcl >> 16);
14362893Sdfr	}
14372893Sdfr
1438231998Skib	if (DOINGASYNC(ap->a_dvp))
1439172798Sbde		bdwrite(bp);
1440172798Sbde	else if ((error = bwrite(bp)) != 0)
144133548Sjkh		goto bad;
144233548Sjkh
14432893Sdfr	/*
14442893Sdfr	 * Now build up a directory entry pointing to the newly allocated
14452893Sdfr	 * cluster.  This will be written to an empty slot in the parent
14462893Sdfr	 * directory.
14472893Sdfr	 */
144833548Sjkh#ifdef DIAGNOSTIC
144933548Sjkh	if ((cnp->cn_flags & HASBUF) == 0)
145033548Sjkh		panic("msdosfs_mkdir: no name");
145133548Sjkh#endif
145233548Sjkh	error = uniqdosname(pdep, cnp, ndirent.de_Name);
145333548Sjkh	if (error)
145433548Sjkh		goto bad;
14552893Sdfr
145633548Sjkh	ndirent.de_Attributes = ATTR_DIRECTORY;
145741275Sdt	ndirent.de_LowerCase = 0;
145833548Sjkh	ndirent.de_StartCluster = newcluster;
145933548Sjkh	ndirent.de_FileSize = 0;
146033548Sjkh	error = createde(&ndirent, pdep, &dep, cnp);
146133548Sjkh	if (error)
146233548Sjkh		goto bad;
146333548Sjkh	*ap->a_vpp = DETOV(dep);
146433548Sjkh	return (0);
146533548Sjkh
146633548Sjkhbad:
146733548Sjkh	clusterfree(pmp, newcluster, NULL);
146833548Sjkhbad2:
146933548Sjkh	return (error);
14702893Sdfr}
14712893Sdfr
147211921Sphkstatic int
14732893Sdfrmsdosfs_rmdir(ap)
14742893Sdfr	struct vop_rmdir_args /* {
14752893Sdfr		struct vnode *a_dvp;
14762893Sdfr		struct vnode *a_vp;
14772893Sdfr		struct componentname *a_cnp;
14782893Sdfr	} */ *ap;
14792893Sdfr{
1480111742Sdes	struct vnode *vp = ap->a_vp;
1481111742Sdes	struct vnode *dvp = ap->a_dvp;
1482111742Sdes	struct componentname *cnp = ap->a_cnp;
1483111742Sdes	struct denode *ip, *dp;
148433548Sjkh	int error;
1485111742Sdes
148633548Sjkh	ip = VTODE(vp);
148733548Sjkh	dp = VTODE(dvp);
14882893Sdfr
14892893Sdfr	/*
149033548Sjkh	 * Verify the directory is empty (and valid).
149133548Sjkh	 * (Rmdir ".." won't be valid since
149233548Sjkh	 *  ".." will contain a reference to
149333548Sjkh	 *  the current directory and thus be
149433548Sjkh	 *  non-empty.)
14952893Sdfr	 */
149633548Sjkh	error = 0;
149733548Sjkh	if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
14982893Sdfr		error = ENOTEMPTY;
14992893Sdfr		goto out;
15002893Sdfr	}
15012893Sdfr	/*
15022893Sdfr	 * Delete the entry from the directory.  For dos filesystems this
15032893Sdfr	 * gets rid of the directory entry on disk, the in memory copy
15042893Sdfr	 * still exists but the de_refcnt is <= 0.  This prevents it from
15052893Sdfr	 * being found by deget().  When the vput() on dep is done we give
15062893Sdfr	 * up access and eventually msdosfs_reclaim() will be called which
15072893Sdfr	 * will remove it from the denode cache.
15082893Sdfr	 */
150933548Sjkh	error = removede(dp, ip);
15103152Sphk	if (error)
15112893Sdfr		goto out;
15122893Sdfr	/*
15132893Sdfr	 * This is where we decrement the link count in the parent
15142893Sdfr	 * directory.  Since dos filesystems don't do this we just purge
151535823Smsmith	 * the name cache.
15162893Sdfr	 */
151733548Sjkh	cache_purge(dvp);
15182893Sdfr	/*
15192893Sdfr	 * Truncate the directory that is being deleted.
15202893Sdfr	 */
1521234605Strasz	error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
152233548Sjkh	cache_purge(vp);
152335823Smsmith
152433548Sjkhout:
152533548Sjkh	return (error);
15262893Sdfr}
15272893Sdfr
15282893Sdfr/*
15292893Sdfr * DOS filesystems don't know what symlinks are.
15302893Sdfr */
153111921Sphkstatic int
15322893Sdfrmsdosfs_symlink(ap)
15332893Sdfr	struct vop_symlink_args /* {
15342893Sdfr		struct vnode *a_dvp;
15352893Sdfr		struct vnode **a_vpp;
15362893Sdfr		struct componentname *a_cnp;
15372893Sdfr		struct vattr *a_vap;
15382893Sdfr		char *a_target;
15392893Sdfr	} */ *ap;
15402893Sdfr{
154133548Sjkh	return (EOPNOTSUPP);
15422893Sdfr}
15432893Sdfr
154411921Sphkstatic int
15452893Sdfrmsdosfs_readdir(ap)
15462893Sdfr	struct vop_readdir_args /* {
15472893Sdfr		struct vnode *a_vp;
15482893Sdfr		struct uio *a_uio;
15492893Sdfr		struct ucred *a_cred;
15502893Sdfr		int *a_eofflag;
155124787Sbde		int *a_ncookies;
155224787Sbde		u_long **a_cookies;
15532893Sdfr	} */ *ap;
15542893Sdfr{
1555172027Sbde	struct mbnambuf nb;
15562893Sdfr	int error = 0;
15572893Sdfr	int diff;
15582893Sdfr	long n;
155933548Sjkh	int blsize;
15602893Sdfr	long on;
15612893Sdfr	u_long cn;
1562131523Stjr	uint64_t fileno;
156333548Sjkh	u_long dirsperblk;
15642893Sdfr	long bias = 0;
156533548Sjkh	daddr_t bn, lbn;
15662893Sdfr	struct buf *bp;
15672893Sdfr	struct denode *dep = VTODE(ap->a_vp);
15682893Sdfr	struct msdosfsmount *pmp = dep->de_pmp;
15692893Sdfr	struct direntry *dentp;
157033548Sjkh	struct dirent dirbuf;
15712893Sdfr	struct uio *uio = ap->a_uio;
157233548Sjkh	u_long *cookies = NULL;
15733167Sdfr	int ncookies = 0;
157433548Sjkh	off_t offset, off;
157533548Sjkh	int chksum = -1;
15762893Sdfr
15772893Sdfr#ifdef MSDOSFS_DEBUG
157833548Sjkh	printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
157933548Sjkh	    ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
15802893Sdfr#endif
15812893Sdfr
15822893Sdfr	/*
15832893Sdfr	 * msdosfs_readdir() won't operate properly on regular files since
1584218909Sbrucec	 * it does i/o only with the filesystem vnode, and hence can
15852893Sdfr	 * retrieve the wrong block from the buffer cache for a plain file.
15862893Sdfr	 * So, fail attempts to readdir() on a plain file.
15872893Sdfr	 */
15882893Sdfr	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
158933548Sjkh		return (ENOTDIR);
15902893Sdfr
15912893Sdfr	/*
159233548Sjkh	 * To be safe, initialize dirbuf
159333548Sjkh	 */
159433548Sjkh	bzero(dirbuf.d_name, sizeof(dirbuf.d_name));
159533548Sjkh
159633548Sjkh	/*
15972893Sdfr	 * If the user buffer is smaller than the size of one dos directory
15982893Sdfr	 * entry or the file offset is not a multiple of the size of a
15992893Sdfr	 * directory entry, then we fail the read.
16002893Sdfr	 */
160133848Smsmith	off = offset = uio->uio_offset;
160233848Smsmith	if (uio->uio_resid < sizeof(struct direntry) ||
160333548Sjkh	    (offset & (sizeof(struct direntry) - 1)))
160433548Sjkh		return (EINVAL);
16052893Sdfr
160633548Sjkh	if (ap->a_ncookies) {
160733548Sjkh		ncookies = uio->uio_resid / 16;
1608184205Sdes		cookies = malloc(ncookies * sizeof(u_long), M_TEMP,
1609111119Simp		       M_WAITOK);
161033548Sjkh		*ap->a_cookies = cookies;
161133548Sjkh		*ap->a_ncookies = ncookies;
161233548Sjkh	}
161333548Sjkh
161433548Sjkh	dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
161533548Sjkh
16162893Sdfr	/*
16172893Sdfr	 * If they are reading from the root directory then, we simulate
16182893Sdfr	 * the . and .. entries since these don't exist in the root
16192893Sdfr	 * directory.  We also set the offset bias to make up for having to
16202893Sdfr	 * simulate these entries. By this I mean that at file offset 64 we
16212893Sdfr	 * read the first entry in the root directory that lives on disk.
16222893Sdfr	 */
162333548Sjkh	if (dep->de_StartCluster == MSDOSFSROOT
162433548Sjkh	    || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
162533548Sjkh#if 0
162633548Sjkh		printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
162733548Sjkh		    offset);
162833548Sjkh#endif
16292893Sdfr		bias = 2 * sizeof(struct direntry);
163033548Sjkh		if (offset < bias) {
163133548Sjkh			for (n = (int)offset / sizeof(struct direntry);
163233548Sjkh			     n < 2; n++) {
163333548Sjkh				if (FAT32(pmp))
1634131523Stjr					fileno = (uint64_t)cntobn(pmp,
163533548Sjkh								 pmp->pm_rootdirblk)
163633548Sjkh							  * dirsperblk;
163733548Sjkh				else
1638131523Stjr					fileno = 1;
1639166340Srodrigc				if (pmp->pm_flags & MSDOSFS_LARGEFS) {
1640166340Srodrigc					dirbuf.d_fileno =
1641166340Srodrigc					    msdosfs_fileno_map(pmp->pm_mountp,
1642166340Srodrigc					    fileno);
1643166340Srodrigc				} else {
1644166340Srodrigc
1645166340Srodrigc					dirbuf.d_fileno = (uint32_t)fileno;
1646166340Srodrigc				}
164733548Sjkh				dirbuf.d_type = DT_DIR;
164833548Sjkh				switch (n) {
164933548Sjkh				case 0:
165033548Sjkh					dirbuf.d_namlen = 1;
165133548Sjkh					strcpy(dirbuf.d_name, ".");
165233548Sjkh					break;
165333548Sjkh				case 1:
165433548Sjkh					dirbuf.d_namlen = 2;
165533548Sjkh					strcpy(dirbuf.d_name, "..");
165633548Sjkh					break;
165733548Sjkh				}
165833548Sjkh				dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
165933548Sjkh				if (uio->uio_resid < dirbuf.d_reclen)
166033548Sjkh					goto out;
1661111741Sdes				error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
166233548Sjkh				if (error)
166333548Sjkh					goto out;
166433848Smsmith				offset += sizeof(struct direntry);
166533848Smsmith				off = offset;
166633548Sjkh				if (cookies) {
166733548Sjkh					*cookies++ = offset;
166833548Sjkh					if (--ncookies <= 0)
166933548Sjkh						goto out;
167033548Sjkh				}
16712893Sdfr			}
16722893Sdfr		}
16732893Sdfr	}
167433548Sjkh
1675172027Sbde	mbnambuf_init(&nb);
167633548Sjkh	off = offset;
167733548Sjkh	while (uio->uio_resid > 0) {
167833548Sjkh		lbn = de_cluster(pmp, offset - bias);
167933548Sjkh		on = (offset - bias) & pmp->pm_crbomask;
168033548Sjkh		n = min(pmp->pm_bpcluster - on, uio->uio_resid);
168133548Sjkh		diff = dep->de_FileSize - (offset - bias);
168212265Sbde		if (diff <= 0)
168312265Sbde			break;
168433548Sjkh		n = min(n, diff);
168533548Sjkh		error = pcbmap(dep, lbn, &bn, &cn, &blsize);
16862893Sdfr		if (error)
16872893Sdfr			break;
168833548Sjkh		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
16892893Sdfr		if (error) {
16902893Sdfr			brelse(bp);
169133548Sjkh			return (error);
16922893Sdfr		}
169333548Sjkh		n = min(n, blsize - bp->b_resid);
1694117200Strhodes		if (n == 0) {
1695117200Strhodes			brelse(bp);
1696117200Strhodes			return (EIO);
1697117200Strhodes		}
16982893Sdfr
16992893Sdfr		/*
170033548Sjkh		 * Convert from dos directory entries to fs-independent
170133548Sjkh		 * directory entries.
17022893Sdfr		 */
170333548Sjkh		for (dentp = (struct direntry *)(bp->b_data + on);
170433548Sjkh		     (char *)dentp < bp->b_data + on + n;
170533548Sjkh		     dentp++, offset += sizeof(struct direntry)) {
170633548Sjkh#if 0
170733548Sjkh			printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
170833548Sjkh			    dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
170933548Sjkh#endif
17102893Sdfr			/*
171133548Sjkh			 * If this is an unused entry, we can stop.
17122893Sdfr			 */
171333548Sjkh			if (dentp->deName[0] == SLOT_EMPTY) {
171433548Sjkh				brelse(bp);
171533548Sjkh				goto out;
171633548Sjkh			}
17172893Sdfr			/*
171833548Sjkh			 * Skip deleted entries.
17192893Sdfr			 */
172033548Sjkh			if (dentp->deName[0] == SLOT_DELETED) {
172133548Sjkh				chksum = -1;
1722172027Sbde				mbnambuf_init(&nb);
172333548Sjkh				continue;
17242893Sdfr			}
17258876Srgrimes
172633548Sjkh			/*
172733548Sjkh			 * Handle Win95 long directory entries
172833548Sjkh			 */
172933548Sjkh			if (dentp->deAttributes == ATTR_WIN95) {
173033548Sjkh				if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
173133548Sjkh					continue;
1732172027Sbde				chksum = win2unixfn(&nb,
1733172027Sbde				    (struct winentry *)dentp, chksum, pmp);
173433548Sjkh				continue;
173533548Sjkh			}
17362893Sdfr
17372893Sdfr			/*
173833548Sjkh			 * Skip volume labels
17392893Sdfr			 */
174033548Sjkh			if (dentp->deAttributes & ATTR_VOLUME) {
174133548Sjkh				chksum = -1;
1742172027Sbde				mbnambuf_init(&nb);
174333548Sjkh				continue;
17442893Sdfr			}
174533548Sjkh			/*
174633548Sjkh			 * This computation of d_fileno must match
174733548Sjkh			 * the computation of va_fileid in
174833548Sjkh			 * msdosfs_getattr.
174933548Sjkh			 */
175033548Sjkh			if (dentp->deAttributes & ATTR_DIRECTORY) {
175133548Sjkh				fileno = getushort(dentp->deStartCluster);
175233548Sjkh				if (FAT32(pmp))
175333548Sjkh					fileno |= getushort(dentp->deHighClust) << 16;
175433548Sjkh				/* if this is the root directory */
175533548Sjkh				if (fileno == MSDOSFSROOT)
175633548Sjkh					if (FAT32(pmp))
1757131523Stjr						fileno = (uint64_t)cntobn(pmp,
175833548Sjkh								pmp->pm_rootdirblk)
175933548Sjkh							 * dirsperblk;
176033548Sjkh					else
176133548Sjkh						fileno = 1;
176233548Sjkh				else
1763131523Stjr					fileno = (uint64_t)cntobn(pmp, fileno) *
1764131523Stjr					    dirsperblk;
176533548Sjkh				dirbuf.d_type = DT_DIR;
176633548Sjkh			} else {
1767171758Sbde				fileno = (uoff_t)offset /
1768171758Sbde				    sizeof(struct direntry);
176933548Sjkh				dirbuf.d_type = DT_REG;
177033548Sjkh			}
1771166340Srodrigc			if (pmp->pm_flags & MSDOSFS_LARGEFS) {
1772166340Srodrigc				dirbuf.d_fileno =
1773166340Srodrigc				    msdosfs_fileno_map(pmp->pm_mountp, fileno);
1774166340Srodrigc			} else
1775166340Srodrigc				dirbuf.d_fileno = (uint32_t)fileno;
1776166340Srodrigc
1777203827Skib			if (chksum != winChksum(dentp->deName)) {
177833548Sjkh				dirbuf.d_namlen = dos2unixfn(dentp->deName,
177933548Sjkh				    (u_char *)dirbuf.d_name,
178041275Sdt				    dentp->deLowerCase |
178141275Sdt					((pmp->pm_flags & MSDOSFSMNT_SHORTNAME) ?
178241275Sdt					(LCASE_BASE | LCASE_EXT) : 0),
1783120492Sfjoe				    pmp);
1784172027Sbde				mbnambuf_init(&nb);
1785120492Sfjoe			} else
1786172027Sbde				mbnambuf_flush(&nb, &dirbuf);
178733548Sjkh			chksum = -1;
178833548Sjkh			dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
178933548Sjkh			if (uio->uio_resid < dirbuf.d_reclen) {
179033548Sjkh				brelse(bp);
179133548Sjkh				goto out;
179233548Sjkh			}
1793111741Sdes			error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
179433548Sjkh			if (error) {
179533548Sjkh				brelse(bp);
179633548Sjkh				goto out;
179733548Sjkh			}
179833548Sjkh			if (cookies) {
179933848Smsmith				*cookies++ = offset + sizeof(struct direntry);
180033548Sjkh				if (--ncookies <= 0) {
180133548Sjkh					brelse(bp);
180233548Sjkh					goto out;
180333548Sjkh				}
180433548Sjkh			}
180533848Smsmith			off = offset + sizeof(struct direntry);
18062893Sdfr		}
18072893Sdfr		brelse(bp);
18082893Sdfr	}
180933548Sjkhout:
181033548Sjkh	/* Subtract unused cookies */
181133548Sjkh	if (ap->a_ncookies)
181233548Sjkh		*ap->a_ncookies -= ncookies;
181333548Sjkh
181433848Smsmith	uio->uio_offset = off;
18152893Sdfr
18162893Sdfr	/*
18173935Spst	 * Set the eofflag (NFS uses it)
18182893Sdfr	 */
181946568Speter	if (ap->a_eofflag) {
182033548Sjkh		if (dep->de_FileSize - (offset - bias) <= 0)
18213167Sdfr			*ap->a_eofflag = 1;
18223167Sdfr		else
18233167Sdfr			*ap->a_eofflag = 0;
182446568Speter	}
182533548Sjkh	return (error);
18262893Sdfr}
18272893Sdfr
1828171522Sbde/*-
1829171522Sbde * a_vp   - pointer to the file's vnode
1830171522Sbde * a_bn   - logical block number within the file (cluster number for us)
1831171522Sbde * a_bop  - where to return the bufobj of the special file containing the fs
1832171522Sbde * a_bnp  - where to return the "physical" block number corresponding to a_bn
1833171522Sbde *          (relative to the special file; units are blocks of size DEV_BSIZE)
1834171522Sbde * a_runp - where to return the "run past" a_bn.  This is the count of logical
1835171522Sbde *          blocks whose physical blocks (together with a_bn's physical block)
1836171522Sbde *          are contiguous.
1837171522Sbde * a_runb - where to return the "run before" a_bn.
18382893Sdfr */
183911921Sphkstatic int
18402893Sdfrmsdosfs_bmap(ap)
18412893Sdfr	struct vop_bmap_args /* {
18422893Sdfr		struct vnode *a_vp;
184396572Sphk		daddr_t a_bn;
1844137726Sphk		struct bufobj **a_bop;
184596572Sphk		daddr_t *a_bnp;
18462893Sdfr		int *a_runp;
184710551Sdyson		int *a_runb;
18482893Sdfr	} */ *ap;
18492893Sdfr{
1850171522Sbde	struct denode *dep;
1851171523Sbde	struct mount *mp;
1852171522Sbde	struct msdosfsmount *pmp;
1853171522Sbde	struct vnode *vp;
1854171523Sbde	daddr_t runbn;
1855171522Sbde	u_long cn;
1856171523Sbde	int bnpercn, error, maxio, maxrun, run;
18572893Sdfr
1858171522Sbde	vp = ap->a_vp;
1859171522Sbde	dep = VTODE(vp);
1860171522Sbde	pmp = dep->de_pmp;
1861137726Sphk	if (ap->a_bop != NULL)
1862171522Sbde		*ap->a_bop = &pmp->pm_devvp->v_bufobj;
18632893Sdfr	if (ap->a_bnp == NULL)
186433548Sjkh		return (0);
1865171522Sbde	if (ap->a_runp != NULL)
18662893Sdfr		*ap->a_runp = 0;
1867171522Sbde	if (ap->a_runb != NULL)
186810551Sdyson		*ap->a_runb = 0;
1869171522Sbde	cn = ap->a_bn;
1870171522Sbde	if (cn != ap->a_bn)
1871171522Sbde		return (EFBIG);
1872171522Sbde	error = pcbmap(dep, cn, ap->a_bnp, NULL, NULL);
1873171523Sbde	if (error != 0 || (ap->a_runp == NULL && ap->a_runb == NULL))
1874171523Sbde		return (error);
1875171523Sbde
1876171523Sbde	mp = vp->v_mount;
1877171523Sbde	maxio = mp->mnt_iosize_max / mp->mnt_stat.f_iosize;
1878171523Sbde	bnpercn = de_cn2bn(pmp, 1);
1879171523Sbde	if (ap->a_runp != NULL) {
1880171523Sbde		maxrun = ulmin(maxio - 1, pmp->pm_maxcluster - cn);
1881171523Sbde		for (run = 1; run <= maxrun; run++) {
1882171523Sbde			if (pcbmap(dep, cn + run, &runbn, NULL, NULL) != 0 ||
1883171523Sbde			    runbn != *ap->a_bnp + run * bnpercn)
1884171523Sbde				break;
1885171523Sbde		}
1886171523Sbde		*ap->a_runp = run - 1;
1887171523Sbde	}
1888171523Sbde	if (ap->a_runb != NULL) {
1889171523Sbde		maxrun = ulmin(maxio - 1, cn);
1890171523Sbde		for (run = 1; run < maxrun; run++) {
1891171523Sbde			if (pcbmap(dep, cn - run, &runbn, NULL, NULL) != 0 ||
1892171523Sbde			    runbn != *ap->a_bnp - run * bnpercn)
1893171523Sbde				break;
1894171523Sbde		}
1895171523Sbde		*ap->a_runb = run - 1;
1896171523Sbde	}
1897171523Sbde	return (0);
18982893Sdfr}
18992893Sdfr
190011921Sphkstatic int
19012893Sdfrmsdosfs_strategy(ap)
19022893Sdfr	struct vop_strategy_args /* {
190337384Sjulian		struct vnode *a_vp;
19042893Sdfr		struct buf *a_bp;
19052893Sdfr	} */ *ap;
19062893Sdfr{
19072893Sdfr	struct buf *bp = ap->a_bp;
1908136991Sphk	struct denode *dep = VTODE(ap->a_vp);
1909137036Sphk	struct bufobj *bo;
19102893Sdfr	int error = 0;
191192363Smckusick	daddr_t blkno;
19122893Sdfr
19132893Sdfr	/*
19142893Sdfr	 * If we don't already know the filesystem relative block number
19152893Sdfr	 * then get it using pcbmap().  If pcbmap() returns the block
19162893Sdfr	 * number as -1 then we've got a hole in the file.  DOS filesystems
19172893Sdfr	 * don't allow files with holes, so we shouldn't ever see this.
19182893Sdfr	 */
19192893Sdfr	if (bp->b_blkno == bp->b_lblkno) {
192092363Smckusick		error = pcbmap(dep, bp->b_lblkno, &blkno, 0, 0);
192192363Smckusick		bp->b_blkno = blkno;
192233548Sjkh		if (error) {
192333548Sjkh			bp->b_error = error;
192458934Sphk			bp->b_ioflags |= BIO_ERROR;
192559249Sphk			bufdone(bp);
1926186194Strasz			return (0);
192733548Sjkh		}
192833548Sjkh		if ((long)bp->b_blkno == -1)
192933548Sjkh			vfs_bio_clrbuf(bp);
19302893Sdfr	}
19312893Sdfr	if (bp->b_blkno == -1) {
193259249Sphk		bufdone(bp);
193333548Sjkh		return (0);
19342893Sdfr	}
19352893Sdfr	/*
19362893Sdfr	 * Read/write the block from/to the disk that contains the desired
19372893Sdfr	 * file block.
19382893Sdfr	 */
1939121205Sphk	bp->b_iooffset = dbtob(bp->b_blkno);
1940137036Sphk	bo = dep->de_pmp->pm_bo;
1941140051Sphk	BO_STRATEGY(bo, bp);
194233548Sjkh	return (0);
19432893Sdfr}
19442893Sdfr
194511921Sphkstatic int
19462893Sdfrmsdosfs_print(ap)
19472893Sdfr	struct vop_print_args /* {
19482893Sdfr		struct vnode *vp;
19492893Sdfr	} */ *ap;
19502893Sdfr{
19512893Sdfr	struct denode *dep = VTODE(ap->a_vp);
19522893Sdfr
1953111841Snjl	printf("\tstartcluster %lu, dircluster %lu, diroffset %lu, ",
1954103559Snjl	       dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1955189120Sjhb	printf("on dev %s\n", devtoname(dep->de_pmp->pm_dev));
195633548Sjkh	return (0);
19572893Sdfr}
19582893Sdfr
195911921Sphkstatic int
19602893Sdfrmsdosfs_pathconf(ap)
19612893Sdfr	struct vop_pathconf_args /* {
19622893Sdfr		struct vnode *a_vp;
19632893Sdfr		int a_name;
19642893Sdfr		int *a_retval;
19652893Sdfr	} */ *ap;
19662893Sdfr{
196733548Sjkh	struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp;
196833548Sjkh
19692893Sdfr	switch (ap->a_name) {
19702893Sdfr	case _PC_LINK_MAX:
19712893Sdfr		*ap->a_retval = 1;
197233548Sjkh		return (0);
19732893Sdfr	case _PC_NAME_MAX:
197433548Sjkh		*ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12;
197533548Sjkh		return (0);
19762893Sdfr	case _PC_PATH_MAX:
197733548Sjkh		*ap->a_retval = PATH_MAX;
197833548Sjkh		return (0);
19792893Sdfr	case _PC_CHOWN_RESTRICTED:
19802893Sdfr		*ap->a_retval = 1;
198133548Sjkh		return (0);
19822893Sdfr	case _PC_NO_TRUNC:
19832893Sdfr		*ap->a_retval = 0;
198433548Sjkh		return (0);
19852893Sdfr	default:
198633548Sjkh		return (EINVAL);
19872893Sdfr	}
198833548Sjkh	/* NOTREACHED */
19892893Sdfr}
19902893Sdfr
1991123873Strhodesstatic int
1992166774Spjdmsdosfs_vptofh(ap)
1993166774Spjd	struct vop_vptofh_args /* {
1994166774Spjd		struct vnode *a_vp;
1995166774Spjd		struct fid *a_fhp;
1996166774Spjd	} */ *ap;
1997166774Spjd{
1998166774Spjd	struct denode *dep;
1999166774Spjd	struct defid *defhp;
2000166774Spjd
2001166774Spjd	dep = VTODE(ap->a_vp);
2002166774Spjd	defhp = (struct defid *)ap->a_fhp;
2003166774Spjd	defhp->defid_len = sizeof(struct defid);
2004166774Spjd	defhp->defid_dirclust = dep->de_dirclust;
2005166774Spjd	defhp->defid_dirofs = dep->de_diroffset;
2006166774Spjd	/* defhp->defid_gen = dep->de_gen; */
2007166774Spjd	return (0);
2008166774Spjd}
2009166774Spjd
20102893Sdfr/* Global vfs data structures for msdosfs */
2011138290Sphkstruct vop_vector msdosfs_vnodeops = {
2012138290Sphk	.vop_default =		&default_vnodeops,
2013140196Sphk
2014138290Sphk	.vop_access =		msdosfs_access,
2015138290Sphk	.vop_bmap =		msdosfs_bmap,
2016138290Sphk	.vop_cachedlookup =	msdosfs_lookup,
2017140965Speadar	.vop_open =		msdosfs_open,
2018138290Sphk	.vop_close =		msdosfs_close,
2019138290Sphk	.vop_create =		msdosfs_create,
2020138290Sphk	.vop_fsync =		msdosfs_fsync,
2021138290Sphk	.vop_getattr =		msdosfs_getattr,
2022138290Sphk	.vop_inactive =		msdosfs_inactive,
2023138290Sphk	.vop_link =		msdosfs_link,
2024138290Sphk	.vop_lookup =		vfs_cache_lookup,
2025138290Sphk	.vop_mkdir =		msdosfs_mkdir,
2026138290Sphk	.vop_mknod =		msdosfs_mknod,
2027138290Sphk	.vop_pathconf =		msdosfs_pathconf,
2028138290Sphk	.vop_print =		msdosfs_print,
2029138290Sphk	.vop_read =		msdosfs_read,
2030138290Sphk	.vop_readdir =		msdosfs_readdir,
2031138290Sphk	.vop_reclaim =		msdosfs_reclaim,
2032138290Sphk	.vop_remove =		msdosfs_remove,
2033138290Sphk	.vop_rename =		msdosfs_rename,
2034138290Sphk	.vop_rmdir =		msdosfs_rmdir,
2035138290Sphk	.vop_setattr =		msdosfs_setattr,
2036138290Sphk	.vop_strategy =		msdosfs_strategy,
2037138290Sphk	.vop_symlink =		msdosfs_symlink,
2038138290Sphk	.vop_write =		msdosfs_write,
2039166774Spjd	.vop_vptofh =		msdosfs_vptofh,
20402893Sdfr};
2041