1/*	$NetBSD: msdosfs_vnops.c,v 1.112 2023/08/18 21:05:44 mrg 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.112 2023/08/18 21:05:44 mrg 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/vnode.h>
64#include <sys/signalvar.h>
65#include <sys/malloc.h>
66#include <sys/dirent.h>
67#include <sys/lockf.h>
68#include <sys/kauth.h>
69
70#include <miscfs/genfs/genfs.h>
71#include <miscfs/specfs/specdev.h> /* XXX */	/* defines v_rdev */
72
73#include <uvm/uvm_extern.h>
74
75#include <fs/msdosfs/bpb.h>
76#include <fs/msdosfs/direntry.h>
77#include <fs/msdosfs/denode.h>
78#include <fs/msdosfs/msdosfsmount.h>
79#include <fs/msdosfs/fat.h>
80
81/*
82 * Some general notes:
83 *
84 * In the ufs filesystem the inodes, superblocks, and indirect blocks are
85 * read/written using the vnode for the filesystem. Blocks that represent
86 * the contents of a file are read/written using the vnode for the file
87 * (including directories when they are read/written as files). This
88 * presents problems for the dos filesystem because data that should be in
89 * an inode (if dos had them) resides in the directory itself.  Since we
90 * must update directory entries without the benefit of having the vnode
91 * for the directory we must use the vnode for the filesystem.  This means
92 * that when a directory is actually read/written (via read, write, or
93 * readdir, or seek) we must use the vnode for the filesystem instead of
94 * the vnode for the directory as would happen in ufs. This is to insure we
95 * retrieve the correct block from the buffer cache since the hash value is
96 * based upon the vnode address and the desired block number.
97 */
98
99/*
100 * Create a regular file. On entry the directory to contain the file being
101 * created is locked.  We must release before we return.
102 */
103int
104msdosfs_create(void *v)
105{
106	struct vop_create_v3_args /* {
107		struct vnode *a_dvp;
108		struct vnode **a_vpp;
109		struct componentname *a_cnp;
110		struct vattr *a_vap;
111	} */ *ap = v;
112	struct componentname *cnp = ap->a_cnp;
113	struct denode ndirent;
114	struct denode *dep;
115	struct denode *pdep = VTODE(ap->a_dvp);
116	int error;
117
118#ifdef MSDOSFS_DEBUG
119	printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
120#endif
121
122	/*
123	 * If this is the root directory and there is no space left we
124	 * can't do anything.  This is because the root directory can not
125	 * change size.
126	 */
127	if (pdep->de_StartCluster == MSDOSFSROOT
128	    && pdep->de_crap.mlr_fndoffset >= pdep->de_FileSize) {
129		error = ENOSPC;
130		goto bad;
131	}
132
133	/*
134	 * Create a directory entry for the file, then call createde() to
135	 * have it installed. NOTE: DOS files are always executable.  We
136	 * use the absence of the owner write bit to make the file
137	 * readonly.
138	 */
139	memset(&ndirent, 0, sizeof(ndirent));
140	if ((error = msdosfs_uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
141		goto bad;
142
143	ndirent.de_Attributes = (ap->a_vap->va_mode & S_IWUSR) ?
144				ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
145	ndirent.de_StartCluster = 0;
146	ndirent.de_FileSize = 0;
147	ndirent.de_dev = pdep->de_dev;
148	ndirent.de_devvp = pdep->de_devvp;
149	ndirent.de_pmp = pdep->de_pmp;
150	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
151	DETIMES(&ndirent, NULL, NULL, NULL, pdep->de_pmp->pm_gmtoff);
152	if ((error = msdosfs_createde(&ndirent, pdep, &pdep->de_crap, &dep,
153	    cnp)) != 0)
154		goto bad;
155	*ap->a_vpp = DETOV(dep);
156	cache_enter(ap->a_dvp, *ap->a_vpp, cnp->cn_nameptr, cnp->cn_namelen,
157	    cnp->cn_flags);
158	return (0);
159
160bad:
161	return (error);
162}
163
164int
165msdosfs_close(void *v)
166{
167	struct vop_close_args /* {
168		struct vnode *a_vp;
169		int a_fflag;
170		kauth_cred_t a_cred;
171	} */ *ap = v;
172	struct vnode *vp = ap->a_vp;
173	struct denode *dep = VTODE(vp);
174
175	mutex_enter(vp->v_interlock);
176	if (vrefcnt(vp) > 1)
177		DETIMES(dep, NULL, NULL, NULL, dep->de_pmp->pm_gmtoff);
178	mutex_exit(vp->v_interlock);
179	return (0);
180}
181
182static int
183msdosfs_check_possible(struct vnode *vp, struct denode *dep, accmode_t accmode)
184{
185
186	/*
187	 * Disallow write attempts on read-only file systems;
188	 * unless the file is a socket, fifo, or a block or
189	 * character device resident on the file system.
190	 */
191	if (accmode & VWRITE) {
192		switch (vp->v_type) {
193		case VDIR:
194		case VLNK:
195		case VREG:
196			if (vp->v_mount->mnt_flag & MNT_RDONLY)
197				return (EROFS);
198		default:
199			break;
200		}
201	}
202
203	return 0;
204}
205
206static int
207msdosfs_check_permitted(struct vnode *vp, struct denode *dep, accmode_t accmode,
208    kauth_cred_t cred)
209{
210	struct msdosfsmount *pmp = dep->de_pmp;
211	mode_t file_mode;
212
213	if ((dep->de_Attributes & ATTR_READONLY) == 0)
214		file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
215	else
216		file_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
217
218	file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
219
220	return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(accmode,
221	    vp->v_type, file_mode), vp, NULL, genfs_can_access(vp, cred,
222	    pmp->pm_uid, pmp->pm_gid, file_mode, NULL, accmode));
223}
224
225int
226msdosfs_access(void *v)
227{
228	struct vop_access_args /* {
229		struct vnode *a_vp;
230		accmode_t a_accmode;
231		kauth_cred_t a_cred;
232	} */ *ap = v;
233	struct vnode *vp = ap->a_vp;
234	struct denode *dep = VTODE(vp);
235	int error;
236
237	error = msdosfs_check_possible(vp, dep, ap->a_accmode);
238	if (error)
239		return error;
240
241	error = msdosfs_check_permitted(vp, dep, ap->a_accmode, ap->a_cred);
242
243	return error;
244}
245
246int
247msdosfs_getattr(void *v)
248{
249	struct vop_getattr_args /* {
250		struct vnode *a_vp;
251		struct vattr *a_vap;
252		kauth_cred_t a_cred;
253	} */ *ap = v;
254	struct denode *dep = VTODE(ap->a_vp);
255	struct msdosfsmount *pmp = dep->de_pmp;
256	struct vattr *vap = ap->a_vap;
257	mode_t mode;
258	u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
259	ino_t fileid;
260
261	DETIMES(dep, NULL, NULL, NULL, pmp->pm_gmtoff);
262	vap->va_fsid = dep->de_dev;
263	/*
264	 * The following computation of the fileid must be the same as that
265	 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
266	 * doesn't work.
267	 */
268	if (dep->de_Attributes & ATTR_DIRECTORY) {
269		fileid = cntobn(pmp, (ino_t)dep->de_StartCluster) * dirsperblk;
270		if (dep->de_StartCluster == MSDOSFSROOT)
271			fileid = 1;
272	} else {
273		fileid = cntobn(pmp, (ino_t)dep->de_dirclust) * dirsperblk;
274		if (dep->de_dirclust == MSDOSFSROOT)
275			fileid = roottobn(pmp, 0) * dirsperblk;
276		fileid += dep->de_diroffset / sizeof(struct direntry);
277	}
278	vap->va_fileid = fileid;
279	if ((dep->de_Attributes & ATTR_READONLY) == 0)
280		mode = S_IRWXU|S_IRWXG|S_IRWXO;
281	else
282		mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
283	vap->va_mode =
284	    mode & (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
285	vap->va_uid = pmp->pm_uid;
286	vap->va_gid = pmp->pm_gid;
287	vap->va_nlink = 1;
288	vap->va_rdev = 0;
289	vap->va_size = ap->a_vp->v_size;
290	msdosfs_dos2unixtime(dep->de_MDate, dep->de_MTime, 0, pmp->pm_gmtoff,
291	    &vap->va_mtime);
292	if (dep->de_pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
293		msdosfs_dos2unixtime(dep->de_ADate, 0, 0, pmp->pm_gmtoff,
294		    &vap->va_atime);
295		msdosfs_dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun,
296		    pmp->pm_gmtoff, &vap->va_ctime);
297	} else {
298		vap->va_atime = vap->va_mtime;
299		vap->va_ctime = vap->va_mtime;
300	}
301	vap->va_flags = 0;
302	if ((dep->de_Attributes & ATTR_ARCHIVE) == 0) {
303		vap->va_flags |= SF_ARCHIVED;
304		vap->va_mode  |= S_ARCH1;
305	}
306	vap->va_gen = 0;
307	vap->va_blocksize = pmp->pm_bpcluster;
308	vap->va_bytes =
309	    (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
310	vap->va_type = ap->a_vp->v_type;
311	return (0);
312}
313
314int
315msdosfs_setattr(void *v)
316{
317	struct vop_setattr_args /* {
318		struct vnode *a_vp;
319		struct vattr *a_vap;
320		kauth_cred_t a_cred;
321	} */ *ap = v;
322	int error = 0, de_changed = 0;
323	struct denode *dep = VTODE(ap->a_vp);
324	struct msdosfsmount *pmp = dep->de_pmp;
325	struct vnode *vp  = ap->a_vp;
326	struct vattr *vap = ap->a_vap;
327	kauth_cred_t cred = ap->a_cred;
328
329#ifdef MSDOSFS_DEBUG
330	printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
331	    ap->a_vp, vap, cred);
332#endif
333	/*
334	 * Note we silently ignore uid or gid changes.
335	 */
336	if ((vap->va_type != VNON) || (vap->va_nlink != (nlink_t)VNOVAL) ||
337	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
338	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
339	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL) ||
340	    (vap->va_uid != VNOVAL && vap->va_uid != pmp->pm_uid) ||
341	    (vap->va_gid != VNOVAL && vap->va_gid != pmp->pm_gid)) {
342#ifdef MSDOSFS_DEBUG
343		printf("msdosfs_setattr(): returning EINVAL\n");
344		printf("    va_type %d, va_nlink %x, va_fsid %"PRIx64", va_fileid %llx\n",
345		    vap->va_type, vap->va_nlink, vap->va_fsid,
346		    (unsigned long long)vap->va_fileid);
347		printf("    va_blocksize %lx, va_rdev %"PRIx64", va_bytes %"PRIx64", va_gen %lx\n",
348		    vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
349#endif
350		return (EINVAL);
351	}
352	/*
353	 * Silently ignore attributes modifications on directories.
354	 */
355	if (ap->a_vp->v_type == VDIR)
356		return 0;
357
358	if (vap->va_size != VNOVAL) {
359		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
360			error = EROFS;
361			goto bad;
362		}
363		error = msdosfs_detrunc(dep, (u_long)vap->va_size, 0, cred);
364		if (error)
365			goto bad;
366		de_changed = 1;
367	}
368	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
369		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
370			error = EROFS;
371			goto bad;
372		}
373		error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES,
374		    ap->a_vp, NULL, genfs_can_chtimes(ap->a_vp, cred,
375			pmp->pm_uid, vap->va_vaflags));
376		if (error)
377			goto bad;
378		if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
379		    vap->va_atime.tv_sec != VNOVAL)
380			msdosfs_unix2dostime(&vap->va_atime, pmp->pm_gmtoff,
381			    &dep->de_ADate, NULL, NULL);
382		if (vap->va_mtime.tv_sec != VNOVAL)
383			msdosfs_unix2dostime(&vap->va_mtime, pmp->pm_gmtoff,
384			    &dep->de_MDate, &dep->de_MTime, NULL);
385		dep->de_Attributes |= ATTR_ARCHIVE;
386		dep->de_flag |= DE_MODIFIED;
387		de_changed = 1;
388	}
389	/*
390	 * DOS files only have the ability to have their writability
391	 * attribute set, so we use the owner write bit to set the readonly
392	 * attribute.
393	 */
394	if (vap->va_mode != (mode_t)VNOVAL) {
395		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
396			error = EROFS;
397			goto bad;
398		}
399		error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_FLAGS, vp,
400		    NULL, genfs_can_chflags(vp, cred, pmp->pm_uid, false));
401		if (error)
402			goto bad;
403		/* We ignore the read and execute bits. */
404		if (vap->va_mode & S_IWUSR)
405			dep->de_Attributes &= ~ATTR_READONLY;
406		else
407			dep->de_Attributes |= ATTR_READONLY;
408		dep->de_flag |= DE_MODIFIED;
409		de_changed = 1;
410	}
411	/*
412	 * Allow the `archived' bit to be toggled.
413	 */
414	if (vap->va_flags != VNOVAL) {
415		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
416			error = EROFS;
417			goto bad;
418		}
419		error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_FLAGS, vp,
420		    NULL, genfs_can_chflags(vp, cred, pmp->pm_uid, false));
421		if (error)
422			goto bad;
423		if (vap->va_flags & SF_ARCHIVED)
424			dep->de_Attributes &= ~ATTR_ARCHIVE;
425		else
426			dep->de_Attributes |= ATTR_ARCHIVE;
427		dep->de_flag |= DE_MODIFIED;
428		de_changed = 1;
429	}
430
431	if (de_changed) {
432		error = msdosfs_deupdat(dep, 1);
433		if (error)
434			goto bad;
435	}
436
437bad:
438	return error;
439}
440
441int
442msdosfs_read(void *v)
443{
444	struct vop_read_args /* {
445		struct vnode *a_vp;
446		struct uio *a_uio;
447		int a_ioflag;
448		kauth_cred_t a_cred;
449	} */ *ap = v;
450	int error = 0;
451	int64_t diff;
452	int blsize;
453	long n;
454	long on;
455	daddr_t lbn;
456	vsize_t bytelen;
457	struct buf *bp;
458	struct vnode *vp = ap->a_vp;
459	struct denode *dep = VTODE(vp);
460	struct msdosfsmount *pmp = dep->de_pmp;
461	struct uio *uio = ap->a_uio;
462
463	/*
464	 * If they didn't ask for any data, then we are done.
465	 */
466
467	if (uio->uio_resid == 0)
468		return (0);
469	if (uio->uio_offset < 0)
470		return (EINVAL);
471	if (uio->uio_offset >= dep->de_FileSize)
472		return (0);
473
474	if (vp->v_type == VREG) {
475		const int advice = IO_ADV_DECODE(ap->a_ioflag);
476
477		while (uio->uio_resid > 0) {
478			bytelen = MIN(dep->de_FileSize - uio->uio_offset,
479				      uio->uio_resid);
480
481			if (bytelen == 0)
482				break;
483			error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
484			    UBC_READ | UBC_PARTIALOK | UBC_VNODE_FLAGS(vp));
485			if (error)
486				break;
487		}
488		dep->de_flag |= DE_ACCESS;
489		goto out;
490	}
491
492	/* this loop is only for directories now */
493	do {
494		lbn = de_cluster(pmp, uio->uio_offset);
495		on = uio->uio_offset & pmp->pm_crbomask;
496		n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
497		if (uio->uio_offset >= dep->de_FileSize) {
498			return (0);
499		}
500		/* file size (and hence diff) may be up to 4GB */
501		diff = dep->de_FileSize - uio->uio_offset;
502		if (diff < n)
503			n = (long) diff;
504
505		/* convert cluster # to sector # */
506		error = msdosfs_pcbmap(dep, lbn, &lbn, 0, &blsize);
507		if (error)
508			goto bad;
509
510		/*
511		 * If we are operating on a directory file then be sure to
512		 * do i/o with the vnode for the filesystem instead of the
513		 * vnode for the directory.
514		 */
515		error = bread(pmp->pm_devvp, de_bn2kb(pmp, lbn), blsize,
516		    0, &bp);
517		if (error) {
518			goto bad;
519		}
520		n = MIN(n, pmp->pm_bpcluster - bp->b_resid);
521		error = uiomove((char *)bp->b_data + on, (int) n, uio);
522		brelse(bp, 0);
523	} while (error == 0 && uio->uio_resid > 0 && n != 0);
524
525out:
526	if ((ap->a_ioflag & IO_SYNC) == IO_SYNC) {
527		int uerror;
528
529		uerror = msdosfs_deupdat(dep, 1);
530		if (error == 0)
531			error = uerror;
532	}
533bad:
534	return (error);
535}
536
537/*
538 * Write data to a file or directory.
539 */
540int
541msdosfs_write(void *v)
542{
543	struct vop_write_args /* {
544		struct vnode *a_vp;
545		struct uio *a_uio;
546		int a_ioflag;
547		kauth_cred_t a_cred;
548	} */ *ap = v;
549	int resid;
550	int error = 0;
551	int ioflag = ap->a_ioflag;
552	u_long osize;
553	u_long count;
554	vsize_t bytelen;
555	off_t oldoff;
556	size_t rem;
557	struct uio *uio = ap->a_uio;
558	struct vnode *vp = ap->a_vp;
559	struct denode *dep = VTODE(vp);
560	struct msdosfsmount *pmp = dep->de_pmp;
561	kauth_cred_t cred = ap->a_cred;
562	bool async;
563
564#ifdef MSDOSFS_DEBUG
565	printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
566	    vp, uio, ioflag, cred);
567	printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
568	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
569#endif
570
571	switch (vp->v_type) {
572	case VREG:
573		if (ioflag & IO_APPEND)
574			uio->uio_offset = dep->de_FileSize;
575		break;
576	case VDIR:
577		return EISDIR;
578	default:
579		panic("msdosfs_write(): bad file type");
580	}
581
582	if (uio->uio_offset < 0)
583		return (EINVAL);
584
585	if (uio->uio_resid == 0)
586		return (0);
587
588	/* Don't bother to try to write files larger than the fs limit */
589	if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)
590		return (EFBIG);
591
592	/*
593	 * If the offset we are starting the write at is beyond the end of
594	 * the file, then they've done a seek.  Unix filesystems allow
595	 * files with holes in them, DOS doesn't so we must fill the hole
596	 * with zeroed blocks.
597	 */
598	if (uio->uio_offset > dep->de_FileSize) {
599		if ((error = msdosfs_deextend(dep, uio->uio_offset,
600		    cred)) != 0) {
601			return (error);
602		}
603	}
604
605	/*
606	 * Remember some values in case the write fails.
607	 */
608	async = vp->v_mount->mnt_flag & MNT_ASYNC;
609	resid = uio->uio_resid;
610	osize = dep->de_FileSize;
611
612	/*
613	 * If we write beyond the end of the file, extend it to its ultimate
614	 * size ahead of the time to hopefully get a contiguous area.
615	 */
616	if (uio->uio_offset + resid > osize) {
617		count = de_clcount(pmp, uio->uio_offset + resid) -
618			de_clcount(pmp, osize);
619		if ((error = msdosfs_extendfile(dep, count, NULL, NULL, 0)))
620			goto errexit;
621
622		dep->de_FileSize = uio->uio_offset + resid;
623		/* hint uvm to not read in extended part */
624		uvm_vnp_setwritesize(vp, dep->de_FileSize);
625		/* zero out the remainder of the last page */
626		rem = round_page(dep->de_FileSize) - dep->de_FileSize;
627		if (rem > 0)
628			ubc_zerorange(&vp->v_uobj, (off_t)dep->de_FileSize,
629			    rem, UBC_VNODE_FLAGS(vp));
630	}
631
632	do {
633		oldoff = uio->uio_offset;
634		bytelen = uio->uio_resid;
635
636		error = ubc_uiomove(&vp->v_uobj, uio, bytelen,
637		    IO_ADV_DECODE(ioflag), UBC_WRITE | UBC_VNODE_FLAGS(vp));
638		if (error)
639			break;
640
641		/*
642		 * flush what we just wrote if necessary.
643		 * XXXUBC simplistic async flushing.
644		 */
645
646		if (!async && oldoff >> 16 != uio->uio_offset >> 16) {
647			rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
648			error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16,
649			    (uio->uio_offset >> 16) << 16,
650			    PGO_CLEANIT | PGO_LAZY);
651		}
652	} while (error == 0 && uio->uio_resid > 0);
653
654	/* set final size */
655	uvm_vnp_setsize(vp, dep->de_FileSize);
656	if (error == 0 && ioflag & IO_SYNC) {
657		rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
658		error = VOP_PUTPAGES(vp, trunc_page(oldoff),
659		    round_page(oldoff + bytelen), PGO_CLEANIT | PGO_SYNCIO);
660	}
661	dep->de_flag |= DE_UPDATE;
662
663	/*
664	 * If the write failed and they want us to, truncate the file back
665	 * to the size it was before the write was attempted.
666	 */
667errexit:
668	if (error) {
669		msdosfs_detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
670		uio->uio_offset -= resid - uio->uio_resid;
671		uio->uio_resid = resid;
672	} else if ((ioflag & IO_SYNC) == IO_SYNC)
673		error = msdosfs_deupdat(dep, 1);
674	KASSERT(vp->v_size == dep->de_FileSize);
675	return (error);
676}
677
678int
679msdosfs_update(struct vnode *vp, const struct timespec *acc,
680    const struct timespec *mod, int flags)
681{
682	struct buf *bp;
683	struct direntry *dirp;
684	struct denode *dep;
685	int error;
686
687	if (vp->v_mount->mnt_flag & MNT_RDONLY)
688		return (0);
689	dep = VTODE(vp);
690	DETIMES(dep, acc, mod, NULL, dep->de_pmp->pm_gmtoff);
691	if ((dep->de_flag & DE_MODIFIED) == 0)
692		return (0);
693	dep->de_flag &= ~DE_MODIFIED;
694	if (dep->de_Attributes & ATTR_DIRECTORY)
695		return (0);
696	if (dep->de_refcnt <= 0)
697		return (0);
698	error = msdosfs_readde(dep, &bp, &dirp);
699	if (error)
700		return (error);
701	DE_EXTERNALIZE(dirp, dep);
702	if (flags & (UPDATE_WAIT|UPDATE_DIROP))
703		return (bwrite(bp));
704	else {
705		bdwrite(bp);
706		return (0);
707	}
708}
709
710/*
711 * Flush the blocks of a file to disk.
712 *
713 * This function is worthless for vnodes that represent directories. Maybe we
714 * could just do a sync if they try an fsync on a directory file.
715 */
716int
717msdosfs_remove(void *v)
718{
719	struct vop_remove_v3_args /* {
720		struct vnode *a_dvp;
721		struct vnode *a_vp;
722		struct componentname *a_cnp;
723		nlink_t ctx_vp_new_nlink;
724	} */ *ap = v;
725	struct denode *dep = VTODE(ap->a_vp);
726	struct denode *ddep = VTODE(ap->a_dvp);
727	int error;
728
729	if (ap->a_vp->v_type == VDIR)
730		error = EPERM;
731	else
732		error = msdosfs_removede(ddep, dep, &ddep->de_crap);
733#ifdef MSDOSFS_DEBUG
734	printf("msdosfs_remove(), dep %p, usecount %d\n",
735		dep, vrefcnt(ap->a_vp));
736#endif
737	if (ddep == dep)
738		vrele(ap->a_vp);
739	else
740		vput(ap->a_vp);	/* causes msdosfs_inactive() to be called
741				 * via vrele() */
742
743	return (error);
744}
745
746static const struct {
747	struct direntry dot;
748	struct direntry dotdot;
749} dosdirtemplate = {
750	{	".       ", "   ",			/* the . entry */
751		ATTR_DIRECTORY,				/* file attribute */
752		0,	 				/* reserved */
753		0, { 0, 0 }, { 0, 0 },			/* create time & date */
754		{ 0, 0 },				/* access date */
755		{ 0, 0 },				/* high bits of start cluster */
756		{ 210, 4 }, { 210, 4 },			/* modify time & date */
757		{ 0, 0 },				/* startcluster */
758		{ 0, 0, 0, 0 } 				/* filesize */
759	},
760	{	"..      ", "   ",			/* the .. entry */
761		ATTR_DIRECTORY,				/* file attribute */
762		0,	 				/* reserved */
763		0, { 0, 0 }, { 0, 0 },			/* create time & date */
764		{ 0, 0 },				/* access date */
765		{ 0, 0 },				/* high bits of start cluster */
766		{ 210, 4 }, { 210, 4 },			/* modify time & date */
767		{ 0, 0 },				/* startcluster */
768		{ 0, 0, 0, 0 }				/* filesize */
769	}
770};
771
772int
773msdosfs_mkdir(void *v)
774{
775	struct vop_mkdir_v3_args /* {
776		struct vnode *a_dvp;
777		struvt vnode **a_vpp;
778		struvt componentname *a_cnp;
779		struct vattr *a_vap;
780	} */ *ap = v;
781	struct componentname *cnp = ap->a_cnp;
782	struct denode ndirent;
783	struct denode *dep;
784	struct denode *pdep = VTODE(ap->a_dvp);
785	int error;
786	int bn;
787	u_long newcluster, pcl;
788	daddr_t lbn;
789	struct direntry *denp;
790	struct msdosfsmount *pmp = pdep->de_pmp;
791	struct buf *bp;
792	int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
793
794	/*
795	 * If this is the root directory and there is no space left we
796	 * can't do anything.  This is because the root directory can not
797	 * change size.
798	 */
799	if (pdep->de_StartCluster == MSDOSFSROOT
800	    && pdep->de_crap.mlr_fndoffset >= pdep->de_FileSize) {
801		error = ENOSPC;
802		goto bad2;
803	}
804
805	/*
806	 * Allocate a cluster to hold the about to be created directory.
807	 */
808	error = msdosfs_clusteralloc(pmp, 0, 1, &newcluster, NULL);
809	if (error)
810		goto bad2;
811
812	memset(&ndirent, 0, sizeof(ndirent));
813	ndirent.de_pmp = pmp;
814	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
815	DETIMES(&ndirent, NULL, NULL, NULL, pmp->pm_gmtoff);
816
817	/*
818	 * Now fill the cluster with the "." and ".." entries. And write
819	 * the cluster to disk.  This way it is there for the parent
820	 * directory to be pointing at if there were a crash.
821	 */
822	bn = cntobn(pmp, newcluster);
823	lbn = de_bn2kb(pmp, bn);
824	/* always succeeds */
825	bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
826	memset(bp->b_data, 0, pmp->pm_bpcluster);
827	memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
828	denp = (struct direntry *)bp->b_data;
829	putushort(denp[0].deStartCluster, newcluster);
830	putushort(denp[0].deCDate, ndirent.de_CDate);
831	putushort(denp[0].deCTime, ndirent.de_CTime);
832	denp[0].deCHundredth = ndirent.de_CHun;
833	putushort(denp[0].deADate, ndirent.de_ADate);
834	putushort(denp[0].deMDate, ndirent.de_MDate);
835	putushort(denp[0].deMTime, ndirent.de_MTime);
836	pcl = pdep->de_StartCluster;
837	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
838		pcl = 0;
839	putushort(denp[1].deStartCluster, pcl);
840	putushort(denp[1].deCDate, ndirent.de_CDate);
841	putushort(denp[1].deCTime, ndirent.de_CTime);
842	denp[1].deCHundredth = ndirent.de_CHun;
843	putushort(denp[1].deADate, ndirent.de_ADate);
844	putushort(denp[1].deMDate, ndirent.de_MDate);
845	putushort(denp[1].deMTime, ndirent.de_MTime);
846	if (FAT32(pmp)) {
847		putushort(denp[0].deHighClust, newcluster >> 16);
848		putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
849	} else {
850		putushort(denp[0].deHighClust, 0);
851		putushort(denp[1].deHighClust, 0);
852	}
853
854	if (async)
855		bdwrite(bp);
856	else if ((error = bwrite(bp)) != 0)
857		goto bad;
858
859	/*
860	 * Now build up a directory entry pointing to the newly allocated
861	 * cluster.  This will be written to an empty slot in the parent
862	 * directory.
863	 */
864	if ((error = msdosfs_uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
865		goto bad;
866
867	ndirent.de_Attributes = ATTR_DIRECTORY;
868	ndirent.de_StartCluster = newcluster;
869	ndirent.de_FileSize = 0;
870	ndirent.de_dev = pdep->de_dev;
871	ndirent.de_devvp = pdep->de_devvp;
872	if ((error = msdosfs_createde(&ndirent, pdep, &pdep->de_crap, &dep,
873	    cnp)) != 0)
874		goto bad;
875	*ap->a_vpp = DETOV(dep);
876	return (0);
877
878bad:
879	msdosfs_clusterfree(pmp, newcluster, NULL);
880bad2:
881	return (error);
882}
883
884int
885msdosfs_rmdir(void *v)
886{
887	struct vop_rmdir_v2_args /* {
888		struct vnode *a_dvp;
889		struct vnode *a_vp;
890		struct componentname *a_cnp;
891	} */ *ap = v;
892	struct vnode *vp = ap->a_vp;
893	struct vnode *dvp = ap->a_dvp;
894	struct componentname *cnp = ap->a_cnp;
895	struct denode *ip, *dp;
896	int error;
897
898	ip = VTODE(vp);
899	dp = VTODE(dvp);
900	/*
901	 * No rmdir "." please.
902	 */
903	if (dp == ip) {
904		vrele(vp);
905		return (EINVAL);
906	}
907	/*
908	 * Verify the directory is empty (and valid).
909	 * (Rmdir ".." won't be valid since
910	 *  ".." will contain a reference to
911	 *  the current directory and thus be
912	 *  non-empty.)
913	 */
914	error = 0;
915	if (!msdosfs_dosdirempty(ip) || ip->de_flag & DE_RENAME) {
916		error = ENOTEMPTY;
917		goto out;
918	}
919	/*
920	 * Delete the entry from the directory.  For dos filesystems this
921	 * gets rid of the directory entry on disk, the in memory copy
922	 * still exists but the de_refcnt is <= 0.  This prevents it from
923	 * being found by deget().  When the vput() on dep is done we give
924	 * up access and eventually msdosfs_reclaim() will be called which
925	 * will remove it from the denode cache.
926	 */
927	if ((error = msdosfs_removede(dp, ip, &dp->de_crap)) != 0)
928		goto out;
929	/*
930	 * This is where we decrement the link count in the parent
931	 * directory.  Since dos filesystems don't do this we just purge
932	 * the name cache and let go of the parent directory denode.
933	 */
934	cache_purge(dvp);
935	/*
936	 * Truncate the directory that is being deleted.
937	 */
938	error = msdosfs_detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
939	cache_purge(vp);
940out:
941	vput(vp);
942	return (error);
943}
944
945int
946msdosfs_readdir(void *v)
947{
948	struct vop_readdir_args /* {
949		struct vnode *a_vp;
950		struct uio *a_uio;
951		kauth_cred_t a_cred;
952		int *a_eofflag;
953		off_t **a_cookies;
954		int *a_ncookies;
955	} */ *ap = v;
956	int error = 0;
957	int diff;
958	long n;
959	int blsize;
960	long on;
961	long lost;
962	long count;
963	u_long cn;
964	ino_t fileno;
965	u_long dirsperblk;
966	long bias = 0;
967	daddr_t bn, lbn;
968	struct buf *bp;
969	struct denode *dep = VTODE(ap->a_vp);
970	struct msdosfsmount *pmp = dep->de_pmp;
971	struct direntry *dentp;
972	struct dirent *dirbuf;
973	struct uio *uio = ap->a_uio;
974	off_t *cookies = NULL;
975	int ncookies = 0, nc = 0;
976	off_t offset, uio_off;
977	int chksum = -1;
978	uint16_t namlen;
979
980#ifdef MSDOSFS_DEBUG
981	printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
982	    ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
983#endif
984
985	/*
986	 * msdosfs_readdir() won't operate properly on regular files since
987	 * it does i/o only with the filesystem vnode, and hence can
988	 * retrieve the wrong block from the buffer cache for a plain file.
989	 * So, fail attempts to readdir() on a plain file.
990	 */
991	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
992		return (ENOTDIR);
993
994	/*
995	 * If the user buffer is smaller than the size of one dos directory
996	 * entry or the file offset is not a multiple of the size of a
997	 * directory entry, then we fail the read.
998	 */
999	count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
1000	offset = uio->uio_offset;
1001	if (count < sizeof(struct direntry) ||
1002	    (offset & (sizeof(struct direntry) - 1)))
1003		return (EINVAL);
1004	lost = uio->uio_resid - count;
1005	uio->uio_resid = count;
1006	uio_off = uio->uio_offset;
1007
1008
1009	/* Allocate a temporary dirent buffer. */
1010	dirbuf = malloc(sizeof(struct dirent), M_MSDOSFSTMP, M_WAITOK | M_ZERO);
1011
1012	if (ap->a_ncookies) {
1013		nc = uio->uio_resid / _DIRENT_MINSIZE((struct dirent *)0);
1014		cookies = malloc(nc * sizeof (off_t), M_TEMP, M_WAITOK);
1015		*ap->a_cookies = cookies;
1016	}
1017
1018	dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
1019
1020	/*
1021	 * If they are reading from the root directory then, we simulate
1022	 * the . and .. entries since these don't exist in the root
1023	 * directory.  We also set the offset bias to make up for having to
1024	 * simulate these entries. By this I mean that at file offset 64 we
1025	 * read the first entry in the root directory that lives on disk.
1026	 */
1027	if (dep->de_StartCluster == MSDOSFSROOT
1028	    || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
1029#if 0
1030		printf("msdosfs_readdir(): going after . or .. in root dir, "
1031		    "offset %" PRIu64 "\n", offset);
1032#endif
1033		bias = 2 * sizeof(struct direntry);
1034		if (offset < bias) {
1035			for (n = (int)offset / sizeof(struct direntry);
1036			     n < 2; n++) {
1037				if (FAT32(pmp))
1038					dirbuf->d_fileno = cntobn(pmp,
1039					     (ino_t)pmp->pm_rootdirblk)
1040					     * dirsperblk;
1041				else
1042					dirbuf->d_fileno = 1;
1043				dirbuf->d_type = DT_DIR;
1044				switch (n) {
1045				case 0:
1046					dirbuf->d_namlen = 1;
1047					strlcpy(dirbuf->d_name, ".",
1048					    sizeof(dirbuf->d_name));
1049					break;
1050				case 1:
1051					dirbuf->d_namlen = 2;
1052					strlcpy(dirbuf->d_name, "..",
1053					    sizeof(dirbuf->d_name));
1054					break;
1055				}
1056				dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
1057				if (uio->uio_resid < dirbuf->d_reclen)
1058					goto out;
1059				error = uiomove(dirbuf, dirbuf->d_reclen, uio);
1060				if (error)
1061					goto out;
1062				offset += sizeof(struct direntry);
1063				uio_off = offset;
1064				if (cookies) {
1065					*cookies++ = offset;
1066					ncookies++;
1067					if (ncookies >= nc)
1068						goto out;
1069				}
1070			}
1071		}
1072	}
1073
1074	while (uio->uio_resid > 0) {
1075		lbn = de_cluster(pmp, offset - bias);
1076		on = (offset - bias) & pmp->pm_crbomask;
1077		n = MIN(pmp->pm_bpcluster - on, uio->uio_resid);
1078		diff = dep->de_FileSize - (offset - bias);
1079		if (diff <= 0)
1080			break;
1081		n = MIN(n, diff);
1082		if ((error = msdosfs_pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0)
1083			break;
1084		error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
1085		    0, &bp);
1086		if (error) {
1087			goto bad;
1088		}
1089		n = MIN(n, blsize - bp->b_resid);
1090
1091		/*
1092		 * Convert from dos directory entries to fs-independent
1093		 * directory entries.
1094		 */
1095		for (dentp = (struct direntry *)((char *)bp->b_data + on);
1096		     (char *)dentp < (char *)bp->b_data + on + n;
1097		     dentp++, offset += sizeof(struct direntry)) {
1098#if 0
1099
1100			printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1101			    dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1102#endif
1103			/*
1104			 * If this is an unused entry, we can stop.
1105			 */
1106			if (dentp->deName[0] == SLOT_EMPTY) {
1107				brelse(bp, 0);
1108				goto out;
1109			}
1110			/*
1111			 * Skip deleted entries.
1112			 */
1113			if (dentp->deName[0] == SLOT_DELETED) {
1114				chksum = -1;
1115				continue;
1116			}
1117
1118			/*
1119			 * Handle Win95 long directory entries
1120			 */
1121			if (dentp->deAttributes == ATTR_WIN95) {
1122				if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
1123					continue;
1124				chksum =
1125				    msdosfs_win2unixfn((struct winentry *)dentp,
1126				    dirbuf, chksum, &namlen,
1127				    pmp->pm_flags & MSDOSFSMNT_UTF8);
1128				if (chksum != -1)
1129					dirbuf->d_namlen = namlen;
1130				continue;
1131			}
1132
1133			/*
1134			 * Skip volume labels
1135			 */
1136			if (dentp->deAttributes & ATTR_VOLUME) {
1137				chksum = -1;
1138				continue;
1139			}
1140			/*
1141			 * This computation of d_fileno must match
1142			 * the computation of va_fileid in
1143			 * msdosfs_getattr.
1144			 */
1145			if (dentp->deAttributes & ATTR_DIRECTORY) {
1146				fileno = getushort(dentp->deStartCluster);
1147				if (FAT32(pmp))
1148					fileno |= ((ino_t)getushort(dentp->deHighClust)) << 16;
1149				/* if this is the root directory */
1150				if (fileno == MSDOSFSROOT)
1151					if (FAT32(pmp))
1152						fileno = cntobn(pmp,
1153						    (ino_t)pmp->pm_rootdirblk)
1154						    * dirsperblk;
1155					else
1156						fileno = 1;
1157				else
1158					fileno = cntobn(pmp, fileno) * dirsperblk;
1159				dirbuf->d_fileno = fileno;
1160				dirbuf->d_type = DT_DIR;
1161			} else {
1162				dirbuf->d_fileno =
1163				    offset / sizeof(struct direntry);
1164				dirbuf->d_type = DT_REG;
1165			}
1166			if (chksum != msdosfs_winChksum(dentp->deName)) {
1167				char deName[11];
1168
1169				memcpy(deName, dentp->deName,
1170				       sizeof dentp->deName);
1171				memcpy(deName + 8, dentp->deExtension,
1172				       sizeof dentp->deExtension);
1173				assert(sizeof(deName) == sizeof(dentp->deName) +
1174					sizeof(dentp->deExtension));
1175				dirbuf->d_namlen =
1176				    msdosfs_dos2unixfn(deName,
1177				        (u_char *)dirbuf->d_name,
1178				        pmp->pm_flags & MSDOSFSMNT_SHORTNAME);
1179			} else
1180				dirbuf->d_name[dirbuf->d_namlen] = 0;
1181			namlen = dirbuf->d_namlen;
1182			chksum = -1;
1183			dirbuf->d_reclen = _DIRENT_SIZE(dirbuf);
1184			if (uio->uio_resid < dirbuf->d_reclen) {
1185				brelse(bp, 0);
1186				goto out;
1187			}
1188			error = uiomove(dirbuf, dirbuf->d_reclen, uio);
1189			if (error) {
1190				brelse(bp, 0);
1191				goto out;
1192			}
1193			uio_off = offset + sizeof(struct direntry);
1194			if (cookies) {
1195				*cookies++ = offset + sizeof(struct direntry);
1196				ncookies++;
1197				if (ncookies >= nc) {
1198					brelse(bp, 0);
1199					goto out;
1200				}
1201			}
1202		}
1203		brelse(bp, 0);
1204	}
1205
1206out:
1207	uio->uio_offset = uio_off;
1208	uio->uio_resid += lost;
1209	if (dep->de_FileSize - (offset - bias) <= 0)
1210		*ap->a_eofflag = 1;
1211	else
1212		*ap->a_eofflag = 0;
1213
1214	if (ap->a_ncookies) {
1215		if (error) {
1216			free(*ap->a_cookies, M_TEMP);
1217			*ap->a_ncookies = 0;
1218			*ap->a_cookies = NULL;
1219		} else
1220			*ap->a_ncookies = ncookies;
1221	}
1222
1223bad:
1224	free(dirbuf, M_MSDOSFSTMP);
1225	return (error);
1226}
1227
1228/*
1229 * vp  - address of vnode file the file
1230 * bn  - which cluster we are interested in mapping to a filesystem block number.
1231 * vpp - returns the vnode for the block special file holding the filesystem
1232 *	 containing the file of interest
1233 * bnp - address of where to return the filesystem relative block number
1234 */
1235int
1236msdosfs_bmap(void *v)
1237{
1238	struct vop_bmap_args /* {
1239		struct vnode *a_vp;
1240		daddr_t a_bn;
1241		struct vnode **a_vpp;
1242		daddr_t *a_bnp;
1243		int *a_runp;
1244	} */ *ap = v;
1245	struct denode *dep = VTODE(ap->a_vp);
1246	int run, maxrun;
1247	daddr_t runbn;
1248	int status;
1249
1250	if (ap->a_vpp != NULL)
1251		*ap->a_vpp = dep->de_devvp;
1252	if (ap->a_bnp == NULL)
1253		return (0);
1254	status = msdosfs_pcbmap(dep, ap->a_bn, ap->a_bnp, 0, 0);
1255
1256	/*
1257	 * From FreeBSD:
1258	 * A little kludgy, but we loop calling pcbmap until we
1259	 * reach the end of the contiguous piece, or reach MAXPHYS.
1260	 * Since it reduces disk I/Os, the "wasted" CPU is put to
1261	 * good use (4 to 5 fold sequential read I/O improvement on USB
1262	 * drives).
1263	 */
1264	if (ap->a_runp != NULL) {
1265		/* taken from ufs_bmap */
1266		maxrun = ulmin(MAXPHYS / dep->de_pmp->pm_bpcluster - 1,
1267			       dep->de_pmp->pm_maxcluster - ap->a_bn);
1268		for (run = 1; run <= maxrun; run++) {
1269			if (msdosfs_pcbmap(dep, ap->a_bn + run, &runbn, NULL,
1270			    NULL) != 0 || runbn !=
1271			            *ap->a_bnp + de_cn2bn(dep->de_pmp, run))
1272				break;
1273		}
1274		*ap->a_runp = run - 1;
1275	}
1276
1277	/*
1278	 * We need to scale *ap->a_bnp by sector_size/DEV_BSIZE
1279	 */
1280	*ap->a_bnp = de_bn2kb(dep->de_pmp, *ap->a_bnp);
1281	return status;
1282}
1283
1284int
1285msdosfs_strategy(void *v)
1286{
1287	struct vop_strategy_args /* {
1288		struct vnode *a_vp;
1289		struct buf *a_bp;
1290	} */ *ap = v;
1291	struct vnode *vp = ap->a_vp;
1292	struct buf *bp = ap->a_bp;
1293	struct denode *dep = VTODE(bp->b_vp);
1294	int error = 0;
1295
1296	if (vp->v_type == VBLK || vp->v_type == VCHR)
1297		panic("msdosfs_strategy: spec");
1298	/*
1299	 * If we don't already know the filesystem relative block number
1300	 * then get it using pcbmap().  If pcbmap() returns the block
1301	 * number as -1 then we've got a hole in the file.  DOS filesystems
1302	 * don't allow files with holes, so we shouldn't ever see this.
1303	 */
1304	if (bp->b_blkno == bp->b_lblkno) {
1305		error = msdosfs_pcbmap(dep, de_bn2cn(dep->de_pmp, bp->b_lblkno),
1306			       &bp->b_blkno, 0, 0);
1307		if (error)
1308			bp->b_blkno = -1;
1309		if (bp->b_blkno == -1)
1310			clrbuf(bp);
1311		else
1312			bp->b_blkno = de_bn2kb(dep->de_pmp, bp->b_blkno);
1313	}
1314	if (bp->b_blkno == -1) {
1315		biodone(bp);
1316		return (error);
1317	}
1318
1319	/*
1320	 * Read/write the block from/to the disk that contains the desired
1321	 * file block.
1322	 */
1323
1324	vp = dep->de_devvp;
1325	return (VOP_STRATEGY(vp, bp));
1326}
1327
1328int
1329msdosfs_print(void *v)
1330{
1331	struct vop_print_args /* {
1332		struct vnode *vp;
1333	} */ *ap = v;
1334	struct denode *dep = VTODE(ap->a_vp);
1335
1336	printf(
1337	    "tag VT_MSDOSFS, startcluster %ld, dircluster %ld, diroffset %ld ",
1338	    dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1339	printf(" dev %llu, %llu ", (unsigned long long)major(dep->de_dev),
1340	    (unsigned long long)minor(dep->de_dev));
1341	printf("\n");
1342	return (0);
1343}
1344
1345int
1346msdosfs_advlock(void *v)
1347{
1348	struct vop_advlock_args /* {
1349		struct vnode *a_vp;
1350		void *a_id;
1351		int a_op;
1352		struct flock *a_fl;
1353		int a_flags;
1354	} */ *ap = v;
1355	struct denode *dep = VTODE(ap->a_vp);
1356
1357	return lf_advlock(ap, &dep->de_lockf, dep->de_FileSize);
1358}
1359
1360int
1361msdosfs_pathconf(void *v)
1362{
1363	struct vop_pathconf_args /* {
1364		struct vnode *a_vp;
1365		int a_name;
1366		register_t *a_retval;
1367	} */ *ap = v;
1368
1369	switch (ap->a_name) {
1370	case _PC_LINK_MAX:
1371		*ap->a_retval = 1;
1372		return (0);
1373	case _PC_NAME_MAX:
1374		*ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_namemax;
1375		return (0);
1376	case _PC_PATH_MAX:
1377		*ap->a_retval = PATH_MAX;
1378		return (0);
1379	case _PC_CHOWN_RESTRICTED:
1380		*ap->a_retval = 1;
1381		return (0);
1382	case _PC_NO_TRUNC:
1383		*ap->a_retval = 1;
1384		return (0);
1385	case _PC_SYNC_IO:
1386		*ap->a_retval = 1;
1387		return (0);
1388	case _PC_FILESIZEBITS:
1389		*ap->a_retval = 32;
1390		return (0);
1391	default:
1392		return genfs_pathconf(ap);
1393	}
1394	/* NOTREACHED */
1395}
1396
1397int
1398msdosfs_fsync(void *v)
1399{
1400	struct vop_fsync_args /* {
1401		struct vnode *a_vp;
1402		kauth_cred_t a_cred;
1403		int a_flags;
1404		off_t offlo;
1405		off_t offhi;
1406	} */ *ap = v;
1407	struct vnode *vp = ap->a_vp;
1408	int wait;
1409	int error;
1410
1411	wait = (ap->a_flags & FSYNC_WAIT) != 0;
1412	error = vflushbuf(vp, ap->a_flags);
1413	if (error == 0 && (ap->a_flags & FSYNC_DATAONLY) == 0)
1414		error = msdosfs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0);
1415
1416	if (error == 0 && ap->a_flags & FSYNC_CACHE) {
1417		struct denode *dep = VTODE(vp);
1418		struct vnode *devvp = dep->de_devvp;
1419
1420		int l = 0;
1421		error = VOP_IOCTL(devvp, DIOCCACHESYNC, &l, FWRITE,
1422					  curlwp->l_cred);
1423	}
1424
1425	return (error);
1426}
1427
1428void
1429msdosfs_detimes(struct denode *dep, const struct timespec *acc,
1430    const struct timespec *mod, const struct timespec *cre, int gmtoff)
1431{
1432	struct timespec *ts = NULL, tsb;
1433
1434	KASSERT(dep->de_flag & (DE_UPDATE | DE_CREATE | DE_ACCESS));
1435	/* XXX just call getnanotime early and use result if needed? */
1436	dep->de_flag |= DE_MODIFIED;
1437	if (dep->de_flag & DE_UPDATE) {
1438		if (mod == NULL) {
1439			getnanotime(&tsb);
1440			mod = ts = &tsb;
1441		}
1442		msdosfs_unix2dostime(mod, gmtoff, &dep->de_MDate,
1443		    &dep->de_MTime, NULL);
1444		dep->de_Attributes |= ATTR_ARCHIVE;
1445	}
1446	if ((dep->de_pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0) {
1447		if (dep->de_flag & DE_ACCESS)  {
1448			if (acc == NULL)
1449				acc = ts == NULL ?
1450				    (getnanotime(&tsb), ts = &tsb) : ts;
1451			msdosfs_unix2dostime(acc, gmtoff, &dep->de_ADate,
1452			    NULL, NULL);
1453		}
1454		if (dep->de_flag & DE_CREATE) {
1455			if (cre == NULL)
1456				cre = ts == NULL ?
1457				    (getnanotime(&tsb), ts = &tsb) : ts;
1458			msdosfs_unix2dostime(cre, gmtoff, &dep->de_CDate,
1459			    &dep->de_CTime, &dep->de_CHun);
1460		}
1461	}
1462
1463	dep->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS);
1464}
1465
1466/* Global vfs data structures for msdosfs */
1467int (**msdosfs_vnodeop_p)(void *);
1468const struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
1469	{ &vop_default_desc, vn_default_error },
1470	{ &vop_parsepath_desc, genfs_parsepath },	/* parsepath */
1471	{ &vop_lookup_desc, msdosfs_lookup },		/* lookup */
1472	{ &vop_create_desc, msdosfs_create },		/* create */
1473	{ &vop_mknod_desc, genfs_eopnotsupp },		/* mknod */
1474	{ &vop_open_desc, genfs_nullop },		/* open */
1475	{ &vop_close_desc, msdosfs_close },		/* close */
1476	{ &vop_access_desc, msdosfs_access },		/* access */
1477	{ &vop_accessx_desc, genfs_accessx },		/* accessx */
1478	{ &vop_getattr_desc, msdosfs_getattr },		/* getattr */
1479	{ &vop_setattr_desc, msdosfs_setattr },		/* setattr */
1480	{ &vop_read_desc, msdosfs_read },		/* read */
1481	{ &vop_write_desc, msdosfs_write },		/* write */
1482	{ &vop_fallocate_desc, genfs_eopnotsupp },	/* fallocate */
1483	{ &vop_fdiscard_desc, genfs_eopnotsupp },	/* fdiscard */
1484	{ &vop_fcntl_desc, genfs_fcntl },		/* fcntl */
1485	{ &vop_ioctl_desc, genfs_enoioctl },		/* ioctl */
1486	{ &vop_poll_desc, genfs_poll },			/* poll */
1487	{ &vop_kqfilter_desc, genfs_kqfilter },		/* kqfilter */
1488	{ &vop_revoke_desc, genfs_revoke },		/* revoke */
1489	{ &vop_mmap_desc, genfs_mmap },			/* mmap */
1490	{ &vop_fsync_desc, msdosfs_fsync },		/* fsync */
1491	{ &vop_seek_desc, genfs_seek },			/* seek */
1492	{ &vop_remove_desc, msdosfs_remove },		/* remove */
1493	{ &vop_link_desc, genfs_eopnotsupp },		/* link */
1494	{ &vop_rename_desc, msdosfs_rename },		/* rename */
1495	{ &vop_mkdir_desc, msdosfs_mkdir },		/* mkdir */
1496	{ &vop_rmdir_desc, msdosfs_rmdir },		/* rmdir */
1497	{ &vop_symlink_desc, genfs_eopnotsupp },	/* symlink */
1498	{ &vop_readdir_desc, msdosfs_readdir },		/* readdir */
1499	{ &vop_readlink_desc, genfs_einval },		/* readlink */
1500	{ &vop_abortop_desc, genfs_abortop },		/* abortop */
1501	{ &vop_inactive_desc, msdosfs_inactive },	/* inactive */
1502	{ &vop_reclaim_desc, msdosfs_reclaim },		/* reclaim */
1503	{ &vop_lock_desc, genfs_lock },			/* lock */
1504	{ &vop_unlock_desc, genfs_unlock },		/* unlock */
1505	{ &vop_bmap_desc, msdosfs_bmap },		/* bmap */
1506	{ &vop_strategy_desc, msdosfs_strategy },	/* strategy */
1507	{ &vop_print_desc, msdosfs_print },		/* print */
1508	{ &vop_islocked_desc, genfs_islocked },		/* islocked */
1509	{ &vop_pathconf_desc, msdosfs_pathconf },	/* pathconf */
1510	{ &vop_advlock_desc, msdosfs_advlock },		/* advlock */
1511	{ &vop_bwrite_desc, vn_bwrite },		/* bwrite */
1512	{ &vop_getpages_desc, genfs_getpages },		/* getpages */
1513	{ &vop_putpages_desc, genfs_putpages },		/* putpages */
1514	{ NULL, NULL }
1515};
1516const struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
1517	{ &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
1518