xref: /netbsd-src/sys/fs/msdosfs/msdosfs_vnops.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: msdosfs_vnops.c,v 1.68 2010/07/22 18:08:11 njoly Exp $	*/
2 
3 /*-
4  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
6  * All rights reserved.
7  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by TooLs GmbH.
20  * 4. The name of TooLs GmbH may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 /*
35  * Written by Paul Popelka (paulp@uts.amdahl.com)
36  *
37  * You can do anything you want with this software, just don't say you wrote
38  * it, and don't remove this notice.
39  *
40  * This software is provided "as is".
41  *
42  * The author supplies this software to be publicly redistributed on the
43  * understanding that the author is not responsible for the correct
44  * functioning of this software in any circumstances and is not liable for
45  * any damages caused by this software.
46  *
47  * October 1992
48  */
49 
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.68 2010/07/22 18:08:11 njoly Exp $");
52 
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/namei.h>
56 #include <sys/resourcevar.h>	/* defines plimit structure in proc struct */
57 #include <sys/kernel.h>
58 #include <sys/file.h>		/* define FWRITE ... */
59 #include <sys/stat.h>
60 #include <sys/buf.h>
61 #include <sys/proc.h>
62 #include <sys/mount.h>
63 #include <sys/fstrans.h>
64 #include <sys/vnode.h>
65 #include <sys/signalvar.h>
66 #include <sys/malloc.h>
67 #include <sys/dirent.h>
68 #include <sys/lockf.h>
69 #include <sys/kauth.h>
70 
71 #include <miscfs/genfs/genfs.h>
72 #include <miscfs/specfs/specdev.h> /* XXX */	/* defines v_rdev */
73 
74 #include <uvm/uvm_extern.h>
75 
76 #include <fs/msdosfs/bpb.h>
77 #include <fs/msdosfs/direntry.h>
78 #include <fs/msdosfs/denode.h>
79 #include <fs/msdosfs/msdosfsmount.h>
80 #include <fs/msdosfs/fat.h>
81 
82 /*
83  * Some general notes:
84  *
85  * In the ufs filesystem the inodes, superblocks, and indirect blocks are
86  * read/written using the vnode for the filesystem. Blocks that represent
87  * the contents of a file are read/written using the vnode for the file
88  * (including directories when they are read/written as files). This
89  * presents problems for the dos filesystem because data that should be in
90  * an inode (if dos had them) resides in the directory itself.  Since we
91  * must update directory entries without the benefit of having the vnode
92  * for the directory we must use the vnode for the filesystem.  This means
93  * that when a directory is actually read/written (via read, write, or
94  * readdir, or seek) we must use the vnode for the filesystem instead of
95  * the vnode for the directory as would happen in ufs. This is to insure we
96  * retrieve the correct block from the buffer cache since the hash value is
97  * based upon the vnode address and the desired block number.
98  */
99 
100 /*
101  * Create a regular file. On entry the directory to contain the file being
102  * created is locked.  We must release before we return. We must also free
103  * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
104  * only if the SAVESTART bit in cn_flags is clear on success.
105  */
106 int
107 msdosfs_create(void *v)
108 {
109 	struct vop_create_args /* {
110 		struct vnode *a_dvp;
111 		struct vnode **a_vpp;
112 		struct componentname *a_cnp;
113 		struct vattr *a_vap;
114 	} */ *ap = v;
115 	struct componentname *cnp = ap->a_cnp;
116 	struct denode ndirent;
117 	struct denode *dep;
118 	struct denode *pdep = VTODE(ap->a_dvp);
119 	int error;
120 
121 #ifdef MSDOSFS_DEBUG
122 	printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
123 #endif
124 
125 	fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
126 	/*
127 	 * If this is the root directory and there is no space left we
128 	 * can't do anything.  This is because the root directory can not
129 	 * change size.
130 	 */
131 	if (pdep->de_StartCluster == MSDOSFSROOT
132 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
133 		error = ENOSPC;
134 		goto bad;
135 	}
136 
137 	/*
138 	 * Create a directory entry for the file, then call createde() to
139 	 * have it installed. NOTE: DOS files are always executable.  We
140 	 * use the absence of the owner write bit to make the file
141 	 * readonly.
142 	 */
143 #ifdef DIAGNOSTIC
144 	if ((cnp->cn_flags & HASBUF) == 0)
145 		panic("msdosfs_create: no name");
146 #endif
147 	memset(&ndirent, 0, sizeof(ndirent));
148 	if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
149 		goto bad;
150 
151 	ndirent.de_Attributes = (ap->a_vap->va_mode & S_IWUSR) ?
152 				ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
153 	ndirent.de_StartCluster = 0;
154 	ndirent.de_FileSize = 0;
155 	ndirent.de_dev = pdep->de_dev;
156 	ndirent.de_devvp = pdep->de_devvp;
157 	ndirent.de_pmp = pdep->de_pmp;
158 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
159 	DETIMES(&ndirent, NULL, NULL, NULL, pdep->de_pmp->pm_gmtoff);
160 	if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
161 		goto bad;
162 	if ((cnp->cn_flags & SAVESTART) == 0)
163 		PNBUF_PUT(cnp->cn_pnbuf);
164 	fstrans_done(ap->a_dvp->v_mount);
165 	VN_KNOTE(ap->a_dvp, NOTE_WRITE);
166 	vput(ap->a_dvp);
167 	*ap->a_vpp = DETOV(dep);
168 	return (0);
169 
170 bad:
171 	fstrans_done(ap->a_dvp->v_mount);
172 	PNBUF_PUT(cnp->cn_pnbuf);
173 	vput(ap->a_dvp);
174 	return (error);
175 }
176 
177 int
178 msdosfs_close(void *v)
179 {
180 	struct vop_close_args /* {
181 		struct vnode *a_vp;
182 		int a_fflag;
183 		kauth_cred_t a_cred;
184 	} */ *ap = v;
185 	struct vnode *vp = ap->a_vp;
186 	struct denode *dep = VTODE(vp);
187 
188 	fstrans_start(vp->v_mount, FSTRANS_SHARED);
189 	mutex_enter(&vp->v_interlock);
190 	if (vp->v_usecount > 1)
191 		DETIMES(dep, NULL, NULL, NULL, dep->de_pmp->pm_gmtoff);
192 	mutex_exit(&vp->v_interlock);
193 	fstrans_done(vp->v_mount);
194 	return (0);
195 }
196 
197 static int
198 msdosfs_check_possible(struct vnode *vp, struct denode *dep, mode_t mode)
199 {
200 
201 	/*
202 	 * Disallow write attempts on read-only file systems;
203 	 * unless the file is a socket, fifo, or a block or
204 	 * character device resident on the file system.
205 	 */
206 	if (mode & VWRITE) {
207 		switch (vp->v_type) {
208 		case VDIR:
209 		case VLNK:
210 		case VREG:
211 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
212 				return (EROFS);
213 		default:
214 			break;
215 		}
216 	}
217 
218 	return 0;
219 }
220 
221 static int
222 msdosfs_check_permitted(struct vnode *vp, struct denode *dep, mode_t mode,
223     kauth_cred_t cred)
224 {
225 	struct msdosfsmount *pmp = dep->de_pmp;
226 	mode_t file_mode;
227 
228 	if ((dep->de_Attributes & ATTR_READONLY) == 0)
229 		file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
230 	else
231 		file_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
232 
233 	return genfs_can_access(vp->v_type,
234 	    file_mode & (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask),
235 	    pmp->pm_uid, pmp->pm_gid, mode, cred);
236 }
237 
238 int
239 msdosfs_access(void *v)
240 {
241 	struct vop_access_args /* {
242 		struct vnode *a_vp;
243 		int a_mode;
244 		kauth_cred_t a_cred;
245 	} */ *ap = v;
246 	struct vnode *vp = ap->a_vp;
247 	struct denode *dep = VTODE(vp);
248 	int error;
249 
250 	error = msdosfs_check_possible(vp, dep, ap->a_mode);
251 	if (error)
252 		return error;
253 
254 	error = msdosfs_check_permitted(vp, dep, ap->a_mode, ap->a_cred);
255 
256 	return error;
257 }
258 
259 int
260 msdosfs_getattr(void *v)
261 {
262 	struct vop_getattr_args /* {
263 		struct vnode *a_vp;
264 		struct vattr *a_vap;
265 		kauth_cred_t a_cred;
266 	} */ *ap = v;
267 	struct denode *dep = VTODE(ap->a_vp);
268 	struct msdosfsmount *pmp = dep->de_pmp;
269 	struct vattr *vap = ap->a_vap;
270 	mode_t mode;
271 	u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
272 	ino_t fileid;
273 
274 	fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
275 	DETIMES(dep, NULL, NULL, NULL, pmp->pm_gmtoff);
276 	vap->va_fsid = dep->de_dev;
277 	/*
278 	 * The following computation of the fileid must be the same as that
279 	 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
280 	 * doesn't work.
281 	 */
282 	if (dep->de_Attributes & ATTR_DIRECTORY) {
283 		fileid = cntobn(pmp, (ino_t)dep->de_StartCluster) * dirsperblk;
284 		if (dep->de_StartCluster == MSDOSFSROOT)
285 			fileid = 1;
286 	} else {
287 		fileid = cntobn(pmp, (ino_t)dep->de_dirclust) * dirsperblk;
288 		if (dep->de_dirclust == MSDOSFSROOT)
289 			fileid = roottobn(pmp, 0) * dirsperblk;
290 		fileid += dep->de_diroffset / sizeof(struct direntry);
291 	}
292 	vap->va_fileid = fileid;
293 	if ((dep->de_Attributes & ATTR_READONLY) == 0)
294 		mode = S_IRWXU|S_IRWXG|S_IRWXO;
295 	else
296 		mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
297 	vap->va_mode =
298 	    mode & (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
299 	vap->va_uid = pmp->pm_uid;
300 	vap->va_gid = pmp->pm_gid;
301 	vap->va_nlink = 1;
302 	vap->va_rdev = 0;
303 	vap->va_size = ap->a_vp->v_size;
304 	dos2unixtime(dep->de_MDate, dep->de_MTime, 0, pmp->pm_gmtoff,
305 	    &vap->va_mtime);
306 	if (dep->de_pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
307 		dos2unixtime(dep->de_ADate, 0, 0, pmp->pm_gmtoff,
308 		    &vap->va_atime);
309 		dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun,
310 		    pmp->pm_gmtoff, &vap->va_ctime);
311 	} else {
312 		vap->va_atime = vap->va_mtime;
313 		vap->va_ctime = vap->va_mtime;
314 	}
315 	vap->va_flags = 0;
316 	if ((dep->de_Attributes & ATTR_ARCHIVE) == 0)
317 		vap->va_mode  |= S_ARCH1;
318 	vap->va_gen = 0;
319 	vap->va_blocksize = pmp->pm_bpcluster;
320 	vap->va_bytes =
321 	    (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
322 	vap->va_type = ap->a_vp->v_type;
323 	fstrans_done(ap->a_vp->v_mount);
324 	return (0);
325 }
326 
327 int
328 msdosfs_setattr(void *v)
329 {
330 	struct vop_setattr_args /* {
331 		struct vnode *a_vp;
332 		struct vattr *a_vap;
333 		kauth_cred_t a_cred;
334 	} */ *ap = v;
335 	int error = 0, de_changed = 0;
336 	struct denode *dep = VTODE(ap->a_vp);
337 	struct msdosfsmount *pmp = dep->de_pmp;
338 	struct vnode *vp  = ap->a_vp;
339 	struct vattr *vap = ap->a_vap;
340 	kauth_cred_t cred = ap->a_cred;
341 
342 #ifdef MSDOSFS_DEBUG
343 	printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
344 	    ap->a_vp, vap, cred);
345 #endif
346 	/*
347 	 * Note we silently ignore uid or gid changes.
348 	 */
349 	if ((vap->va_type != VNON) || (vap->va_nlink != (nlink_t)VNOVAL) ||
350 	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
351 	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
352 	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL) ||
353 	    (vap->va_uid != VNOVAL && vap->va_uid != pmp->pm_uid) ||
354 	    (vap->va_gid != VNOVAL && vap->va_gid != pmp->pm_gid)) {
355 #ifdef MSDOSFS_DEBUG
356 		printf("msdosfs_setattr(): returning EINVAL\n");
357 		printf("    va_type %d, va_nlink %x, va_fsid %"PRIx64", va_fileid %llx\n",
358 		    vap->va_type, vap->va_nlink, vap->va_fsid,
359 		    (unsigned long long)vap->va_fileid);
360 		printf("    va_blocksize %lx, va_rdev %"PRIx64", va_bytes %"PRIx64", va_gen %lx\n",
361 		    vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
362 #endif
363 		return (EINVAL);
364 	}
365 	/*
366 	 * Silently ignore attributes modifications on directories.
367 	 */
368 	if (ap->a_vp->v_type == VDIR)
369 		return 0;
370 
371 	fstrans_start(vp->v_mount, FSTRANS_SHARED);
372 	if (vap->va_size != VNOVAL) {
373 		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
374 			error = EROFS;
375 			goto bad;
376 		}
377 		error = detrunc(dep, (u_long)vap->va_size, 0, cred);
378 		if (error)
379 			goto bad;
380 		de_changed = 1;
381 	}
382 	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
383 		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
384 			error = EROFS;
385 			goto bad;
386 		}
387 		error = genfs_can_chtimes(ap->a_vp, vap->va_vaflags,
388 		    pmp->pm_uid, cred);
389 		if (error)
390 			goto bad;
391 		if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
392 		    vap->va_atime.tv_sec != VNOVAL)
393 			unix2dostime(&vap->va_atime, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL);
394 		if (vap->va_mtime.tv_sec != VNOVAL)
395 			unix2dostime(&vap->va_mtime, pmp->pm_gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
396 		dep->de_Attributes |= ATTR_ARCHIVE;
397 		dep->de_flag |= DE_MODIFIED;
398 		de_changed = 1;
399 	}
400 	/*
401 	 * DOS files only have the ability to have their writability
402 	 * attribute set, so we use the owner write bit to set the readonly
403 	 * attribute.
404 	 */
405 	if (vap->va_mode != (mode_t)VNOVAL) {
406 		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
407 			error = EROFS;
408 			goto bad;
409 		}
410 		if (kauth_cred_geteuid(cred) != pmp->pm_uid &&
411 		    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
412 		    NULL)))
413 			goto bad;
414 		/* We ignore the read and execute bits. */
415 		if (vap->va_mode & S_IWUSR)
416 			dep->de_Attributes &= ~ATTR_READONLY;
417 		else
418 			dep->de_Attributes |= ATTR_READONLY;
419 		dep->de_flag |= DE_MODIFIED;
420 		de_changed = 1;
421 	}
422 	/*
423 	 * Allow the `archived' bit to be toggled.
424 	 */
425 	if (vap->va_flags != VNOVAL) {
426 		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
427 			error = EROFS;
428 			goto bad;
429 		}
430 		if (kauth_cred_geteuid(cred) != pmp->pm_uid &&
431 		    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
432 		    NULL)))
433 			goto bad;
434 		if (vap->va_flags & SF_ARCHIVED)
435 			dep->de_Attributes &= ~ATTR_ARCHIVE;
436 		else
437 			dep->de_Attributes |= ATTR_ARCHIVE;
438 		dep->de_flag |= DE_MODIFIED;
439 		de_changed = 1;
440 	}
441 
442 	if (de_changed) {
443 		VN_KNOTE(vp, NOTE_ATTRIB);
444 		error = deupdat(dep, 1);
445 		if (error)
446 			goto bad;
447 	}
448 
449 bad:
450 	fstrans_done(vp->v_mount);
451 	return error;
452 }
453 
454 int
455 msdosfs_read(void *v)
456 {
457 	struct vop_read_args /* {
458 		struct vnode *a_vp;
459 		struct uio *a_uio;
460 		int a_ioflag;
461 		kauth_cred_t a_cred;
462 	} */ *ap = v;
463 	int error = 0;
464 	int64_t diff;
465 	int blsize;
466 	long n;
467 	long on;
468 	daddr_t lbn;
469 	vsize_t bytelen;
470 	struct buf *bp;
471 	struct vnode *vp = ap->a_vp;
472 	struct denode *dep = VTODE(vp);
473 	struct msdosfsmount *pmp = dep->de_pmp;
474 	struct uio *uio = ap->a_uio;
475 
476 	/*
477 	 * If they didn't ask for any data, then we are done.
478 	 */
479 
480 	if (uio->uio_resid == 0)
481 		return (0);
482 	if (uio->uio_offset < 0)
483 		return (EINVAL);
484 	if (uio->uio_offset >= dep->de_FileSize)
485 		return (0);
486 
487 	fstrans_start(vp->v_mount, FSTRANS_SHARED);
488 	if (vp->v_type == VREG) {
489 		const int advice = IO_ADV_DECODE(ap->a_ioflag);
490 
491 		while (uio->uio_resid > 0) {
492 			bytelen = MIN(dep->de_FileSize - uio->uio_offset,
493 				      uio->uio_resid);
494 
495 			if (bytelen == 0)
496 				break;
497 			error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
498 			    UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
499 			if (error)
500 				break;
501 		}
502 		dep->de_flag |= DE_ACCESS;
503 		goto out;
504 	}
505 
506 	/* this loop is only for directories now */
507 	do {
508 		lbn = de_cluster(pmp, uio->uio_offset);
509 		on = uio->uio_offset & pmp->pm_crbomask;
510 		n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
511 		if (uio->uio_offset >= dep->de_FileSize)
512 			return (0);
513 		/* file size (and hence diff) may be up to 4GB */
514 		diff = dep->de_FileSize - uio->uio_offset;
515 		if (diff < n)
516 			n = (long) diff;
517 
518 		/* convert cluster # to sector # */
519 		error = pcbmap(dep, lbn, &lbn, 0, &blsize);
520 		if (error)
521 			goto bad;
522 
523 		/*
524 		 * If we are operating on a directory file then be sure to
525 		 * do i/o with the vnode for the filesystem instead of the
526 		 * vnode for the directory.
527 		 */
528 		error = bread(pmp->pm_devvp, de_bn2kb(pmp, lbn), blsize,
529 		    NOCRED, 0, &bp);
530 		n = MIN(n, pmp->pm_bpcluster - bp->b_resid);
531 		if (error) {
532 			brelse(bp, 0);
533 			goto bad;
534 		}
535 		error = uiomove((char *)bp->b_data + on, (int) n, uio);
536 		brelse(bp, 0);
537 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
538 
539 out:
540 	if ((ap->a_ioflag & IO_SYNC) == IO_SYNC)
541 		error = deupdat(dep, 1);
542 bad:
543 	fstrans_done(vp->v_mount);
544 	return (error);
545 }
546 
547 /*
548  * Write data to a file or directory.
549  */
550 int
551 msdosfs_write(void *v)
552 {
553 	struct vop_write_args /* {
554 		struct vnode *a_vp;
555 		struct uio *a_uio;
556 		int a_ioflag;
557 		kauth_cred_t a_cred;
558 	} */ *ap = v;
559 	int resid, extended = 0;
560 	int error = 0;
561 	int ioflag = ap->a_ioflag;
562 	u_long osize;
563 	u_long count;
564 	vsize_t bytelen;
565 	off_t oldoff;
566 	struct uio *uio = ap->a_uio;
567 	struct vnode *vp = ap->a_vp;
568 	struct denode *dep = VTODE(vp);
569 	struct msdosfsmount *pmp = dep->de_pmp;
570 	kauth_cred_t cred = ap->a_cred;
571 	bool async;
572 
573 #ifdef MSDOSFS_DEBUG
574 	printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
575 	    vp, uio, ioflag, cred);
576 	printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
577 	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
578 #endif
579 
580 	switch (vp->v_type) {
581 	case VREG:
582 		if (ioflag & IO_APPEND)
583 			uio->uio_offset = dep->de_FileSize;
584 		break;
585 	case VDIR:
586 		return EISDIR;
587 	default:
588 		panic("msdosfs_write(): bad file type");
589 	}
590 
591 	if (uio->uio_offset < 0)
592 		return (EINVAL);
593 
594 	if (uio->uio_resid == 0)
595 		return (0);
596 
597 	/* Don't bother to try to write files larger than the fs limit */
598 	if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)
599 		return (EFBIG);
600 
601 	fstrans_start(vp->v_mount, FSTRANS_SHARED);
602 	/*
603 	 * If the offset we are starting the write at is beyond the end of
604 	 * the file, then they've done a seek.  Unix filesystems allow
605 	 * files with holes in them, DOS doesn't so we must fill the hole
606 	 * with zeroed blocks.
607 	 */
608 	if (uio->uio_offset > dep->de_FileSize) {
609 		if ((error = deextend(dep, uio->uio_offset, cred)) != 0) {
610 			fstrans_done(vp->v_mount);
611 			return (error);
612 		}
613 	}
614 
615 	/*
616 	 * Remember some values in case the write fails.
617 	 */
618 	async = vp->v_mount->mnt_flag & MNT_ASYNC;
619 	resid = uio->uio_resid;
620 	osize = dep->de_FileSize;
621 
622 	/*
623 	 * If we write beyond the end of the file, extend it to its ultimate
624 	 * size ahead of the time to hopefully get a contiguous area.
625 	 */
626 	if (uio->uio_offset + resid > osize) {
627 		count = de_clcount(pmp, uio->uio_offset + resid) -
628 			de_clcount(pmp, osize);
629 		if ((error = extendfile(dep, count, NULL, NULL, 0)))
630 			goto errexit;
631 
632 		dep->de_FileSize = uio->uio_offset + resid;
633 		/* hint uvm to not read in extended part */
634 		uvm_vnp_setwritesize(vp, dep->de_FileSize);
635 		extended = 1;
636 	}
637 
638 	do {
639 		oldoff = uio->uio_offset;
640 		bytelen = uio->uio_resid;
641 
642 		error = ubc_uiomove(&vp->v_uobj, uio, bytelen,
643 		    IO_ADV_DECODE(ioflag), UBC_WRITE | UBC_UNMAP_FLAG(vp));
644 		if (error)
645 			break;
646 
647 		/*
648 		 * flush what we just wrote if necessary.
649 		 * XXXUBC simplistic async flushing.
650 		 */
651 
652 		if (!async && oldoff >> 16 != uio->uio_offset >> 16) {
653 			mutex_enter(&vp->v_interlock);
654 			error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16,
655 			    (uio->uio_offset >> 16) << 16, PGO_CLEANIT);
656 		}
657 	} while (error == 0 && uio->uio_resid > 0);
658 
659 	/* set final size */
660 	uvm_vnp_setsize(vp, dep->de_FileSize);
661 	if (error == 0 && ioflag & IO_SYNC) {
662 		mutex_enter(&vp->v_interlock);
663 		error = VOP_PUTPAGES(vp, trunc_page(oldoff),
664 		    round_page(oldoff + bytelen), PGO_CLEANIT | PGO_SYNCIO);
665 	}
666 	dep->de_flag |= DE_UPDATE;
667 
668 	/*
669 	 * If the write failed and they want us to, truncate the file back
670 	 * to the size it was before the write was attempted.
671 	 */
672 errexit:
673 	if (resid > uio->uio_resid)
674 		VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
675 	if (error) {
676 		detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
677 		uio->uio_offset -= resid - uio->uio_resid;
678 		uio->uio_resid = resid;
679 	} else if ((ioflag & IO_SYNC) == IO_SYNC)
680 		error = deupdat(dep, 1);
681 	fstrans_done(vp->v_mount);
682 	KASSERT(vp->v_size == dep->de_FileSize);
683 	return (error);
684 }
685 
686 int
687 msdosfs_update(struct vnode *vp, const struct timespec *acc,
688     const struct timespec *mod, int flags)
689 {
690 	struct buf *bp;
691 	struct direntry *dirp;
692 	struct denode *dep;
693 	int error;
694 
695 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
696 		return (0);
697 	dep = VTODE(vp);
698 	DETIMES(dep, acc, mod, NULL, dep->de_pmp->pm_gmtoff);
699 	if ((dep->de_flag & DE_MODIFIED) == 0)
700 		return (0);
701 	dep->de_flag &= ~DE_MODIFIED;
702 	if (dep->de_Attributes & ATTR_DIRECTORY)
703 		return (0);
704 	if (dep->de_refcnt <= 0)
705 		return (0);
706 	error = readde(dep, &bp, &dirp);
707 	if (error)
708 		return (error);
709 	DE_EXTERNALIZE(dirp, dep);
710 	if (flags & (UPDATE_WAIT|UPDATE_DIROP))
711 		return (bwrite(bp));
712 	else {
713 		bdwrite(bp);
714 		return (0);
715 	}
716 }
717 
718 /*
719  * Flush the blocks of a file to disk.
720  *
721  * This function is worthless for vnodes that represent directories. Maybe we
722  * could just do a sync if they try an fsync on a directory file.
723  */
724 int
725 msdosfs_remove(void *v)
726 {
727 	struct vop_remove_args /* {
728 		struct vnode *a_dvp;
729 		struct vnode *a_vp;
730 		struct componentname *a_cnp;
731 	} */ *ap = v;
732 	struct denode *dep = VTODE(ap->a_vp);
733 	struct denode *ddep = VTODE(ap->a_dvp);
734 	int error;
735 
736 	fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
737 	if (ap->a_vp->v_type == VDIR)
738 		error = EPERM;
739 	else
740 		error = removede(ddep, dep);
741 #ifdef MSDOSFS_DEBUG
742 	printf("msdosfs_remove(), dep %p, v_usecount %d\n",
743 		dep, ap->a_vp->v_usecount);
744 #endif
745 	VN_KNOTE(ap->a_vp, NOTE_DELETE);
746 	VN_KNOTE(ap->a_dvp, NOTE_WRITE);
747 	if (ddep == dep)
748 		vrele(ap->a_vp);
749 	else
750 		vput(ap->a_vp);	/* causes msdosfs_inactive() to be called
751 				 * via vrele() */
752 	vput(ap->a_dvp);
753 	fstrans_done(ap->a_dvp->v_mount);
754 	return (error);
755 }
756 
757 /*
758  * Renames on files require moving the denode to a new hash queue since the
759  * denode's location is used to compute which hash queue to put the file
760  * in. Unless it is a rename in place.  For example "mv a b".
761  *
762  * What follows is the basic algorithm:
763  *
764  * if (file move) {
765  *	if (dest file exists) {
766  *		remove dest file
767  *	}
768  *	if (dest and src in same directory) {
769  *		rewrite name in existing directory slot
770  *	} else {
771  *		write new entry in dest directory
772  *		update offset and dirclust in denode
773  *		move denode to new hash chain
774  *		clear old directory entry
775  *	}
776  * } else {
777  *	directory move
778  *	if (dest directory exists) {
779  *		if (dest is not empty) {
780  *			return ENOTEMPTY
781  *		}
782  *		remove dest directory
783  *	}
784  *	if (dest and src in same directory) {
785  *		rewrite name in existing entry
786  *	} else {
787  *		be sure dest is not a child of src directory
788  *		write entry in dest directory
789  *		update "." and ".." in moved directory
790  *		update offset and dirclust in denode
791  *		move denode to new hash chain
792  *		clear old directory entry for moved directory
793  *	}
794  * }
795  *
796  * On entry:
797  *	source's parent directory is unlocked
798  *	source file or directory is unlocked
799  *	destination's parent directory is locked
800  *	destination file or directory is locked if it exists
801  *
802  * On exit:
803  *	all denodes should be released
804  *
805  * Notes:
806  * I'm not sure how the memory containing the pathnames pointed at by the
807  * componentname structures is freed, there may be some memory bleeding
808  * for each rename done.
809  *
810  * --More-- Notes:
811  * This routine needs help.  badly.
812  */
813 int
814 msdosfs_rename(void *v)
815 {
816 	struct vop_rename_args /* {
817 		struct vnode *a_fdvp;
818 		struct vnode *a_fvp;
819 		struct componentname *a_fcnp;
820 		struct vnode *a_tdvp;
821 		struct vnode *a_tvp;
822 		struct componentname *a_tcnp;
823 	} */ *ap = v;
824 	struct vnode *tvp = ap->a_tvp;
825 	struct vnode *tdvp = ap->a_tdvp;
826 	struct vnode *fvp = ap->a_fvp;
827 	struct vnode *fdvp = ap->a_fdvp;
828 	struct componentname *tcnp = ap->a_tcnp;
829 	struct componentname *fcnp = ap->a_fcnp;
830 	struct denode *ip, *xp, *dp, *zp;
831 	u_char toname[11], oldname[11];
832 	u_long from_diroffset, to_diroffset;
833 	u_char to_count;
834 	int doingdirectory = 0, newparent = 0;
835 	int error;
836 	u_long cn;
837 	daddr_t bn;
838 	struct msdosfsmount *pmp;
839 	struct direntry *dotdotp;
840 	struct buf *bp;
841 	int fdvp_dorele = 0;
842 
843 	pmp = VFSTOMSDOSFS(fdvp->v_mount);
844 
845 #ifdef DIAGNOSTIC
846 	if ((tcnp->cn_flags & HASBUF) == 0 ||
847 	    (fcnp->cn_flags & HASBUF) == 0)
848 		panic("msdosfs_rename: no name");
849 #endif
850 	/*
851 	 * Check for cross-device rename.
852 	 */
853 	if ((fvp->v_mount != tdvp->v_mount) ||
854 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
855 		error = EXDEV;
856 abortit:
857 		VOP_ABORTOP(tdvp, tcnp);
858 		if (tdvp == tvp)
859 			vrele(tdvp);
860 		else
861 			vput(tdvp);
862 		if (tvp)
863 			vput(tvp);
864 		VOP_ABORTOP(fdvp, fcnp);
865 		vrele(fdvp);
866 		vrele(fvp);
867 		return (error);
868 	}
869 
870 	/*
871 	 * If source and dest are the same, do nothing.
872 	 */
873 	if (tvp == fvp) {
874 		error = 0;
875 		goto abortit;
876 	}
877 
878 	/*
879 	 * XXX: This can deadlock since we hold tdvp/tvp locked.
880 	 * But I'm not going to fix it now.
881 	 */
882 	if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
883 		goto abortit;
884 	dp = VTODE(fdvp);
885 	ip = VTODE(fvp);
886 
887 	/*
888 	 * Be sure we are not renaming ".", "..", or an alias of ".". This
889 	 * leads to a crippled directory tree.  It's pretty tough to do a
890 	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
891 	 * doesn't work if the ".." entry is missing.
892 	 */
893 	if (ip->de_Attributes & ATTR_DIRECTORY) {
894 		/*
895 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
896 		 */
897 		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
898 		    dp == ip ||
899 		    (fcnp->cn_flags & ISDOTDOT) ||
900 		    (tcnp->cn_flags & ISDOTDOT) ||
901 		    (ip->de_flag & DE_RENAME)) {
902 			VOP_UNLOCK(fvp);
903 			error = EINVAL;
904 			goto abortit;
905 		}
906 		ip->de_flag |= DE_RENAME;
907 		doingdirectory++;
908 	}
909 	VN_KNOTE(fdvp, NOTE_WRITE);		/* XXXLUKEM/XXX: right place? */
910 
911 	fstrans_start(fdvp->v_mount, FSTRANS_SHARED);
912 	/*
913 	 * When the target exists, both the directory
914 	 * and target vnodes are returned locked.
915 	 */
916 	dp = VTODE(tdvp);
917 	xp = tvp ? VTODE(tvp) : NULL;
918 	/*
919 	 * Remember direntry place to use for destination
920 	 */
921 	to_diroffset = dp->de_fndoffset;
922 	to_count = dp->de_fndcnt;
923 
924 	/*
925 	 * If ".." must be changed (ie the directory gets a new
926 	 * parent) then the source directory must not be in the
927 	 * directory hierarchy above the target, as this would
928 	 * orphan everything below the source directory. Also
929 	 * the user must have write permission in the source so
930 	 * as to be able to change "..". We must repeat the call
931 	 * to namei, as the parent directory is unlocked by the
932 	 * call to doscheckpath().
933 	 */
934 	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
935 	VOP_UNLOCK(fvp);
936 	if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
937 		newparent = 1;
938 
939 	/*
940 	 * XXX: We can do this here because rename uses SAVEFART and
941 	 * therefore fdvp has at least two references (one doesn't
942 	 * belong to us, though, and that's evil).  We'll get
943 	 * another "extra" reference when we do relookup(), so we
944 	 * need to compensate.  We should *NOT* be doing this, but
945 	 * it works, so whatever.
946 	 */
947 	vrele(fdvp);
948 
949 	if (doingdirectory && newparent) {
950 		if (error)	/* write access check above */
951 			goto tdvpbad;
952 		if (xp != NULL)
953 			vput(tvp);
954 		tvp = NULL;
955 		/*
956 		 * doscheckpath() vput()'s dp,
957 		 * so we have to do a relookup afterwards
958 		 */
959 		if ((error = doscheckpath(ip, dp)) != 0)
960 			goto out;
961 		if ((tcnp->cn_flags & SAVESTART) == 0)
962 			panic("msdosfs_rename: lost to startdir");
963 		vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
964 		if ((error = relookup(tdvp, &tvp, tcnp)) != 0) {
965 			VOP_UNLOCK(tdvp);
966 			goto out;
967 		}
968 		/*
969 		 * XXX: SAVESTART causes us to get a reference, but
970 		 * that's released already above in doscheckpath()
971 		 */
972 		dp = VTODE(tdvp);
973 		xp = tvp ? VTODE(tvp) : NULL;
974 	}
975 
976 	if (xp != NULL) {
977 		/*
978 		 * Target must be empty if a directory and have no links
979 		 * to it. Also, ensure source and target are compatible
980 		 * (both directories, or both not directories).
981 		 */
982 		if (xp->de_Attributes & ATTR_DIRECTORY) {
983 			if (!dosdirempty(xp)) {
984 				error = ENOTEMPTY;
985 				goto tdvpbad;
986 			}
987 			if (!doingdirectory) {
988 				error = ENOTDIR;
989 				goto tdvpbad;
990 			}
991 		} else if (doingdirectory) {
992 			error = EISDIR;
993 			goto tdvpbad;
994 		}
995 		if ((error = removede(dp, xp)) != 0)
996 			goto tdvpbad;
997 		VN_KNOTE(tdvp, NOTE_WRITE);
998 		VN_KNOTE(tvp, NOTE_DELETE);
999 		cache_purge(tvp);
1000 		vput(tvp);
1001 		tvp = NULL;
1002 		xp = NULL;
1003 	}
1004 
1005 	/*
1006 	 * Convert the filename in tcnp into a dos filename. We copy this
1007 	 * into the denode and directory entry for the destination
1008 	 * file/directory.
1009 	 */
1010 	if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0) {
1011 		fstrans_done(fdvp->v_mount);
1012 		goto abortit;
1013 	}
1014 
1015 	/*
1016 	 * Since from wasn't locked at various places above,
1017 	 * have to do a relookup here.
1018 	 */
1019 	fcnp->cn_flags &= ~MODMASK;
1020 	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
1021 	if ((fcnp->cn_flags & SAVESTART) == 0)
1022 		panic("msdosfs_rename: lost from startdir");
1023 	VOP_UNLOCK(tdvp);
1024 	vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
1025 	if ((error = relookup(fdvp, &fvp, fcnp))) {
1026 		VOP_UNLOCK(fdvp);
1027 		vrele(ap->a_fvp);
1028 		vrele(tdvp);
1029 		fstrans_done(fdvp->v_mount);
1030 		return (error);
1031 	}
1032 	if (fvp == NULL) {
1033 		/*
1034 		 * From name has disappeared.
1035 		 */
1036 		if (doingdirectory)
1037 			panic("rename: lost dir entry");
1038 		vput(fdvp);
1039 		vrele(ap->a_fvp);
1040 		vrele(tdvp);
1041 		fstrans_done(fdvp->v_mount);
1042 		return 0;
1043 	}
1044 	fdvp_dorele = 1;
1045 	VOP_UNLOCK(fdvp);
1046 	xp = VTODE(fvp);
1047 	zp = VTODE(fdvp);
1048 	from_diroffset = zp->de_fndoffset;
1049 
1050 	/*
1051 	 * Ensure that the directory entry still exists and has not
1052 	 * changed till now. If the source is a file the entry may
1053 	 * have been unlinked or renamed. In either case there is
1054 	 * no further work to be done. If the source is a directory
1055 	 * then it cannot have been rmdir'ed or renamed; this is
1056 	 * prohibited by the DE_RENAME flag.
1057 	 */
1058 	if (xp != ip) {
1059 		if (doingdirectory)
1060 			panic("rename: lost dir entry");
1061 		vrele(ap->a_fvp);
1062 		xp = NULL;
1063 	} else {
1064 		vrele(fvp);
1065 		xp = NULL;
1066 
1067 		/*
1068 		 * First write a new entry in the destination
1069 		 * directory and mark the entry in the source directory
1070 		 * as deleted.  Then move the denode to the correct hash
1071 		 * chain for its new location in the filesystem.  And, if
1072 		 * we moved a directory, then update its .. entry to point
1073 		 * to the new parent directory.
1074 		 */
1075 		memcpy(oldname, ip->de_Name, 11);
1076 		memcpy(ip->de_Name, toname, 11);	/* update denode */
1077 		dp->de_fndoffset = to_diroffset;
1078 		dp->de_fndcnt = to_count;
1079 		error = createde(ip, dp, (struct denode **)0, tcnp);
1080 		if (error) {
1081 			memcpy(ip->de_Name, oldname, 11);
1082 			VOP_UNLOCK(fvp);
1083 			goto bad;
1084 		}
1085 		ip->de_refcnt++;
1086 		zp->de_fndoffset = from_diroffset;
1087 		if ((error = removede(zp, ip)) != 0) {
1088 			/* XXX should really panic here, fs is corrupt */
1089 			VOP_UNLOCK(fvp);
1090 			goto bad;
1091 		}
1092 		cache_purge(fvp);
1093 		if (!doingdirectory) {
1094 			error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
1095 				       &ip->de_dirclust, 0);
1096 			if (error) {
1097 				/* XXX should really panic here, fs is corrupt */
1098 				VOP_UNLOCK(fvp);
1099 				goto bad;
1100 			}
1101 			ip->de_diroffset = to_diroffset;
1102 			if (ip->de_dirclust != MSDOSFSROOT)
1103 				ip->de_diroffset &= pmp->pm_crbomask;
1104 		}
1105 		reinsert(ip);
1106 	}
1107 
1108 	/*
1109 	 * If we moved a directory to a new parent directory, then we must
1110 	 * fixup the ".." entry in the moved directory.
1111 	 */
1112 	if (doingdirectory && newparent) {
1113 		cn = ip->de_StartCluster;
1114 		if (cn == MSDOSFSROOT) {
1115 			/* this should never happen */
1116 			panic("msdosfs_rename: updating .. in root directory?");
1117 		} else
1118 			bn = cntobn(pmp, cn);
1119 		error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
1120 		    pmp->pm_bpcluster, NOCRED, B_MODIFY, &bp);
1121 		if (error) {
1122 			/* XXX should really panic here, fs is corrupt */
1123 			brelse(bp, 0);
1124 			VOP_UNLOCK(fvp);
1125 			goto bad;
1126 		}
1127 		dotdotp = (struct direntry *)bp->b_data + 1;
1128 		putushort(dotdotp->deStartCluster, dp->de_StartCluster);
1129 		if (FAT32(pmp)) {
1130 			putushort(dotdotp->deHighClust,
1131 				dp->de_StartCluster >> 16);
1132 		} else {
1133 			putushort(dotdotp->deHighClust, 0);
1134 		}
1135 		if ((error = bwrite(bp)) != 0) {
1136 			/* XXX should really panic here, fs is corrupt */
1137 			VOP_UNLOCK(fvp);
1138 			goto bad;
1139 		}
1140 	}
1141 
1142 	VN_KNOTE(fvp, NOTE_RENAME);
1143 	VOP_UNLOCK(fvp);
1144 bad:
1145 	if (tvp)
1146 		vput(tvp);
1147 	vrele(tdvp);
1148 out:
1149 	ip->de_flag &= ~DE_RENAME;
1150 	if (fdvp_dorele)
1151 		vrele(fdvp);
1152 	vrele(fvp);
1153 	fstrans_done(fdvp->v_mount);
1154 	return (error);
1155 
1156 	/* XXX: uuuh */
1157 tdvpbad:
1158 	VOP_UNLOCK(tdvp);
1159 	goto bad;
1160 }
1161 
1162 static const struct {
1163 	struct direntry dot;
1164 	struct direntry dotdot;
1165 } dosdirtemplate = {
1166 	{	".       ", "   ",			/* the . entry */
1167 		ATTR_DIRECTORY,				/* file attribute */
1168 		0,	 				/* reserved */
1169 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1170 		{ 0, 0 },				/* access date */
1171 		{ 0, 0 },				/* high bits of start cluster */
1172 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1173 		{ 0, 0 },				/* startcluster */
1174 		{ 0, 0, 0, 0 } 				/* filesize */
1175 	},
1176 	{	"..      ", "   ",			/* the .. entry */
1177 		ATTR_DIRECTORY,				/* file attribute */
1178 		0,	 				/* reserved */
1179 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1180 		{ 0, 0 },				/* access date */
1181 		{ 0, 0 },				/* high bits of start cluster */
1182 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1183 		{ 0, 0 },				/* startcluster */
1184 		{ 0, 0, 0, 0 }				/* filesize */
1185 	}
1186 };
1187 
1188 int
1189 msdosfs_mkdir(void *v)
1190 {
1191 	struct vop_mkdir_args /* {
1192 		struct vnode *a_dvp;
1193 		struvt vnode **a_vpp;
1194 		struvt componentname *a_cnp;
1195 		struct vattr *a_vap;
1196 	} */ *ap = v;
1197 	struct componentname *cnp = ap->a_cnp;
1198 	struct denode ndirent;
1199 	struct denode *dep;
1200 	struct denode *pdep = VTODE(ap->a_dvp);
1201 	int error;
1202 	int bn;
1203 	u_long newcluster, pcl;
1204 	daddr_t lbn;
1205 	struct direntry *denp;
1206 	struct msdosfsmount *pmp = pdep->de_pmp;
1207 	struct buf *bp;
1208 	int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
1209 
1210 	fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
1211 	/*
1212 	 * If this is the root directory and there is no space left we
1213 	 * can't do anything.  This is because the root directory can not
1214 	 * change size.
1215 	 */
1216 	if (pdep->de_StartCluster == MSDOSFSROOT
1217 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
1218 		error = ENOSPC;
1219 		goto bad2;
1220 	}
1221 
1222 	/*
1223 	 * Allocate a cluster to hold the about to be created directory.
1224 	 */
1225 	error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
1226 	if (error)
1227 		goto bad2;
1228 
1229 	memset(&ndirent, 0, sizeof(ndirent));
1230 	ndirent.de_pmp = pmp;
1231 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
1232 	DETIMES(&ndirent, NULL, NULL, NULL, pmp->pm_gmtoff);
1233 
1234 	/*
1235 	 * Now fill the cluster with the "." and ".." entries. And write
1236 	 * the cluster to disk.  This way it is there for the parent
1237 	 * directory to be pointing at if there were a crash.
1238 	 */
1239 	bn = cntobn(pmp, newcluster);
1240 	lbn = de_bn2kb(pmp, bn);
1241 	/* always succeeds */
1242 	bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
1243 	memset(bp->b_data, 0, pmp->pm_bpcluster);
1244 	memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
1245 	denp = (struct direntry *)bp->b_data;
1246 	putushort(denp[0].deStartCluster, newcluster);
1247 	putushort(denp[0].deCDate, ndirent.de_CDate);
1248 	putushort(denp[0].deCTime, ndirent.de_CTime);
1249 	denp[0].deCHundredth = ndirent.de_CHun;
1250 	putushort(denp[0].deADate, ndirent.de_ADate);
1251 	putushort(denp[0].deMDate, ndirent.de_MDate);
1252 	putushort(denp[0].deMTime, ndirent.de_MTime);
1253 	pcl = pdep->de_StartCluster;
1254 	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1255 		pcl = 0;
1256 	putushort(denp[1].deStartCluster, pcl);
1257 	putushort(denp[1].deCDate, ndirent.de_CDate);
1258 	putushort(denp[1].deCTime, ndirent.de_CTime);
1259 	denp[1].deCHundredth = ndirent.de_CHun;
1260 	putushort(denp[1].deADate, ndirent.de_ADate);
1261 	putushort(denp[1].deMDate, ndirent.de_MDate);
1262 	putushort(denp[1].deMTime, ndirent.de_MTime);
1263 	if (FAT32(pmp)) {
1264 		putushort(denp[0].deHighClust, newcluster >> 16);
1265 		putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
1266 	} else {
1267 		putushort(denp[0].deHighClust, 0);
1268 		putushort(denp[1].deHighClust, 0);
1269 	}
1270 
1271 	if (async)
1272 		bdwrite(bp);
1273 	else if ((error = bwrite(bp)) != 0)
1274 		goto bad;
1275 
1276 	/*
1277 	 * Now build up a directory entry pointing to the newly allocated
1278 	 * cluster.  This will be written to an empty slot in the parent
1279 	 * directory.
1280 	 */
1281 #ifdef DIAGNOSTIC
1282 	if ((cnp->cn_flags & HASBUF) == 0)
1283 		panic("msdosfs_mkdir: no name");
1284 #endif
1285 	if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
1286 		goto bad;
1287 
1288 	ndirent.de_Attributes = ATTR_DIRECTORY;
1289 	ndirent.de_StartCluster = newcluster;
1290 	ndirent.de_FileSize = 0;
1291 	ndirent.de_dev = pdep->de_dev;
1292 	ndirent.de_devvp = pdep->de_devvp;
1293 	if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
1294 		goto bad;
1295 	if ((cnp->cn_flags & SAVESTART) == 0)
1296 		PNBUF_PUT(cnp->cn_pnbuf);
1297 	VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
1298 	vput(ap->a_dvp);
1299 	*ap->a_vpp = DETOV(dep);
1300 	fstrans_done(ap->a_dvp->v_mount);
1301 	return (0);
1302 
1303 bad:
1304 	clusterfree(pmp, newcluster, NULL);
1305 bad2:
1306 	PNBUF_PUT(cnp->cn_pnbuf);
1307 	vput(ap->a_dvp);
1308 	fstrans_done(ap->a_dvp->v_mount);
1309 	return (error);
1310 }
1311 
1312 int
1313 msdosfs_rmdir(void *v)
1314 {
1315 	struct vop_rmdir_args /* {
1316 		struct vnode *a_dvp;
1317 		struct vnode *a_vp;
1318 		struct componentname *a_cnp;
1319 	} */ *ap = v;
1320 	struct vnode *vp = ap->a_vp;
1321 	struct vnode *dvp = ap->a_dvp;
1322 	struct componentname *cnp = ap->a_cnp;
1323 	struct denode *ip, *dp;
1324 	int error;
1325 
1326 	ip = VTODE(vp);
1327 	dp = VTODE(dvp);
1328 	/*
1329 	 * No rmdir "." please.
1330 	 */
1331 	if (dp == ip) {
1332 		vrele(dvp);
1333 		vput(vp);
1334 		return (EINVAL);
1335 	}
1336 	fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
1337 	/*
1338 	 * Verify the directory is empty (and valid).
1339 	 * (Rmdir ".." won't be valid since
1340 	 *  ".." will contain a reference to
1341 	 *  the current directory and thus be
1342 	 *  non-empty.)
1343 	 */
1344 	error = 0;
1345 	if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
1346 		error = ENOTEMPTY;
1347 		goto out;
1348 	}
1349 	/*
1350 	 * Delete the entry from the directory.  For dos filesystems this
1351 	 * gets rid of the directory entry on disk, the in memory copy
1352 	 * still exists but the de_refcnt is <= 0.  This prevents it from
1353 	 * being found by deget().  When the vput() on dep is done we give
1354 	 * up access and eventually msdosfs_reclaim() will be called which
1355 	 * will remove it from the denode cache.
1356 	 */
1357 	if ((error = removede(dp, ip)) != 0)
1358 		goto out;
1359 	/*
1360 	 * This is where we decrement the link count in the parent
1361 	 * directory.  Since dos filesystems don't do this we just purge
1362 	 * the name cache and let go of the parent directory denode.
1363 	 */
1364 	VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1365 	cache_purge(dvp);
1366 	vput(dvp);
1367 	dvp = NULL;
1368 	/*
1369 	 * Truncate the directory that is being deleted.
1370 	 */
1371 	error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
1372 	cache_purge(vp);
1373 out:
1374 	VN_KNOTE(vp, NOTE_DELETE);
1375 	if (dvp)
1376 		vput(dvp);
1377 	vput(vp);
1378 	fstrans_done(ap->a_dvp->v_mount);
1379 	return (error);
1380 }
1381 
1382 int
1383 msdosfs_readdir(void *v)
1384 {
1385 	struct vop_readdir_args /* {
1386 		struct vnode *a_vp;
1387 		struct uio *a_uio;
1388 		kauth_cred_t a_cred;
1389 		int *a_eofflag;
1390 		off_t **a_cookies;
1391 		int *a_ncookies;
1392 	} */ *ap = v;
1393 	int error = 0;
1394 	int diff;
1395 	long n;
1396 	int blsize;
1397 	long on;
1398 	long lost;
1399 	long count;
1400 	u_long cn;
1401 	ino_t fileno;
1402 	u_long dirsperblk;
1403 	long bias = 0;
1404 	daddr_t bn, lbn;
1405 	struct buf *bp;
1406 	struct denode *dep = VTODE(ap->a_vp);
1407 	struct msdosfsmount *pmp = dep->de_pmp;
1408 	struct direntry *dentp;
1409 	struct dirent *dirbuf;
1410 	struct uio *uio = ap->a_uio;
1411 	off_t *cookies = NULL;
1412 	int ncookies = 0, nc = 0;
1413 	off_t offset, uio_off;
1414 	int chksum = -1;
1415 
1416 #ifdef MSDOSFS_DEBUG
1417 	printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
1418 	    ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
1419 #endif
1420 
1421 	/*
1422 	 * msdosfs_readdir() won't operate properly on regular files since
1423 	 * it does i/o only with the filesystem vnode, and hence can
1424 	 * retrieve the wrong block from the buffer cache for a plain file.
1425 	 * So, fail attempts to readdir() on a plain file.
1426 	 */
1427 	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1428 		return (ENOTDIR);
1429 
1430 	/*
1431 	 * If the user buffer is smaller than the size of one dos directory
1432 	 * entry or the file offset is not a multiple of the size of a
1433 	 * directory entry, then we fail the read.
1434 	 */
1435 	count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
1436 	offset = uio->uio_offset;
1437 	if (count < sizeof(struct direntry) ||
1438 	    (offset & (sizeof(struct direntry) - 1)))
1439 		return (EINVAL);
1440 	lost = uio->uio_resid - count;
1441 	uio->uio_resid = count;
1442 	uio_off = uio->uio_offset;
1443 
1444 	fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
1445 
1446 	/* Allocate a temporary dirent buffer. */
1447 	dirbuf = malloc(sizeof(struct dirent), M_MSDOSFSTMP, M_WAITOK | M_ZERO);
1448 
1449 	if (ap->a_ncookies) {
1450 		nc = uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0);
1451 		cookies = malloc(nc * sizeof (off_t), M_TEMP, M_WAITOK);
1452 		*ap->a_cookies = cookies;
1453 	}
1454 
1455 	dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
1456 
1457 	/*
1458 	 * If they are reading from the root directory then, we simulate
1459 	 * the . and .. entries since these don't exist in the root
1460 	 * directory.  We also set the offset bias to make up for having to
1461 	 * simulate these entries. By this I mean that at file offset 64 we
1462 	 * read the first entry in the root directory that lives on disk.
1463 	 */
1464 	if (dep->de_StartCluster == MSDOSFSROOT
1465 	    || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
1466 #if 0
1467 		printf("msdosfs_readdir(): going after . or .. in root dir, "
1468 		    "offset %" PRIu64 "\n", offset);
1469 #endif
1470 		bias = 2 * sizeof(struct direntry);
1471 		if (offset < bias) {
1472 			for (n = (int)offset / sizeof(struct direntry);
1473 			     n < 2; n++) {
1474 				if (FAT32(pmp))
1475 					dirbuf->d_fileno = cntobn(pmp,
1476 					     (ino_t)pmp->pm_rootdirblk)
1477 					     * dirsperblk;
1478 				else
1479 					dirbuf->d_fileno = 1;
1480 				dirbuf->d_type = DT_DIR;
1481 				switch (n) {
1482 				case 0:
1483 					dirbuf->d_namlen = 1;
1484 					strlcpy(dirbuf->d_name, ".",
1485 					    sizeof(dirbuf->d_name));
1486 					break;
1487 				case 1:
1488 					dirbuf->d_namlen = 2;
1489 					strlcpy(dirbuf->d_name, "..",
1490 					    sizeof(dirbuf->d_name));
1491 					break;
1492 				}
1493 				dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
1494 				if (uio->uio_resid < dirbuf->d_reclen)
1495 					goto out;
1496 				error = uiomove(dirbuf, dirbuf->d_reclen, uio);
1497 				if (error)
1498 					goto out;
1499 				offset += sizeof(struct direntry);
1500 				uio_off = offset;
1501 				if (cookies) {
1502 					*cookies++ = offset;
1503 					ncookies++;
1504 					if (ncookies >= nc)
1505 						goto out;
1506 				}
1507 			}
1508 		}
1509 	}
1510 
1511 	while (uio->uio_resid > 0) {
1512 		lbn = de_cluster(pmp, offset - bias);
1513 		on = (offset - bias) & pmp->pm_crbomask;
1514 		n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
1515 		diff = dep->de_FileSize - (offset - bias);
1516 		if (diff <= 0)
1517 			break;
1518 		n = MIN(n, diff);
1519 		if ((error = pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0)
1520 			break;
1521 		error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
1522 		    NOCRED, 0, &bp);
1523 		if (error) {
1524 			brelse(bp, 0);
1525 			goto bad;
1526 		}
1527 		n = MIN(n, blsize - bp->b_resid);
1528 
1529 		/*
1530 		 * Convert from dos directory entries to fs-independent
1531 		 * directory entries.
1532 		 */
1533 		for (dentp = (struct direntry *)((char *)bp->b_data + on);
1534 		     (char *)dentp < (char *)bp->b_data + on + n;
1535 		     dentp++, offset += sizeof(struct direntry)) {
1536 #if 0
1537 
1538 			printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1539 			    dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1540 #endif
1541 			/*
1542 			 * If this is an unused entry, we can stop.
1543 			 */
1544 			if (dentp->deName[0] == SLOT_EMPTY) {
1545 				brelse(bp, 0);
1546 				goto out;
1547 			}
1548 			/*
1549 			 * Skip deleted entries.
1550 			 */
1551 			if (dentp->deName[0] == SLOT_DELETED) {
1552 				chksum = -1;
1553 				continue;
1554 			}
1555 
1556 			/*
1557 			 * Handle Win95 long directory entries
1558 			 */
1559 			if (dentp->deAttributes == ATTR_WIN95) {
1560 				if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
1561 					continue;
1562 				chksum = win2unixfn((struct winentry *)dentp,
1563 				    dirbuf, chksum);
1564 				continue;
1565 			}
1566 
1567 			/*
1568 			 * Skip volume labels
1569 			 */
1570 			if (dentp->deAttributes & ATTR_VOLUME) {
1571 				chksum = -1;
1572 				continue;
1573 			}
1574 			/*
1575 			 * This computation of d_fileno must match
1576 			 * the computation of va_fileid in
1577 			 * msdosfs_getattr.
1578 			 */
1579 			if (dentp->deAttributes & ATTR_DIRECTORY) {
1580 				fileno = getushort(dentp->deStartCluster);
1581 				if (FAT32(pmp))
1582 					fileno |= ((ino_t)getushort(dentp->deHighClust)) << 16;
1583 				/* if this is the root directory */
1584 				if (fileno == MSDOSFSROOT)
1585 					if (FAT32(pmp))
1586 						fileno = cntobn(pmp,
1587 						    (ino_t)pmp->pm_rootdirblk)
1588 						    * dirsperblk;
1589 					else
1590 						fileno = 1;
1591 				else
1592 					fileno = cntobn(pmp, fileno) * dirsperblk;
1593 				dirbuf->d_fileno = fileno;
1594 				dirbuf->d_type = DT_DIR;
1595 			} else {
1596 				dirbuf->d_fileno =
1597 				    offset / sizeof(struct direntry);
1598 				dirbuf->d_type = DT_REG;
1599 			}
1600 			if (chksum != winChksum(dentp->deName))
1601 				dirbuf->d_namlen = dos2unixfn(dentp->deName,
1602 				    (u_char *)dirbuf->d_name,
1603 				    pmp->pm_flags & MSDOSFSMNT_SHORTNAME);
1604 			else
1605 				dirbuf->d_name[dirbuf->d_namlen] = 0;
1606 			chksum = -1;
1607 			dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
1608 			if (uio->uio_resid < dirbuf->d_reclen) {
1609 				brelse(bp, 0);
1610 				goto out;
1611 			}
1612 			error = uiomove(dirbuf, dirbuf->d_reclen, uio);
1613 			if (error) {
1614 				brelse(bp, 0);
1615 				goto out;
1616 			}
1617 			uio_off = offset + sizeof(struct direntry);
1618 			if (cookies) {
1619 				*cookies++ = offset + sizeof(struct direntry);
1620 				ncookies++;
1621 				if (ncookies >= nc) {
1622 					brelse(bp, 0);
1623 					goto out;
1624 				}
1625 			}
1626 		}
1627 		brelse(bp, 0);
1628 	}
1629 
1630 out:
1631 	uio->uio_offset = uio_off;
1632 	uio->uio_resid += lost;
1633 	if (dep->de_FileSize - (offset - bias) <= 0)
1634 		*ap->a_eofflag = 1;
1635 	else
1636 		*ap->a_eofflag = 0;
1637 
1638 	if (ap->a_ncookies) {
1639 		if (error) {
1640 			free(*ap->a_cookies, M_TEMP);
1641 			*ap->a_ncookies = 0;
1642 			*ap->a_cookies = NULL;
1643 		} else
1644 			*ap->a_ncookies = ncookies;
1645 	}
1646 
1647 bad:
1648 	free(dirbuf, M_MSDOSFSTMP);
1649 	fstrans_done(ap->a_vp->v_mount);
1650 	return (error);
1651 }
1652 
1653 /*
1654  * vp  - address of vnode file the file
1655  * bn  - which cluster we are interested in mapping to a filesystem block number.
1656  * vpp - returns the vnode for the block special file holding the filesystem
1657  *	 containing the file of interest
1658  * bnp - address of where to return the filesystem relative block number
1659  */
1660 int
1661 msdosfs_bmap(void *v)
1662 {
1663 	struct vop_bmap_args /* {
1664 		struct vnode *a_vp;
1665 		daddr_t a_bn;
1666 		struct vnode **a_vpp;
1667 		daddr_t *a_bnp;
1668 		int *a_runp;
1669 	} */ *ap = v;
1670 	struct denode *dep = VTODE(ap->a_vp);
1671 	int run, maxrun;
1672 	daddr_t runbn;
1673 	int status;
1674 
1675 	if (ap->a_vpp != NULL)
1676 		*ap->a_vpp = dep->de_devvp;
1677 	if (ap->a_bnp == NULL)
1678 		return (0);
1679 	status = pcbmap(dep, ap->a_bn, ap->a_bnp, 0, 0);
1680 
1681 	/*
1682 	 * From FreeBSD:
1683 	 * A little kludgy, but we loop calling pcbmap until we
1684 	 * reach the end of the contiguous piece, or reach MAXPHYS.
1685 	 * Since it reduces disk I/Os, the "wasted" CPU is put to
1686 	 * good use (4 to 5 fold sequential read I/O improvement on USB
1687 	 * drives).
1688 	 */
1689 	if (ap->a_runp != NULL) {
1690 		/* taken from ufs_bmap */
1691 		maxrun = ulmin(MAXPHYS / dep->de_pmp->pm_bpcluster - 1,
1692 			       dep->de_pmp->pm_maxcluster - ap->a_bn);
1693 		for (run = 1; run <= maxrun; run++) {
1694 			if (pcbmap(dep, ap->a_bn + run, &runbn, NULL, NULL)
1695 			    != 0 || runbn !=
1696 			            *ap->a_bnp + de_cn2bn(dep->de_pmp, run))
1697 				break;
1698 		}
1699 		*ap->a_runp = run - 1;
1700 	}
1701 
1702 	/*
1703 	 * We need to scale *ap->a_bnp by sector_size/DEV_BSIZE
1704 	 */
1705 	*ap->a_bnp = de_bn2kb(dep->de_pmp, *ap->a_bnp);
1706 	return status;
1707 }
1708 
1709 int
1710 msdosfs_strategy(void *v)
1711 {
1712 	struct vop_strategy_args /* {
1713 		struct vnode *a_vp;
1714 		struct buf *a_bp;
1715 	} */ *ap = v;
1716 	struct vnode *vp = ap->a_vp;
1717 	struct buf *bp = ap->a_bp;
1718 	struct denode *dep = VTODE(bp->b_vp);
1719 	int error = 0;
1720 
1721 	if (vp->v_type == VBLK || vp->v_type == VCHR)
1722 		panic("msdosfs_strategy: spec");
1723 	/*
1724 	 * If we don't already know the filesystem relative block number
1725 	 * then get it using pcbmap().  If pcbmap() returns the block
1726 	 * number as -1 then we've got a hole in the file.  DOS filesystems
1727 	 * don't allow files with holes, so we shouldn't ever see this.
1728 	 */
1729 	if (bp->b_blkno == bp->b_lblkno) {
1730 		error = pcbmap(dep, de_bn2cn(dep->de_pmp, bp->b_lblkno),
1731 			       &bp->b_blkno, 0, 0);
1732 		if (error)
1733 			bp->b_blkno = -1;
1734 		if (bp->b_blkno == -1)
1735 			clrbuf(bp);
1736 		else
1737 			bp->b_blkno = de_bn2kb(dep->de_pmp, bp->b_blkno);
1738 	}
1739 	if (bp->b_blkno == -1) {
1740 		biodone(bp);
1741 		return (error);
1742 	}
1743 
1744 	/*
1745 	 * Read/write the block from/to the disk that contains the desired
1746 	 * file block.
1747 	 */
1748 
1749 	vp = dep->de_devvp;
1750 	return (VOP_STRATEGY(vp, bp));
1751 }
1752 
1753 int
1754 msdosfs_print(void *v)
1755 {
1756 	struct vop_print_args /* {
1757 		struct vnode *vp;
1758 	} */ *ap = v;
1759 	struct denode *dep = VTODE(ap->a_vp);
1760 
1761 	printf(
1762 	    "tag VT_MSDOSFS, startcluster %ld, dircluster %ld, diroffset %ld ",
1763 	    dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1764 	printf(" dev %llu, %llu ", (unsigned long long)major(dep->de_dev),
1765 	    (unsigned long long)minor(dep->de_dev));
1766 	printf("\n");
1767 	return (0);
1768 }
1769 
1770 int
1771 msdosfs_advlock(void *v)
1772 {
1773 	struct vop_advlock_args /* {
1774 		struct vnode *a_vp;
1775 		void *a_id;
1776 		int a_op;
1777 		struct flock *a_fl;
1778 		int a_flags;
1779 	} */ *ap = v;
1780 	struct denode *dep = VTODE(ap->a_vp);
1781 
1782 	return lf_advlock(ap, &dep->de_lockf, dep->de_FileSize);
1783 }
1784 
1785 int
1786 msdosfs_pathconf(void *v)
1787 {
1788 	struct vop_pathconf_args /* {
1789 		struct vnode *a_vp;
1790 		int a_name;
1791 		register_t *a_retval;
1792 	} */ *ap = v;
1793 
1794 	switch (ap->a_name) {
1795 	case _PC_LINK_MAX:
1796 		*ap->a_retval = 1;
1797 		return (0);
1798 	case _PC_NAME_MAX:
1799 		*ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_namemax;
1800 		return (0);
1801 	case _PC_PATH_MAX:
1802 		*ap->a_retval = PATH_MAX;
1803 		return (0);
1804 	case _PC_CHOWN_RESTRICTED:
1805 		*ap->a_retval = 1;
1806 		return (0);
1807 	case _PC_NO_TRUNC:
1808 		*ap->a_retval = 0;
1809 		return (0);
1810 	case _PC_SYNC_IO:
1811 		*ap->a_retval = 1;
1812 		return (0);
1813 	case _PC_FILESIZEBITS:
1814 		*ap->a_retval = 32;
1815 		return (0);
1816 	default:
1817 		return (EINVAL);
1818 	}
1819 	/* NOTREACHED */
1820 }
1821 
1822 int
1823 msdosfs_fsync(void *v)
1824 {
1825 	struct vop_fsync_args /* {
1826 		struct vnode *a_vp;
1827 		kauth_cred_t a_cred;
1828 		int a_flags;
1829 		off_t offlo;
1830 		off_t offhi;
1831 	} */ *ap = v;
1832 	struct vnode *vp = ap->a_vp;
1833 	int wait;
1834 	int error;
1835 
1836 	fstrans_start(vp->v_mount, FSTRANS_LAZY);
1837 	wait = (ap->a_flags & FSYNC_WAIT) != 0;
1838 	vflushbuf(vp, wait);
1839 	if ((ap->a_flags & FSYNC_DATAONLY) != 0)
1840 		error = 0;
1841 	else
1842 		error = msdosfs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0);
1843 
1844 	if (error == 0 && ap->a_flags & FSYNC_CACHE) {
1845 		struct denode *dep = VTODE(vp);
1846 		struct vnode *devvp = dep->de_devvp;
1847 
1848 		int l = 0;
1849 		error = VOP_IOCTL(devvp, DIOCCACHESYNC, &l, FWRITE,
1850 					  curlwp->l_cred);
1851 	}
1852 	fstrans_done(vp->v_mount);
1853 
1854 	return (error);
1855 }
1856 
1857 void
1858 msdosfs_detimes(struct denode *dep, const struct timespec *acc,
1859     const struct timespec *mod, const struct timespec *cre, int gmtoff)
1860 {
1861 	struct timespec *ts = NULL, tsb;
1862 
1863 	KASSERT(dep->de_flag & (DE_UPDATE | DE_CREATE | DE_ACCESS));
1864 	/* XXX just call getnanotime early and use result if needed? */
1865 	dep->de_flag |= DE_MODIFIED;
1866 	if (dep->de_flag & DE_UPDATE) {
1867 		if (mod == NULL) {
1868 			getnanotime(&tsb);
1869 			mod = ts = &tsb;
1870 		}
1871 		unix2dostime(mod, gmtoff, &dep->de_MDate, &dep->de_MTime, NULL);
1872 		dep->de_Attributes |= ATTR_ARCHIVE;
1873 	}
1874 	if ((dep->de_pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0) {
1875 		if (dep->de_flag & DE_ACCESS)  {
1876 			if (acc == NULL)
1877 				acc = ts == NULL ?
1878 				    (getnanotime(&tsb), ts = &tsb) : ts;
1879 			unix2dostime(acc, gmtoff, &dep->de_ADate, NULL, NULL);
1880 		}
1881 		if (dep->de_flag & DE_CREATE) {
1882 			if (cre == NULL)
1883 				cre = ts == NULL ?
1884 				    (getnanotime(&tsb), ts = &tsb) : ts;
1885 			unix2dostime(cre, gmtoff, &dep->de_CDate,
1886 			    &dep->de_CTime, &dep->de_CHun);
1887 		}
1888 	}
1889 
1890 	dep->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS);
1891 }
1892 
1893 /* Global vfs data structures for msdosfs */
1894 int (**msdosfs_vnodeop_p)(void *);
1895 const struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
1896 	{ &vop_default_desc, vn_default_error },
1897 	{ &vop_lookup_desc, msdosfs_lookup },		/* lookup */
1898 	{ &vop_create_desc, msdosfs_create },		/* create */
1899 	{ &vop_mknod_desc, genfs_eopnotsupp },		/* mknod */
1900 	{ &vop_open_desc, genfs_nullop },		/* open */
1901 	{ &vop_close_desc, msdosfs_close },		/* close */
1902 	{ &vop_access_desc, msdosfs_access },		/* access */
1903 	{ &vop_getattr_desc, msdosfs_getattr },		/* getattr */
1904 	{ &vop_setattr_desc, msdosfs_setattr },		/* setattr */
1905 	{ &vop_read_desc, msdosfs_read },		/* read */
1906 	{ &vop_write_desc, msdosfs_write },		/* write */
1907 	{ &vop_fcntl_desc, genfs_fcntl },		/* fcntl */
1908 	{ &vop_ioctl_desc, msdosfs_ioctl },		/* ioctl */
1909 	{ &vop_poll_desc, msdosfs_poll },		/* poll */
1910 	{ &vop_kqfilter_desc, genfs_kqfilter },		/* kqfilter */
1911 	{ &vop_revoke_desc, msdosfs_revoke },		/* revoke */
1912 	{ &vop_mmap_desc, msdosfs_mmap },		/* mmap */
1913 	{ &vop_fsync_desc, msdosfs_fsync },		/* fsync */
1914 	{ &vop_seek_desc, msdosfs_seek },		/* seek */
1915 	{ &vop_remove_desc, msdosfs_remove },		/* remove */
1916 	{ &vop_link_desc, genfs_eopnotsupp },		/* link */
1917 	{ &vop_rename_desc, msdosfs_rename },		/* rename */
1918 	{ &vop_mkdir_desc, msdosfs_mkdir },		/* mkdir */
1919 	{ &vop_rmdir_desc, msdosfs_rmdir },		/* rmdir */
1920 	{ &vop_symlink_desc, genfs_eopnotsupp },	/* symlink */
1921 	{ &vop_readdir_desc, msdosfs_readdir },		/* readdir */
1922 	{ &vop_readlink_desc, genfs_einval },		/* readlink */
1923 	{ &vop_abortop_desc, msdosfs_abortop },		/* abortop */
1924 	{ &vop_inactive_desc, msdosfs_inactive },	/* inactive */
1925 	{ &vop_reclaim_desc, msdosfs_reclaim },		/* reclaim */
1926 	{ &vop_lock_desc, genfs_lock },			/* lock */
1927 	{ &vop_unlock_desc, genfs_unlock },		/* unlock */
1928 	{ &vop_bmap_desc, msdosfs_bmap },		/* bmap */
1929 	{ &vop_strategy_desc, msdosfs_strategy },	/* strategy */
1930 	{ &vop_print_desc, msdosfs_print },		/* print */
1931 	{ &vop_islocked_desc, genfs_islocked },		/* islocked */
1932 	{ &vop_pathconf_desc, msdosfs_pathconf },	/* pathconf */
1933 	{ &vop_advlock_desc, msdosfs_advlock },		/* advlock */
1934 	{ &vop_bwrite_desc, vn_bwrite },		/* bwrite */
1935 	{ &vop_getpages_desc, genfs_getpages },		/* getpages */
1936 	{ &vop_putpages_desc, genfs_putpages },		/* putpages */
1937 	{ NULL, NULL }
1938 };
1939 const struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
1940 	{ &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
1941