msdosfs_vnops.c revision 308552
1/* $FreeBSD: stable/10/sys/fs/msdosfs/msdosfs_vnops.c 308552 2016-11-11 20:08:45Z kib $ */
2/*	$NetBSD: msdosfs_vnops.c,v 1.68 1998/02/10 14:10:04 mrg Exp $	*/
3
4/*-
5 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
6 * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
7 * All rights reserved.
8 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by TooLs GmbH.
21 * 4. The name of TooLs GmbH may not be used to endorse or promote products
22 *    derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35/*-
36 * Written by Paul Popelka (paulp@uts.amdahl.com)
37 *
38 * You can do anything you want with this software, just don't say you wrote
39 * it, and don't remove this notice.
40 *
41 * This software is provided "as is".
42 *
43 * The author supplies this software to be publicly redistributed on the
44 * understanding that the author is not responsible for the correct
45 * functioning of this software in any circumstances and is not liable for
46 * any damages caused by this software.
47 *
48 * October 1992
49 */
50
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/bio.h>
54#include <sys/buf.h>
55#include <sys/clock.h>
56#include <sys/dirent.h>
57#include <sys/lock.h>
58#include <sys/lockf.h>
59#include <sys/malloc.h>
60#include <sys/mount.h>
61#include <sys/mutex.h>
62#include <sys/namei.h>
63#include <sys/priv.h>
64#include <sys/stat.h>
65#include <sys/unistd.h>
66#include <sys/vnode.h>
67
68#include <vm/vm.h>
69#include <vm/vm_extern.h>
70
71#include <fs/msdosfs/bpb.h>
72#include <fs/msdosfs/direntry.h>
73#include <fs/msdosfs/denode.h>
74#include <fs/msdosfs/fat.h>
75#include <fs/msdosfs/msdosfsmount.h>
76
77#define	DOS_FILESIZE_MAX	0xffffffff
78
79/*
80 * Prototypes for MSDOSFS vnode operations
81 */
82static vop_create_t	msdosfs_create;
83static vop_mknod_t	msdosfs_mknod;
84static vop_open_t	msdosfs_open;
85static vop_close_t	msdosfs_close;
86static vop_access_t	msdosfs_access;
87static vop_getattr_t	msdosfs_getattr;
88static vop_setattr_t	msdosfs_setattr;
89static vop_read_t	msdosfs_read;
90static vop_write_t	msdosfs_write;
91static vop_fsync_t	msdosfs_fsync;
92static vop_remove_t	msdosfs_remove;
93static vop_link_t	msdosfs_link;
94static vop_rename_t	msdosfs_rename;
95static vop_mkdir_t	msdosfs_mkdir;
96static vop_rmdir_t	msdosfs_rmdir;
97static vop_symlink_t	msdosfs_symlink;
98static vop_readdir_t	msdosfs_readdir;
99static vop_bmap_t	msdosfs_bmap;
100static vop_strategy_t	msdosfs_strategy;
101static vop_print_t	msdosfs_print;
102static vop_pathconf_t	msdosfs_pathconf;
103static vop_vptofh_t	msdosfs_vptofh;
104
105/*
106 * Some general notes:
107 *
108 * In the ufs filesystem the inodes, superblocks, and indirect blocks are
109 * read/written using the vnode for the filesystem. Blocks that represent
110 * the contents of a file are read/written using the vnode for the file
111 * (including directories when they are read/written as files). This
112 * presents problems for the dos filesystem because data that should be in
113 * an inode (if dos had them) resides in the directory itself.  Since we
114 * must update directory entries without the benefit of having the vnode
115 * for the directory we must use the vnode for the filesystem.  This means
116 * that when a directory is actually read/written (via read, write, or
117 * readdir, or seek) we must use the vnode for the filesystem instead of
118 * the vnode for the directory as would happen in ufs. This is to insure we
119 * retreive the correct block from the buffer cache since the hash value is
120 * based upon the vnode address and the desired block number.
121 */
122
123/*
124 * Create a regular file. On entry the directory to contain the file being
125 * created is locked.  We must release before we return. We must also free
126 * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
127 * only if the SAVESTART bit in cn_flags is clear on success.
128 */
129static int
130msdosfs_create(ap)
131	struct vop_create_args /* {
132		struct vnode *a_dvp;
133		struct vnode **a_vpp;
134		struct componentname *a_cnp;
135		struct vattr *a_vap;
136	} */ *ap;
137{
138	struct componentname *cnp = ap->a_cnp;
139	struct denode ndirent;
140	struct denode *dep;
141	struct denode *pdep = VTODE(ap->a_dvp);
142	struct timespec ts;
143	int error;
144
145#ifdef MSDOSFS_DEBUG
146	printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
147#endif
148
149	/*
150	 * If this is the root directory and there is no space left we
151	 * can't do anything.  This is because the root directory can not
152	 * change size.
153	 */
154	if (pdep->de_StartCluster == MSDOSFSROOT
155	    && pdep->de_fndoffset >= pdep->de_FileSize) {
156		error = ENOSPC;
157		goto bad;
158	}
159
160	/*
161	 * Create a directory entry for the file, then call createde() to
162	 * have it installed. NOTE: DOS files are always executable.  We
163	 * use the absence of the owner write bit to make the file
164	 * readonly.
165	 */
166#ifdef DIAGNOSTIC
167	if ((cnp->cn_flags & HASBUF) == 0)
168		panic("msdosfs_create: no name");
169#endif
170	bzero(&ndirent, sizeof(ndirent));
171	error = uniqdosname(pdep, cnp, ndirent.de_Name);
172	if (error)
173		goto bad;
174
175	ndirent.de_Attributes = ATTR_ARCHIVE;
176	ndirent.de_LowerCase = 0;
177	ndirent.de_StartCluster = 0;
178	ndirent.de_FileSize = 0;
179	ndirent.de_pmp = pdep->de_pmp;
180	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
181	getnanotime(&ts);
182	DETIMES(&ndirent, &ts, &ts, &ts);
183	error = createde(&ndirent, pdep, &dep, cnp);
184	if (error)
185		goto bad;
186	*ap->a_vpp = DETOV(dep);
187	if ((cnp->cn_flags & MAKEENTRY) != 0)
188		cache_enter(ap->a_dvp, *ap->a_vpp, cnp);
189	return (0);
190
191bad:
192	return (error);
193}
194
195static int
196msdosfs_mknod(ap)
197	struct vop_mknod_args /* {
198		struct vnode *a_dvp;
199		struct vnode **a_vpp;
200		struct componentname *a_cnp;
201		struct vattr *a_vap;
202	} */ *ap;
203{
204
205    return (EINVAL);
206}
207
208static int
209msdosfs_open(ap)
210	struct vop_open_args /* {
211		struct vnode *a_vp;
212		int a_mode;
213		struct ucred *a_cred;
214		struct thread *a_td;
215		struct file *a_fp;
216	} */ *ap;
217{
218	struct denode *dep = VTODE(ap->a_vp);
219	vnode_create_vobject(ap->a_vp, dep->de_FileSize, ap->a_td);
220	return 0;
221}
222
223static int
224msdosfs_close(ap)
225	struct vop_close_args /* {
226		struct vnode *a_vp;
227		int a_fflag;
228		struct ucred *a_cred;
229		struct thread *a_td;
230	} */ *ap;
231{
232	struct vnode *vp = ap->a_vp;
233	struct denode *dep = VTODE(vp);
234	struct timespec ts;
235
236	VI_LOCK(vp);
237	if (vp->v_usecount > 1) {
238		getnanotime(&ts);
239		DETIMES(dep, &ts, &ts, &ts);
240	}
241	VI_UNLOCK(vp);
242	return 0;
243}
244
245static int
246msdosfs_access(ap)
247	struct vop_access_args /* {
248		struct vnode *a_vp;
249		accmode_t a_accmode;
250		struct ucred *a_cred;
251		struct thread *a_td;
252	} */ *ap;
253{
254	struct vnode *vp = ap->a_vp;
255	struct denode *dep = VTODE(ap->a_vp);
256	struct msdosfsmount *pmp = dep->de_pmp;
257	mode_t file_mode;
258	accmode_t accmode = ap->a_accmode;
259
260	file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
261	file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
262
263	/*
264	 * Disallow writing to directories and regular files if the
265	 * filesystem is read-only.
266	 */
267	if (accmode & VWRITE) {
268		switch (vp->v_type) {
269		case VREG:
270		case VDIR:
271			if (vp->v_mount->mnt_flag & MNT_RDONLY)
272				return (EROFS);
273			break;
274		default:
275			break;
276		}
277	}
278
279	return (vaccess(vp->v_type, file_mode, pmp->pm_uid, pmp->pm_gid,
280	    ap->a_accmode, ap->a_cred, NULL));
281}
282
283static int
284msdosfs_getattr(ap)
285	struct vop_getattr_args /* {
286		struct vnode *a_vp;
287		struct vattr *a_vap;
288		struct ucred *a_cred;
289	} */ *ap;
290{
291	struct denode *dep = VTODE(ap->a_vp);
292	struct msdosfsmount *pmp = dep->de_pmp;
293	struct vattr *vap = ap->a_vap;
294	mode_t mode;
295	struct timespec ts;
296	u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
297	uint64_t fileid;
298
299	getnanotime(&ts);
300	DETIMES(dep, &ts, &ts, &ts);
301	vap->va_fsid = dev2udev(pmp->pm_dev);
302	/*
303	 * The following computation of the fileid must be the same as that
304	 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
305	 * doesn't work.
306	 */
307	if (dep->de_Attributes & ATTR_DIRECTORY) {
308		fileid = (uint64_t)cntobn(pmp, dep->de_StartCluster) *
309		    dirsperblk;
310		if (dep->de_StartCluster == MSDOSFSROOT)
311			fileid = 1;
312	} else {
313		fileid = (uint64_t)cntobn(pmp, dep->de_dirclust) *
314		    dirsperblk;
315		if (dep->de_dirclust == MSDOSFSROOT)
316			fileid = (uint64_t)roottobn(pmp, 0) * dirsperblk;
317		fileid += (uoff_t)dep->de_diroffset / sizeof(struct direntry);
318	}
319
320	if (pmp->pm_flags & MSDOSFS_LARGEFS)
321		vap->va_fileid = msdosfs_fileno_map(pmp->pm_mountp, fileid);
322	else
323		vap->va_fileid = (long)fileid;
324
325	mode = S_IRWXU|S_IRWXG|S_IRWXO;
326	vap->va_mode = mode &
327	    (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
328	vap->va_uid = pmp->pm_uid;
329	vap->va_gid = pmp->pm_gid;
330	vap->va_nlink = 1;
331	vap->va_rdev = NODEV;
332	vap->va_size = dep->de_FileSize;
333	fattime2timespec(dep->de_MDate, dep->de_MTime, 0, 0, &vap->va_mtime);
334	vap->va_ctime = vap->va_mtime;
335	if (pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
336		fattime2timespec(dep->de_ADate, 0, 0, 0, &vap->va_atime);
337		fattime2timespec(dep->de_CDate, dep->de_CTime, dep->de_CHun,
338		    0, &vap->va_birthtime);
339	} else {
340		vap->va_atime = vap->va_mtime;
341		vap->va_birthtime.tv_sec = -1;
342		vap->va_birthtime.tv_nsec = 0;
343	}
344	vap->va_flags = 0;
345	if (dep->de_Attributes & ATTR_ARCHIVE)
346		vap->va_flags |= UF_ARCHIVE;
347	if (dep->de_Attributes & ATTR_HIDDEN)
348		vap->va_flags |= UF_HIDDEN;
349	if (dep->de_Attributes & ATTR_READONLY)
350		vap->va_flags |= UF_READONLY;
351	if (dep->de_Attributes & ATTR_SYSTEM)
352		vap->va_flags |= UF_SYSTEM;
353	vap->va_gen = 0;
354	vap->va_blocksize = pmp->pm_bpcluster;
355	vap->va_bytes =
356	    (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
357	vap->va_type = ap->a_vp->v_type;
358	vap->va_filerev = dep->de_modrev;
359	return (0);
360}
361
362static int
363msdosfs_setattr(ap)
364	struct vop_setattr_args /* {
365		struct vnode *a_vp;
366		struct vattr *a_vap;
367		struct ucred *a_cred;
368	} */ *ap;
369{
370	struct vnode *vp = ap->a_vp;
371	struct denode *dep = VTODE(ap->a_vp);
372	struct msdosfsmount *pmp = dep->de_pmp;
373	struct vattr *vap = ap->a_vap;
374	struct ucred *cred = ap->a_cred;
375	struct thread *td = curthread;
376	int error = 0;
377
378#ifdef MSDOSFS_DEBUG
379	printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
380	    ap->a_vp, vap, cred);
381#endif
382
383	/*
384	 * Check for unsettable attributes.
385	 */
386	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
387	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
388	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
389	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
390#ifdef MSDOSFS_DEBUG
391		printf("msdosfs_setattr(): returning EINVAL\n");
392		printf("    va_type %d, va_nlink %x, va_fsid %lx, va_fileid %lx\n",
393		    vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid);
394		printf("    va_blocksize %lx, va_rdev %x, va_bytes %qx, va_gen %lx\n",
395		    vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
396		printf("    va_uid %x, va_gid %x\n",
397		    vap->va_uid, vap->va_gid);
398#endif
399		return (EINVAL);
400	}
401
402	/*
403	 * We don't allow setting attributes on the root directory.
404	 * The special case for the root directory is because before
405	 * FAT32, the root directory didn't have an entry for itself
406	 * (and was otherwise special).  With FAT32, the root
407	 * directory is not so special, but still doesn't have an
408	 * entry for itself.
409	 */
410	if (vp->v_vflag & VV_ROOT)
411		return (EINVAL);
412
413	if (vap->va_flags != VNOVAL) {
414		if (vp->v_mount->mnt_flag & MNT_RDONLY)
415			return (EROFS);
416		if (cred->cr_uid != pmp->pm_uid) {
417			error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
418			if (error)
419				return (error);
420		}
421		/*
422		 * We are very inconsistent about handling unsupported
423		 * attributes.  We ignored the access time and the
424		 * read and execute bits.  We were strict for the other
425		 * attributes.
426		 */
427		if (vap->va_flags & ~(UF_ARCHIVE | UF_HIDDEN | UF_READONLY |
428		    UF_SYSTEM))
429			return EOPNOTSUPP;
430		if (vap->va_flags & UF_ARCHIVE)
431			dep->de_Attributes |= ATTR_ARCHIVE;
432		else
433			dep->de_Attributes &= ~ATTR_ARCHIVE;
434		if (vap->va_flags & UF_HIDDEN)
435			dep->de_Attributes |= ATTR_HIDDEN;
436		else
437			dep->de_Attributes &= ~ATTR_HIDDEN;
438		/* We don't allow changing the readonly bit on directories. */
439		if (vp->v_type != VDIR) {
440			if (vap->va_flags & UF_READONLY)
441				dep->de_Attributes |= ATTR_READONLY;
442			else
443				dep->de_Attributes &= ~ATTR_READONLY;
444		}
445		if (vap->va_flags & UF_SYSTEM)
446			dep->de_Attributes |= ATTR_SYSTEM;
447		else
448			dep->de_Attributes &= ~ATTR_SYSTEM;
449		dep->de_flag |= DE_MODIFIED;
450	}
451
452	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
453		uid_t uid;
454		gid_t gid;
455
456		if (vp->v_mount->mnt_flag & MNT_RDONLY)
457			return (EROFS);
458		uid = vap->va_uid;
459		if (uid == (uid_t)VNOVAL)
460			uid = pmp->pm_uid;
461		gid = vap->va_gid;
462		if (gid == (gid_t)VNOVAL)
463			gid = pmp->pm_gid;
464		if (cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid ||
465		    (gid != pmp->pm_gid && !groupmember(gid, cred))) {
466			error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0);
467			if (error)
468				return (error);
469		}
470		if (uid != pmp->pm_uid || gid != pmp->pm_gid)
471			return EINVAL;
472	}
473
474	if (vap->va_size != VNOVAL) {
475		switch (vp->v_type) {
476		case VDIR:
477			return (EISDIR);
478		case VREG:
479			/*
480			 * Truncation is only supported for regular files,
481			 * Disallow it if the filesystem is read-only.
482			 */
483			if (vp->v_mount->mnt_flag & MNT_RDONLY)
484				return (EROFS);
485			break;
486		default:
487			/*
488			 * According to POSIX, the result is unspecified
489			 * for file types other than regular files,
490			 * directories and shared memory objects.  We
491			 * don't support any file types except regular
492			 * files and directories in this file system, so
493			 * this (default) case is unreachable and can do
494			 * anything.  Keep falling through to detrunc()
495			 * for now.
496			 */
497			break;
498		}
499		error = detrunc(dep, vap->va_size, 0, cred);
500		if (error)
501			return error;
502	}
503	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
504		if (vp->v_mount->mnt_flag & MNT_RDONLY)
505			return (EROFS);
506		error = vn_utimes_perm(vp, vap, cred, td);
507		if (error != 0)
508			return (error);
509		if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
510		    vap->va_atime.tv_sec != VNOVAL) {
511			dep->de_flag &= ~DE_ACCESS;
512			timespec2fattime(&vap->va_atime, 0,
513			    &dep->de_ADate, NULL, NULL);
514		}
515		if (vap->va_mtime.tv_sec != VNOVAL) {
516			dep->de_flag &= ~DE_UPDATE;
517			timespec2fattime(&vap->va_mtime, 0,
518			    &dep->de_MDate, &dep->de_MTime, NULL);
519		}
520		/*
521		 * We don't set the archive bit when modifying the time of
522		 * a directory to emulate the Windows/DOS behavior.
523		 */
524		if (vp->v_type != VDIR)
525			dep->de_Attributes |= ATTR_ARCHIVE;
526		dep->de_flag |= DE_MODIFIED;
527	}
528	/*
529	 * DOS files only have the ability to have their writability
530	 * attribute set, so we use the owner write bit to set the readonly
531	 * attribute.
532	 */
533	if (vap->va_mode != (mode_t)VNOVAL) {
534		if (vp->v_mount->mnt_flag & MNT_RDONLY)
535			return (EROFS);
536		if (cred->cr_uid != pmp->pm_uid) {
537			error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
538			if (error)
539				return (error);
540		}
541		if (vp->v_type != VDIR) {
542			/* We ignore the read and execute bits. */
543			if (vap->va_mode & VWRITE)
544				dep->de_Attributes &= ~ATTR_READONLY;
545			else
546				dep->de_Attributes |= ATTR_READONLY;
547			dep->de_Attributes |= ATTR_ARCHIVE;
548			dep->de_flag |= DE_MODIFIED;
549		}
550	}
551	return (deupdat(dep, 0));
552}
553
554static int
555msdosfs_read(ap)
556	struct vop_read_args /* {
557		struct vnode *a_vp;
558		struct uio *a_uio;
559		int a_ioflag;
560		struct ucred *a_cred;
561	} */ *ap;
562{
563	int error = 0;
564	int blsize;
565	int isadir;
566	ssize_t orig_resid;
567	u_int n;
568	u_long diff;
569	u_long on;
570	daddr_t lbn;
571	daddr_t rablock;
572	int rasize;
573	int seqcount;
574	struct buf *bp;
575	struct vnode *vp = ap->a_vp;
576	struct denode *dep = VTODE(vp);
577	struct msdosfsmount *pmp = dep->de_pmp;
578	struct uio *uio = ap->a_uio;
579
580	/*
581	 * If they didn't ask for any data, then we are done.
582	 */
583	orig_resid = uio->uio_resid;
584	if (orig_resid == 0)
585		return (0);
586
587	/*
588	 * The caller is supposed to ensure that
589	 * uio->uio_offset >= 0 and uio->uio_resid >= 0.
590	 * We don't need to check for large offsets as in ffs because
591	 * dep->de_FileSize <= DOS_FILESIZE_MAX < OFF_MAX, so large
592	 * offsets cannot cause overflow even in theory.
593	 */
594
595	seqcount = ap->a_ioflag >> IO_SEQSHIFT;
596
597	isadir = dep->de_Attributes & ATTR_DIRECTORY;
598	do {
599		if (uio->uio_offset >= dep->de_FileSize)
600			break;
601		lbn = de_cluster(pmp, uio->uio_offset);
602		rablock = lbn + 1;
603		blsize = pmp->pm_bpcluster;
604		on = uio->uio_offset & pmp->pm_crbomask;
605		/*
606		 * If we are operating on a directory file then be sure to
607		 * do i/o with the vnode for the filesystem instead of the
608		 * vnode for the directory.
609		 */
610		if (isadir) {
611			/* convert cluster # to block # */
612			error = pcbmap(dep, lbn, &lbn, 0, &blsize);
613			if (error == E2BIG) {
614				error = EINVAL;
615				break;
616			} else if (error)
617				break;
618			error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp);
619		} else if (de_cn2off(pmp, rablock) >= dep->de_FileSize) {
620			error = bread(vp, lbn, blsize, NOCRED, &bp);
621		} else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) {
622			error = cluster_read(vp, dep->de_FileSize, lbn, blsize,
623			    NOCRED, on + uio->uio_resid, seqcount, 0, &bp);
624		} else if (seqcount > 1) {
625			rasize = blsize;
626			error = breadn(vp, lbn,
627			    blsize, &rablock, &rasize, 1, NOCRED, &bp);
628		} else {
629			error = bread(vp, lbn, blsize, NOCRED, &bp);
630		}
631		if (error) {
632			brelse(bp);
633			break;
634		}
635		diff = pmp->pm_bpcluster - on;
636		n = diff > uio->uio_resid ? uio->uio_resid : diff;
637		diff = dep->de_FileSize - uio->uio_offset;
638		if (diff < n)
639			n = diff;
640		diff = blsize - bp->b_resid;
641		if (diff < n)
642			n = diff;
643		error = vn_io_fault_uiomove(bp->b_data + on, (int) n, uio);
644		brelse(bp);
645	} while (error == 0 && uio->uio_resid > 0 && n != 0);
646	if (!isadir && (error == 0 || uio->uio_resid != orig_resid) &&
647	    (vp->v_mount->mnt_flag & (MNT_NOATIME | MNT_RDONLY)) == 0)
648		dep->de_flag |= DE_ACCESS;
649	return (error);
650}
651
652/*
653 * Write data to a file or directory.
654 */
655static int
656msdosfs_write(ap)
657	struct vop_write_args /* {
658		struct vnode *a_vp;
659		struct uio *a_uio;
660		int a_ioflag;
661		struct ucred *a_cred;
662	} */ *ap;
663{
664	int n;
665	int croffset;
666	ssize_t resid;
667	u_long osize;
668	int error = 0;
669	u_long count;
670	int seqcount;
671	daddr_t bn, lastcn;
672	struct buf *bp;
673	int ioflag = ap->a_ioflag;
674	struct uio *uio = ap->a_uio;
675	struct vnode *vp = ap->a_vp;
676	struct vnode *thisvp;
677	struct denode *dep = VTODE(vp);
678	struct msdosfsmount *pmp = dep->de_pmp;
679	struct ucred *cred = ap->a_cred;
680
681#ifdef MSDOSFS_DEBUG
682	printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
683	    vp, uio, ioflag, cred);
684	printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
685	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
686#endif
687
688	switch (vp->v_type) {
689	case VREG:
690		if (ioflag & IO_APPEND)
691			uio->uio_offset = dep->de_FileSize;
692		thisvp = vp;
693		break;
694	case VDIR:
695		return EISDIR;
696	default:
697		panic("msdosfs_write(): bad file type");
698	}
699
700	/*
701	 * This is needed (unlike in ffs_write()) because we extend the
702	 * file outside of the loop but we don't want to extend the file
703	 * for writes of 0 bytes.
704	 */
705	if (uio->uio_resid == 0)
706		return (0);
707
708	/*
709	 * The caller is supposed to ensure that
710	 * uio->uio_offset >= 0 and uio->uio_resid >= 0.
711	 */
712	if ((uoff_t)uio->uio_offset + uio->uio_resid > DOS_FILESIZE_MAX)
713		return (EFBIG);
714
715	/*
716	 * If they've exceeded their filesize limit, tell them about it.
717	 */
718	if (vn_rlimit_fsize(vp, uio, uio->uio_td))
719		return (EFBIG);
720
721	/*
722	 * If the offset we are starting the write at is beyond the end of
723	 * the file, then they've done a seek.  Unix filesystems allow
724	 * files with holes in them, DOS doesn't so we must fill the hole
725	 * with zeroed blocks.
726	 */
727	if (uio->uio_offset > dep->de_FileSize) {
728		error = deextend(dep, uio->uio_offset, cred);
729		if (error)
730			return (error);
731	}
732
733	/*
734	 * Remember some values in case the write fails.
735	 */
736	resid = uio->uio_resid;
737	osize = dep->de_FileSize;
738
739	/*
740	 * If we write beyond the end of the file, extend it to its ultimate
741	 * size ahead of the time to hopefully get a contiguous area.
742	 */
743	if (uio->uio_offset + resid > osize) {
744		count = de_clcount(pmp, uio->uio_offset + resid) -
745			de_clcount(pmp, osize);
746		error = extendfile(dep, count, NULL, NULL, 0);
747		if (error &&  (error != ENOSPC || (ioflag & IO_UNIT)))
748			goto errexit;
749		lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
750	} else
751		lastcn = de_clcount(pmp, osize) - 1;
752
753	seqcount = ioflag >> IO_SEQSHIFT;
754	do {
755		if (de_cluster(pmp, uio->uio_offset) > lastcn) {
756			error = ENOSPC;
757			break;
758		}
759
760		croffset = uio->uio_offset & pmp->pm_crbomask;
761		n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
762		if (uio->uio_offset + n > dep->de_FileSize) {
763			dep->de_FileSize = uio->uio_offset + n;
764			/* The object size needs to be set before buffer is allocated */
765			vnode_pager_setsize(vp, dep->de_FileSize);
766		}
767
768		bn = de_cluster(pmp, uio->uio_offset);
769		if ((uio->uio_offset & pmp->pm_crbomask) == 0
770		    && (de_cluster(pmp, uio->uio_offset + uio->uio_resid)
771			> de_cluster(pmp, uio->uio_offset)
772			|| uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
773			/*
774			 * If either the whole cluster gets written,
775			 * or we write the cluster from its start beyond EOF,
776			 * then no need to read data from disk.
777			 */
778			bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0, 0);
779			/*
780			 * This call to vfs_bio_clrbuf() ensures that
781			 * even if vn_io_fault_uiomove() below faults,
782			 * garbage from the newly instantiated buffer
783			 * is not exposed to the userspace via mmap().
784			 */
785			vfs_bio_clrbuf(bp);
786			/*
787			 * Do the bmap now, since pcbmap needs buffers
788			 * for the fat table. (see msdosfs_strategy)
789			 */
790			if (bp->b_blkno == bp->b_lblkno) {
791				error = pcbmap(dep, bp->b_lblkno, &bn, 0, 0);
792				if (error)
793					bp->b_blkno = -1;
794				else
795					bp->b_blkno = bn;
796			}
797			if (bp->b_blkno == -1) {
798				brelse(bp);
799				if (!error)
800					error = EIO;		/* XXX */
801				break;
802			}
803		} else {
804			/*
805			 * The block we need to write into exists, so read it in.
806			 */
807			error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp);
808			if (error) {
809				brelse(bp);
810				break;
811			}
812		}
813
814		/*
815		 * Should these vnode_pager_* functions be done on dir
816		 * files?
817		 */
818
819		/*
820		 * Copy the data from user space into the buf header.
821		 */
822		error = vn_io_fault_uiomove(bp->b_data + croffset, n, uio);
823		if (error) {
824			brelse(bp);
825			break;
826		}
827
828		/* Prepare for clustered writes in some else clauses. */
829		if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
830			bp->b_flags |= B_CLUSTEROK;
831
832		/*
833		 * If IO_SYNC, then each buffer is written synchronously.
834		 * Otherwise, if we have a severe page deficiency then
835		 * write the buffer asynchronously.  Otherwise, if on a
836		 * cluster boundary then write the buffer asynchronously,
837		 * combining it with contiguous clusters if permitted and
838		 * possible, since we don't expect more writes into this
839		 * buffer soon.  Otherwise, do a delayed write because we
840		 * expect more writes into this buffer soon.
841		 */
842		if (ioflag & IO_SYNC)
843			(void)bwrite(bp);
844		else if (vm_page_count_severe() || buf_dirty_count_severe())
845			bawrite(bp);
846		else if (n + croffset == pmp->pm_bpcluster) {
847			if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
848				cluster_write(vp, bp, dep->de_FileSize,
849				    seqcount, 0);
850			else
851				bawrite(bp);
852		} else
853			bdwrite(bp);
854		dep->de_flag |= DE_UPDATE;
855	} while (error == 0 && uio->uio_resid > 0);
856
857	/*
858	 * If the write failed and they want us to, truncate the file back
859	 * to the size it was before the write was attempted.
860	 */
861errexit:
862	if (error) {
863		if (ioflag & IO_UNIT) {
864			detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
865			uio->uio_offset -= resid - uio->uio_resid;
866			uio->uio_resid = resid;
867		} else {
868			detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED);
869			if (uio->uio_resid != resid)
870				error = 0;
871		}
872	} else if (ioflag & IO_SYNC)
873		error = deupdat(dep, 1);
874	return (error);
875}
876
877/*
878 * Flush the blocks of a file to disk.
879 */
880static int
881msdosfs_fsync(ap)
882	struct vop_fsync_args /* {
883		struct vnode *a_vp;
884		struct ucred *a_cred;
885		int a_waitfor;
886		struct thread *a_td;
887	} */ *ap;
888{
889	struct vnode *devvp;
890	int allerror, error;
891
892	vop_stdfsync(ap);
893
894	/*
895	* If the syncing request comes from fsync(2), sync the entire
896	* FAT and any other metadata that happens to be on devvp.  We
897	* need this mainly for the FAT.  We write the FAT sloppily, and
898	* syncing it all now is the best we can easily do to get all
899	* directory entries associated with the file (not just the file)
900	* fully synced.  The other metadata includes critical metadata
901	* for all directory entries, but only in the MNT_ASYNC case.  We
902	* will soon sync all metadata in the file's directory entry.
903	* Non-critical metadata for associated directory entries only
904	* gets synced accidentally, as in most file systems.
905	*/
906	if (ap->a_waitfor == MNT_WAIT) {
907		devvp = VTODE(ap->a_vp)->de_pmp->pm_devvp;
908		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
909		allerror = VOP_FSYNC(devvp, MNT_WAIT, ap->a_td);
910		VOP_UNLOCK(devvp, 0);
911	} else
912		allerror = 0;
913
914	error = deupdat(VTODE(ap->a_vp), ap->a_waitfor == MNT_WAIT);
915	if (allerror == 0)
916		allerror = error;
917	return (allerror);
918}
919
920static int
921msdosfs_remove(ap)
922	struct vop_remove_args /* {
923		struct vnode *a_dvp;
924		struct vnode *a_vp;
925		struct componentname *a_cnp;
926	} */ *ap;
927{
928	struct denode *dep = VTODE(ap->a_vp);
929	struct denode *ddep = VTODE(ap->a_dvp);
930	int error;
931
932	if (ap->a_vp->v_type == VDIR)
933		error = EPERM;
934	else
935		error = removede(ddep, dep);
936#ifdef MSDOSFS_DEBUG
937	printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep, ap->a_vp->v_usecount);
938#endif
939	return (error);
940}
941
942/*
943 * DOS filesystems don't know what links are.
944 */
945static int
946msdosfs_link(ap)
947	struct vop_link_args /* {
948		struct vnode *a_tdvp;
949		struct vnode *a_vp;
950		struct componentname *a_cnp;
951	} */ *ap;
952{
953	return (EOPNOTSUPP);
954}
955
956/*
957 * Renames on files require moving the denode to a new hash queue since the
958 * denode's location is used to compute which hash queue to put the file
959 * in. Unless it is a rename in place.  For example "mv a b".
960 *
961 * What follows is the basic algorithm:
962 *
963 * if (file move) {
964 *	if (dest file exists) {
965 *		remove dest file
966 *	}
967 *	if (dest and src in same directory) {
968 *		rewrite name in existing directory slot
969 *	} else {
970 *		write new entry in dest directory
971 *		update offset and dirclust in denode
972 *		move denode to new hash chain
973 *		clear old directory entry
974 *	}
975 * } else {
976 *	directory move
977 *	if (dest directory exists) {
978 *		if (dest is not empty) {
979 *			return ENOTEMPTY
980 *		}
981 *		remove dest directory
982 *	}
983 *	if (dest and src in same directory) {
984 *		rewrite name in existing entry
985 *	} else {
986 *		be sure dest is not a child of src directory
987 *		write entry in dest directory
988 *		update "." and ".." in moved directory
989 *		clear old directory entry for moved directory
990 *	}
991 * }
992 *
993 * On entry:
994 *	source's parent directory is unlocked
995 *	source file or directory is unlocked
996 *	destination's parent directory is locked
997 *	destination file or directory is locked if it exists
998 *
999 * On exit:
1000 *	all denodes should be released
1001 */
1002static int
1003msdosfs_rename(ap)
1004	struct vop_rename_args /* {
1005		struct vnode *a_fdvp;
1006		struct vnode *a_fvp;
1007		struct componentname *a_fcnp;
1008		struct vnode *a_tdvp;
1009		struct vnode *a_tvp;
1010		struct componentname *a_tcnp;
1011	} */ *ap;
1012{
1013	struct vnode *tdvp = ap->a_tdvp;
1014	struct vnode *fvp = ap->a_fvp;
1015	struct vnode *fdvp = ap->a_fdvp;
1016	struct vnode *tvp = ap->a_tvp;
1017	struct componentname *tcnp = ap->a_tcnp;
1018	struct componentname *fcnp = ap->a_fcnp;
1019	struct denode *ip, *xp, *dp, *zp;
1020	u_char toname[12], oldname[11];
1021	u_long from_diroffset, to_diroffset;
1022	u_char to_count;
1023	int doingdirectory = 0, newparent = 0;
1024	int error;
1025	u_long cn, pcl;
1026	daddr_t bn;
1027	struct denode *fddep;	/* from file's parent directory	 */
1028	struct msdosfsmount *pmp;
1029	struct direntry *dotdotp;
1030	struct buf *bp;
1031
1032	fddep = VTODE(ap->a_fdvp);
1033	pmp = fddep->de_pmp;
1034
1035	pmp = VFSTOMSDOSFS(fdvp->v_mount);
1036
1037#ifdef DIAGNOSTIC
1038	if ((tcnp->cn_flags & HASBUF) == 0 ||
1039	    (fcnp->cn_flags & HASBUF) == 0)
1040		panic("msdosfs_rename: no name");
1041#endif
1042	/*
1043	 * Check for cross-device rename.
1044	 */
1045	if (fvp->v_mount != tdvp->v_mount ||
1046	    (tvp && fvp->v_mount != tvp->v_mount)) {
1047		error = EXDEV;
1048abortit:
1049		if (tdvp == tvp)
1050			vrele(tdvp);
1051		else
1052			vput(tdvp);
1053		if (tvp)
1054			vput(tvp);
1055		vrele(fdvp);
1056		vrele(fvp);
1057		return (error);
1058	}
1059
1060	/*
1061	 * If source and dest are the same, do nothing.
1062	 */
1063	if (tvp == fvp) {
1064		error = 0;
1065		goto abortit;
1066	}
1067
1068	error = vn_lock(fvp, LK_EXCLUSIVE);
1069	if (error)
1070		goto abortit;
1071	dp = VTODE(fdvp);
1072	ip = VTODE(fvp);
1073
1074	/*
1075	 * Be sure we are not renaming ".", "..", or an alias of ".". This
1076	 * leads to a crippled directory tree.  It's pretty tough to do a
1077	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
1078	 * doesn't work if the ".." entry is missing.
1079	 */
1080	if (ip->de_Attributes & ATTR_DIRECTORY) {
1081		/*
1082		 * Avoid ".", "..", and aliases of "." for obvious reasons.
1083		 */
1084		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
1085		    dp == ip ||
1086		    (fcnp->cn_flags & ISDOTDOT) ||
1087		    (tcnp->cn_flags & ISDOTDOT) ||
1088		    (ip->de_flag & DE_RENAME)) {
1089			VOP_UNLOCK(fvp, 0);
1090			error = EINVAL;
1091			goto abortit;
1092		}
1093		ip->de_flag |= DE_RENAME;
1094		doingdirectory++;
1095	}
1096
1097	/*
1098	 * When the target exists, both the directory
1099	 * and target vnodes are returned locked.
1100	 */
1101	dp = VTODE(tdvp);
1102	xp = tvp ? VTODE(tvp) : NULL;
1103	/*
1104	 * Remember direntry place to use for destination
1105	 */
1106	to_diroffset = dp->de_fndoffset;
1107	to_count = dp->de_fndcnt;
1108
1109	/*
1110	 * If ".." must be changed (ie the directory gets a new
1111	 * parent) then the source directory must not be in the
1112	 * directory hierarchy above the target, as this would
1113	 * orphan everything below the source directory. Also
1114	 * the user must have write permission in the source so
1115	 * as to be able to change "..". We must repeat the call
1116	 * to namei, as the parent directory is unlocked by the
1117	 * call to doscheckpath().
1118	 */
1119	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
1120	VOP_UNLOCK(fvp, 0);
1121	if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
1122		newparent = 1;
1123	if (doingdirectory && newparent) {
1124		if (error)	/* write access check above */
1125			goto bad;
1126		if (xp != NULL)
1127			vput(tvp);
1128		/*
1129		 * doscheckpath() vput()'s dp,
1130		 * so we have to do a relookup afterwards
1131		 */
1132		error = doscheckpath(ip, dp);
1133		if (error)
1134			goto out;
1135		if ((tcnp->cn_flags & SAVESTART) == 0)
1136			panic("msdosfs_rename: lost to startdir");
1137		error = relookup(tdvp, &tvp, tcnp);
1138		if (error)
1139			goto out;
1140		dp = VTODE(tdvp);
1141		xp = tvp ? VTODE(tvp) : NULL;
1142	}
1143
1144	if (xp != NULL) {
1145		/*
1146		 * Target must be empty if a directory and have no links
1147		 * to it. Also, ensure source and target are compatible
1148		 * (both directories, or both not directories).
1149		 */
1150		if (xp->de_Attributes & ATTR_DIRECTORY) {
1151			if (!dosdirempty(xp)) {
1152				error = ENOTEMPTY;
1153				goto bad;
1154			}
1155			if (!doingdirectory) {
1156				error = ENOTDIR;
1157				goto bad;
1158			}
1159			cache_purge(tdvp);
1160		} else if (doingdirectory) {
1161			error = EISDIR;
1162			goto bad;
1163		}
1164		error = removede(dp, xp);
1165		if (error)
1166			goto bad;
1167		vput(tvp);
1168		xp = NULL;
1169	}
1170
1171	/*
1172	 * Convert the filename in tcnp into a dos filename. We copy this
1173	 * into the denode and directory entry for the destination
1174	 * file/directory.
1175	 */
1176	error = uniqdosname(VTODE(tdvp), tcnp, toname);
1177	if (error)
1178		goto abortit;
1179
1180	/*
1181	 * Since from wasn't locked at various places above,
1182	 * have to do a relookup here.
1183	 */
1184	fcnp->cn_flags &= ~MODMASK;
1185	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
1186	if ((fcnp->cn_flags & SAVESTART) == 0)
1187		panic("msdosfs_rename: lost from startdir");
1188	if (!newparent)
1189		VOP_UNLOCK(tdvp, 0);
1190	if (relookup(fdvp, &fvp, fcnp) == 0)
1191		vrele(fdvp);
1192	if (fvp == NULL) {
1193		/*
1194		 * From name has disappeared.
1195		 */
1196		if (doingdirectory)
1197			panic("rename: lost dir entry");
1198		if (newparent)
1199			VOP_UNLOCK(tdvp, 0);
1200		vrele(tdvp);
1201		vrele(ap->a_fvp);
1202		return 0;
1203	}
1204	xp = VTODE(fvp);
1205	zp = VTODE(fdvp);
1206	from_diroffset = zp->de_fndoffset;
1207
1208	/*
1209	 * Ensure that the directory entry still exists and has not
1210	 * changed till now. If the source is a file the entry may
1211	 * have been unlinked or renamed. In either case there is
1212	 * no further work to be done. If the source is a directory
1213	 * then it cannot have been rmdir'ed or renamed; this is
1214	 * prohibited by the DE_RENAME flag.
1215	 */
1216	if (xp != ip) {
1217		if (doingdirectory)
1218			panic("rename: lost dir entry");
1219		VOP_UNLOCK(fvp, 0);
1220		if (newparent)
1221			VOP_UNLOCK(fdvp, 0);
1222		vrele(ap->a_fvp);
1223		xp = NULL;
1224	} else {
1225		vrele(fvp);
1226		xp = NULL;
1227
1228		/*
1229		 * First write a new entry in the destination
1230		 * directory and mark the entry in the source directory
1231		 * as deleted.  Then move the denode to the correct hash
1232		 * chain for its new location in the filesystem.  And, if
1233		 * we moved a directory, then update its .. entry to point
1234		 * to the new parent directory.
1235		 */
1236		bcopy(ip->de_Name, oldname, 11);
1237		bcopy(toname, ip->de_Name, 11);	/* update denode */
1238		dp->de_fndoffset = to_diroffset;
1239		dp->de_fndcnt = to_count;
1240		error = createde(ip, dp, (struct denode **)0, tcnp);
1241		if (error) {
1242			bcopy(oldname, ip->de_Name, 11);
1243			if (newparent)
1244				VOP_UNLOCK(fdvp, 0);
1245			VOP_UNLOCK(fvp, 0);
1246			goto bad;
1247		}
1248		/*
1249		 * If ip is for a directory, then its name should always
1250		 * be "." since it is for the directory entry in the
1251		 * directory itself (msdosfs_lookup() always translates
1252		 * to the "." entry so as to get a unique denode, except
1253		 * for the root directory there are different
1254		 * complications).  However, we just corrupted its name
1255		 * to pass the correct name to createde().  Undo this.
1256		 */
1257		if ((ip->de_Attributes & ATTR_DIRECTORY) != 0)
1258			bcopy(oldname, ip->de_Name, 11);
1259		ip->de_refcnt++;
1260		zp->de_fndoffset = from_diroffset;
1261		error = removede(zp, ip);
1262		if (error) {
1263			/* XXX should downgrade to ro here, fs is corrupt */
1264			if (newparent)
1265				VOP_UNLOCK(fdvp, 0);
1266			VOP_UNLOCK(fvp, 0);
1267			goto bad;
1268		}
1269		if (!doingdirectory) {
1270			error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
1271				       &ip->de_dirclust, 0);
1272			if (error) {
1273				/* XXX should downgrade to ro here, fs is corrupt */
1274				if (newparent)
1275					VOP_UNLOCK(fdvp, 0);
1276				VOP_UNLOCK(fvp, 0);
1277				goto bad;
1278			}
1279			if (ip->de_dirclust == MSDOSFSROOT)
1280				ip->de_diroffset = to_diroffset;
1281			else
1282				ip->de_diroffset = to_diroffset & pmp->pm_crbomask;
1283		}
1284		reinsert(ip);
1285		if (newparent)
1286			VOP_UNLOCK(fdvp, 0);
1287	}
1288
1289	/*
1290	 * If we moved a directory to a new parent directory, then we must
1291	 * fixup the ".." entry in the moved directory.
1292	 */
1293	if (doingdirectory && newparent) {
1294		cn = ip->de_StartCluster;
1295		if (cn == MSDOSFSROOT) {
1296			/* this should never happen */
1297			panic("msdosfs_rename(): updating .. in root directory?");
1298		} else
1299			bn = cntobn(pmp, cn);
1300		error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
1301			      NOCRED, &bp);
1302		if (error) {
1303			/* XXX should downgrade to ro here, fs is corrupt */
1304			brelse(bp);
1305			VOP_UNLOCK(fvp, 0);
1306			goto bad;
1307		}
1308		dotdotp = (struct direntry *)bp->b_data + 1;
1309		pcl = dp->de_StartCluster;
1310		if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1311			pcl = MSDOSFSROOT;
1312		putushort(dotdotp->deStartCluster, pcl);
1313		if (FAT32(pmp))
1314			putushort(dotdotp->deHighClust, pcl >> 16);
1315		if (DOINGASYNC(fvp))
1316			bdwrite(bp);
1317		else if ((error = bwrite(bp)) != 0) {
1318			/* XXX should downgrade to ro here, fs is corrupt */
1319			VOP_UNLOCK(fvp, 0);
1320			goto bad;
1321		}
1322	}
1323
1324	/*
1325	 * The msdosfs lookup is case insensitive. Several aliases may
1326	 * be inserted for a single directory entry. As a consequnce,
1327	 * name cache purge done by lookup for fvp when DELETE op for
1328	 * namei is specified, might be not enough to expunge all
1329	 * namecache entries that were installed for this direntry.
1330	 */
1331	cache_purge(fvp);
1332	VOP_UNLOCK(fvp, 0);
1333bad:
1334	if (xp)
1335		vput(tvp);
1336	vput(tdvp);
1337out:
1338	ip->de_flag &= ~DE_RENAME;
1339	vrele(fdvp);
1340	vrele(fvp);
1341	return (error);
1342
1343}
1344
1345static struct {
1346	struct direntry dot;
1347	struct direntry dotdot;
1348} dosdirtemplate = {
1349	{	".          ",				/* the . entry */
1350		ATTR_DIRECTORY,				/* file attribute */
1351		0,					/* reserved */
1352		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1353		{ 0, 0 },				/* access date */
1354		{ 0, 0 },				/* high bits of start cluster */
1355		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1356		{ 0, 0 },				/* startcluster */
1357		{ 0, 0, 0, 0 }				/* filesize */
1358	},
1359	{	"..         ",				/* the .. entry */
1360		ATTR_DIRECTORY,				/* file attribute */
1361		0,					/* reserved */
1362		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1363		{ 0, 0 },				/* access date */
1364		{ 0, 0 },				/* high bits of start cluster */
1365		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1366		{ 0, 0 },				/* startcluster */
1367		{ 0, 0, 0, 0 }				/* filesize */
1368	}
1369};
1370
1371static int
1372msdosfs_mkdir(ap)
1373	struct vop_mkdir_args /* {
1374		struct vnode *a_dvp;
1375		struct vnode **a_vpp;
1376		struvt componentname *a_cnp;
1377		struct vattr *a_vap;
1378	} */ *ap;
1379{
1380	struct componentname *cnp = ap->a_cnp;
1381	struct denode *dep;
1382	struct denode *pdep = VTODE(ap->a_dvp);
1383	struct direntry *denp;
1384	struct msdosfsmount *pmp = pdep->de_pmp;
1385	struct buf *bp;
1386	u_long newcluster, pcl;
1387	int bn;
1388	int error;
1389	struct denode ndirent;
1390	struct timespec ts;
1391
1392	/*
1393	 * If this is the root directory and there is no space left we
1394	 * can't do anything.  This is because the root directory can not
1395	 * change size.
1396	 */
1397	if (pdep->de_StartCluster == MSDOSFSROOT
1398	    && pdep->de_fndoffset >= pdep->de_FileSize) {
1399		error = ENOSPC;
1400		goto bad2;
1401	}
1402
1403	/*
1404	 * Allocate a cluster to hold the about to be created directory.
1405	 */
1406	error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
1407	if (error)
1408		goto bad2;
1409
1410	bzero(&ndirent, sizeof(ndirent));
1411	ndirent.de_pmp = pmp;
1412	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
1413	getnanotime(&ts);
1414	DETIMES(&ndirent, &ts, &ts, &ts);
1415
1416	/*
1417	 * Now fill the cluster with the "." and ".." entries. And write
1418	 * the cluster to disk.  This way it is there for the parent
1419	 * directory to be pointing at if there were a crash.
1420	 */
1421	bn = cntobn(pmp, newcluster);
1422	/* always succeeds */
1423	bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0);
1424	bzero(bp->b_data, pmp->pm_bpcluster);
1425	bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
1426	denp = (struct direntry *)bp->b_data;
1427	putushort(denp[0].deStartCluster, newcluster);
1428	putushort(denp[0].deCDate, ndirent.de_CDate);
1429	putushort(denp[0].deCTime, ndirent.de_CTime);
1430	denp[0].deCHundredth = ndirent.de_CHun;
1431	putushort(denp[0].deADate, ndirent.de_ADate);
1432	putushort(denp[0].deMDate, ndirent.de_MDate);
1433	putushort(denp[0].deMTime, ndirent.de_MTime);
1434	pcl = pdep->de_StartCluster;
1435	/*
1436	 * Although the root directory has a non-magic starting cluster
1437	 * number for FAT32, chkdsk and fsck_msdosfs still require
1438	 * references to it in dotdot entries to be magic.
1439	 */
1440	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1441		pcl = MSDOSFSROOT;
1442	putushort(denp[1].deStartCluster, pcl);
1443	putushort(denp[1].deCDate, ndirent.de_CDate);
1444	putushort(denp[1].deCTime, ndirent.de_CTime);
1445	denp[1].deCHundredth = ndirent.de_CHun;
1446	putushort(denp[1].deADate, ndirent.de_ADate);
1447	putushort(denp[1].deMDate, ndirent.de_MDate);
1448	putushort(denp[1].deMTime, ndirent.de_MTime);
1449	if (FAT32(pmp)) {
1450		putushort(denp[0].deHighClust, newcluster >> 16);
1451		putushort(denp[1].deHighClust, pcl >> 16);
1452	}
1453
1454	if (DOINGASYNC(ap->a_dvp))
1455		bdwrite(bp);
1456	else if ((error = bwrite(bp)) != 0)
1457		goto bad;
1458
1459	/*
1460	 * Now build up a directory entry pointing to the newly allocated
1461	 * cluster.  This will be written to an empty slot in the parent
1462	 * directory.
1463	 */
1464#ifdef DIAGNOSTIC
1465	if ((cnp->cn_flags & HASBUF) == 0)
1466		panic("msdosfs_mkdir: no name");
1467#endif
1468	error = uniqdosname(pdep, cnp, ndirent.de_Name);
1469	if (error)
1470		goto bad;
1471
1472	ndirent.de_Attributes = ATTR_DIRECTORY;
1473	ndirent.de_LowerCase = 0;
1474	ndirent.de_StartCluster = newcluster;
1475	ndirent.de_FileSize = 0;
1476	error = createde(&ndirent, pdep, &dep, cnp);
1477	if (error)
1478		goto bad;
1479	*ap->a_vpp = DETOV(dep);
1480	return (0);
1481
1482bad:
1483	clusterfree(pmp, newcluster, NULL);
1484bad2:
1485	return (error);
1486}
1487
1488static int
1489msdosfs_rmdir(ap)
1490	struct vop_rmdir_args /* {
1491		struct vnode *a_dvp;
1492		struct vnode *a_vp;
1493		struct componentname *a_cnp;
1494	} */ *ap;
1495{
1496	struct vnode *vp = ap->a_vp;
1497	struct vnode *dvp = ap->a_dvp;
1498	struct componentname *cnp = ap->a_cnp;
1499	struct denode *ip, *dp;
1500	int error;
1501
1502	ip = VTODE(vp);
1503	dp = VTODE(dvp);
1504
1505	/*
1506	 * Verify the directory is empty (and valid).
1507	 * (Rmdir ".." won't be valid since
1508	 *  ".." will contain a reference to
1509	 *  the current directory and thus be
1510	 *  non-empty.)
1511	 */
1512	error = 0;
1513	if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
1514		error = ENOTEMPTY;
1515		goto out;
1516	}
1517	/*
1518	 * Delete the entry from the directory.  For dos filesystems this
1519	 * gets rid of the directory entry on disk, the in memory copy
1520	 * still exists but the de_refcnt is <= 0.  This prevents it from
1521	 * being found by deget().  When the vput() on dep is done we give
1522	 * up access and eventually msdosfs_reclaim() will be called which
1523	 * will remove it from the denode cache.
1524	 */
1525	error = removede(dp, ip);
1526	if (error)
1527		goto out;
1528	/*
1529	 * This is where we decrement the link count in the parent
1530	 * directory.  Since dos filesystems don't do this we just purge
1531	 * the name cache.
1532	 */
1533	cache_purge(dvp);
1534	/*
1535	 * Truncate the directory that is being deleted.
1536	 */
1537	error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
1538	cache_purge(vp);
1539
1540out:
1541	return (error);
1542}
1543
1544/*
1545 * DOS filesystems don't know what symlinks are.
1546 */
1547static int
1548msdosfs_symlink(ap)
1549	struct vop_symlink_args /* {
1550		struct vnode *a_dvp;
1551		struct vnode **a_vpp;
1552		struct componentname *a_cnp;
1553		struct vattr *a_vap;
1554		char *a_target;
1555	} */ *ap;
1556{
1557	return (EOPNOTSUPP);
1558}
1559
1560static int
1561msdosfs_readdir(ap)
1562	struct vop_readdir_args /* {
1563		struct vnode *a_vp;
1564		struct uio *a_uio;
1565		struct ucred *a_cred;
1566		int *a_eofflag;
1567		int *a_ncookies;
1568		u_long **a_cookies;
1569	} */ *ap;
1570{
1571	struct mbnambuf nb;
1572	int error = 0;
1573	int diff;
1574	long n;
1575	int blsize;
1576	long on;
1577	u_long cn;
1578	uint64_t fileno;
1579	u_long dirsperblk;
1580	long bias = 0;
1581	daddr_t bn, lbn;
1582	struct buf *bp;
1583	struct denode *dep = VTODE(ap->a_vp);
1584	struct msdosfsmount *pmp = dep->de_pmp;
1585	struct direntry *dentp;
1586	struct dirent dirbuf;
1587	struct uio *uio = ap->a_uio;
1588	u_long *cookies = NULL;
1589	int ncookies = 0;
1590	off_t offset, off;
1591	int chksum = -1;
1592
1593#ifdef MSDOSFS_DEBUG
1594	printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
1595	    ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
1596#endif
1597
1598	/*
1599	 * msdosfs_readdir() won't operate properly on regular files since
1600	 * it does i/o only with the filesystem vnode, and hence can
1601	 * retrieve the wrong block from the buffer cache for a plain file.
1602	 * So, fail attempts to readdir() on a plain file.
1603	 */
1604	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1605		return (ENOTDIR);
1606
1607	/*
1608	 * To be safe, initialize dirbuf
1609	 */
1610	bzero(dirbuf.d_name, sizeof(dirbuf.d_name));
1611
1612	/*
1613	 * If the user buffer is smaller than the size of one dos directory
1614	 * entry or the file offset is not a multiple of the size of a
1615	 * directory entry, then we fail the read.
1616	 */
1617	off = offset = uio->uio_offset;
1618	if (uio->uio_resid < sizeof(struct direntry) ||
1619	    (offset & (sizeof(struct direntry) - 1)))
1620		return (EINVAL);
1621
1622	if (ap->a_ncookies) {
1623		ncookies = uio->uio_resid / 16;
1624		cookies = malloc(ncookies * sizeof(u_long), M_TEMP,
1625		       M_WAITOK);
1626		*ap->a_cookies = cookies;
1627		*ap->a_ncookies = ncookies;
1628	}
1629
1630	dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
1631
1632	/*
1633	 * If they are reading from the root directory then, we simulate
1634	 * the . and .. entries since these don't exist in the root
1635	 * directory.  We also set the offset bias to make up for having to
1636	 * simulate these entries. By this I mean that at file offset 64 we
1637	 * read the first entry in the root directory that lives on disk.
1638	 */
1639	if (dep->de_StartCluster == MSDOSFSROOT
1640	    || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
1641#if 0
1642		printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
1643		    offset);
1644#endif
1645		bias = 2 * sizeof(struct direntry);
1646		if (offset < bias) {
1647			for (n = (int)offset / sizeof(struct direntry);
1648			     n < 2; n++) {
1649				if (FAT32(pmp))
1650					fileno = (uint64_t)cntobn(pmp,
1651								 pmp->pm_rootdirblk)
1652							  * dirsperblk;
1653				else
1654					fileno = 1;
1655				if (pmp->pm_flags & MSDOSFS_LARGEFS) {
1656					dirbuf.d_fileno =
1657					    msdosfs_fileno_map(pmp->pm_mountp,
1658					    fileno);
1659				} else {
1660
1661					dirbuf.d_fileno = (uint32_t)fileno;
1662				}
1663				dirbuf.d_type = DT_DIR;
1664				switch (n) {
1665				case 0:
1666					dirbuf.d_namlen = 1;
1667					strcpy(dirbuf.d_name, ".");
1668					break;
1669				case 1:
1670					dirbuf.d_namlen = 2;
1671					strcpy(dirbuf.d_name, "..");
1672					break;
1673				}
1674				dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
1675				if (uio->uio_resid < dirbuf.d_reclen)
1676					goto out;
1677				error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1678				if (error)
1679					goto out;
1680				offset += sizeof(struct direntry);
1681				off = offset;
1682				if (cookies) {
1683					*cookies++ = offset;
1684					if (--ncookies <= 0)
1685						goto out;
1686				}
1687			}
1688		}
1689	}
1690
1691	mbnambuf_init(&nb);
1692	off = offset;
1693	while (uio->uio_resid > 0) {
1694		lbn = de_cluster(pmp, offset - bias);
1695		on = (offset - bias) & pmp->pm_crbomask;
1696		n = min(pmp->pm_bpcluster - on, uio->uio_resid);
1697		diff = dep->de_FileSize - (offset - bias);
1698		if (diff <= 0)
1699			break;
1700		n = min(n, diff);
1701		error = pcbmap(dep, lbn, &bn, &cn, &blsize);
1702		if (error)
1703			break;
1704		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
1705		if (error) {
1706			brelse(bp);
1707			return (error);
1708		}
1709		n = min(n, blsize - bp->b_resid);
1710		if (n == 0) {
1711			brelse(bp);
1712			return (EIO);
1713		}
1714
1715		/*
1716		 * Convert from dos directory entries to fs-independent
1717		 * directory entries.
1718		 */
1719		for (dentp = (struct direntry *)(bp->b_data + on);
1720		     (char *)dentp < bp->b_data + on + n;
1721		     dentp++, offset += sizeof(struct direntry)) {
1722#if 0
1723			printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1724			    dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1725#endif
1726			/*
1727			 * If this is an unused entry, we can stop.
1728			 */
1729			if (dentp->deName[0] == SLOT_EMPTY) {
1730				brelse(bp);
1731				goto out;
1732			}
1733			/*
1734			 * Skip deleted entries.
1735			 */
1736			if (dentp->deName[0] == SLOT_DELETED) {
1737				chksum = -1;
1738				mbnambuf_init(&nb);
1739				continue;
1740			}
1741
1742			/*
1743			 * Handle Win95 long directory entries
1744			 */
1745			if (dentp->deAttributes == ATTR_WIN95) {
1746				if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
1747					continue;
1748				chksum = win2unixfn(&nb,
1749				    (struct winentry *)dentp, chksum, pmp);
1750				continue;
1751			}
1752
1753			/*
1754			 * Skip volume labels
1755			 */
1756			if (dentp->deAttributes & ATTR_VOLUME) {
1757				chksum = -1;
1758				mbnambuf_init(&nb);
1759				continue;
1760			}
1761			/*
1762			 * This computation of d_fileno must match
1763			 * the computation of va_fileid in
1764			 * msdosfs_getattr.
1765			 */
1766			if (dentp->deAttributes & ATTR_DIRECTORY) {
1767				fileno = getushort(dentp->deStartCluster);
1768				if (FAT32(pmp))
1769					fileno |= getushort(dentp->deHighClust) << 16;
1770				/* if this is the root directory */
1771				if (fileno == MSDOSFSROOT)
1772					if (FAT32(pmp))
1773						fileno = (uint64_t)cntobn(pmp,
1774								pmp->pm_rootdirblk)
1775							 * dirsperblk;
1776					else
1777						fileno = 1;
1778				else
1779					fileno = (uint64_t)cntobn(pmp, fileno) *
1780					    dirsperblk;
1781				dirbuf.d_type = DT_DIR;
1782			} else {
1783				fileno = (uoff_t)offset /
1784				    sizeof(struct direntry);
1785				dirbuf.d_type = DT_REG;
1786			}
1787			if (pmp->pm_flags & MSDOSFS_LARGEFS) {
1788				dirbuf.d_fileno =
1789				    msdosfs_fileno_map(pmp->pm_mountp, fileno);
1790			} else
1791				dirbuf.d_fileno = (uint32_t)fileno;
1792
1793			if (chksum != winChksum(dentp->deName)) {
1794				dirbuf.d_namlen = dos2unixfn(dentp->deName,
1795				    (u_char *)dirbuf.d_name,
1796				    dentp->deLowerCase |
1797					((pmp->pm_flags & MSDOSFSMNT_SHORTNAME) ?
1798					(LCASE_BASE | LCASE_EXT) : 0),
1799				    pmp);
1800				mbnambuf_init(&nb);
1801			} else
1802				mbnambuf_flush(&nb, &dirbuf);
1803			chksum = -1;
1804			dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
1805			if (uio->uio_resid < dirbuf.d_reclen) {
1806				brelse(bp);
1807				goto out;
1808			}
1809			error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1810			if (error) {
1811				brelse(bp);
1812				goto out;
1813			}
1814			if (cookies) {
1815				*cookies++ = offset + sizeof(struct direntry);
1816				if (--ncookies <= 0) {
1817					brelse(bp);
1818					goto out;
1819				}
1820			}
1821			off = offset + sizeof(struct direntry);
1822		}
1823		brelse(bp);
1824	}
1825out:
1826	/* Subtract unused cookies */
1827	if (ap->a_ncookies)
1828		*ap->a_ncookies -= ncookies;
1829
1830	uio->uio_offset = off;
1831
1832	/*
1833	 * Set the eofflag (NFS uses it)
1834	 */
1835	if (ap->a_eofflag) {
1836		if (dep->de_FileSize - (offset - bias) <= 0)
1837			*ap->a_eofflag = 1;
1838		else
1839			*ap->a_eofflag = 0;
1840	}
1841	return (error);
1842}
1843
1844/*-
1845 * a_vp   - pointer to the file's vnode
1846 * a_bn   - logical block number within the file (cluster number for us)
1847 * a_bop  - where to return the bufobj of the special file containing the fs
1848 * a_bnp  - where to return the "physical" block number corresponding to a_bn
1849 *          (relative to the special file; units are blocks of size DEV_BSIZE)
1850 * a_runp - where to return the "run past" a_bn.  This is the count of logical
1851 *          blocks whose physical blocks (together with a_bn's physical block)
1852 *          are contiguous.
1853 * a_runb - where to return the "run before" a_bn.
1854 */
1855static int
1856msdosfs_bmap(ap)
1857	struct vop_bmap_args /* {
1858		struct vnode *a_vp;
1859		daddr_t a_bn;
1860		struct bufobj **a_bop;
1861		daddr_t *a_bnp;
1862		int *a_runp;
1863		int *a_runb;
1864	} */ *ap;
1865{
1866	struct denode *dep;
1867	struct mount *mp;
1868	struct msdosfsmount *pmp;
1869	struct vnode *vp;
1870	daddr_t runbn;
1871	u_long cn;
1872	int bnpercn, error, maxio, maxrun, run;
1873
1874	vp = ap->a_vp;
1875	dep = VTODE(vp);
1876	pmp = dep->de_pmp;
1877	if (ap->a_bop != NULL)
1878		*ap->a_bop = &pmp->pm_devvp->v_bufobj;
1879	if (ap->a_bnp == NULL)
1880		return (0);
1881	if (ap->a_runp != NULL)
1882		*ap->a_runp = 0;
1883	if (ap->a_runb != NULL)
1884		*ap->a_runb = 0;
1885	cn = ap->a_bn;
1886	if (cn != ap->a_bn)
1887		return (EFBIG);
1888	error = pcbmap(dep, cn, ap->a_bnp, NULL, NULL);
1889	if (error != 0 || (ap->a_runp == NULL && ap->a_runb == NULL))
1890		return (error);
1891
1892	mp = vp->v_mount;
1893	maxio = mp->mnt_iosize_max / mp->mnt_stat.f_iosize;
1894	bnpercn = de_cn2bn(pmp, 1);
1895	if (ap->a_runp != NULL) {
1896		maxrun = ulmin(maxio - 1, pmp->pm_maxcluster - cn);
1897		for (run = 1; run <= maxrun; run++) {
1898			if (pcbmap(dep, cn + run, &runbn, NULL, NULL) != 0 ||
1899			    runbn != *ap->a_bnp + run * bnpercn)
1900				break;
1901		}
1902		*ap->a_runp = run - 1;
1903	}
1904	if (ap->a_runb != NULL) {
1905		maxrun = ulmin(maxio - 1, cn);
1906		for (run = 1; run < maxrun; run++) {
1907			if (pcbmap(dep, cn - run, &runbn, NULL, NULL) != 0 ||
1908			    runbn != *ap->a_bnp - run * bnpercn)
1909				break;
1910		}
1911		*ap->a_runb = run - 1;
1912	}
1913	return (0);
1914}
1915
1916static int
1917msdosfs_strategy(ap)
1918	struct vop_strategy_args /* {
1919		struct vnode *a_vp;
1920		struct buf *a_bp;
1921	} */ *ap;
1922{
1923	struct buf *bp = ap->a_bp;
1924	struct denode *dep = VTODE(ap->a_vp);
1925	struct bufobj *bo;
1926	int error = 0;
1927	daddr_t blkno;
1928
1929	/*
1930	 * If we don't already know the filesystem relative block number
1931	 * then get it using pcbmap().  If pcbmap() returns the block
1932	 * number as -1 then we've got a hole in the file.  DOS filesystems
1933	 * don't allow files with holes, so we shouldn't ever see this.
1934	 */
1935	if (bp->b_blkno == bp->b_lblkno) {
1936		error = pcbmap(dep, bp->b_lblkno, &blkno, 0, 0);
1937		bp->b_blkno = blkno;
1938		if (error) {
1939			bp->b_error = error;
1940			bp->b_ioflags |= BIO_ERROR;
1941			bufdone(bp);
1942			return (0);
1943		}
1944		if ((long)bp->b_blkno == -1)
1945			vfs_bio_clrbuf(bp);
1946	}
1947	if (bp->b_blkno == -1) {
1948		bufdone(bp);
1949		return (0);
1950	}
1951	/*
1952	 * Read/write the block from/to the disk that contains the desired
1953	 * file block.
1954	 */
1955	bp->b_iooffset = dbtob(bp->b_blkno);
1956	bo = dep->de_pmp->pm_bo;
1957	BO_STRATEGY(bo, bp);
1958	return (0);
1959}
1960
1961static int
1962msdosfs_print(ap)
1963	struct vop_print_args /* {
1964		struct vnode *vp;
1965	} */ *ap;
1966{
1967	struct denode *dep = VTODE(ap->a_vp);
1968
1969	printf("\tstartcluster %lu, dircluster %lu, diroffset %lu, ",
1970	       dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1971	printf("on dev %s\n", devtoname(dep->de_pmp->pm_dev));
1972	return (0);
1973}
1974
1975static int
1976msdosfs_pathconf(ap)
1977	struct vop_pathconf_args /* {
1978		struct vnode *a_vp;
1979		int a_name;
1980		int *a_retval;
1981	} */ *ap;
1982{
1983	struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp;
1984
1985	switch (ap->a_name) {
1986	case _PC_LINK_MAX:
1987		*ap->a_retval = 1;
1988		return (0);
1989	case _PC_NAME_MAX:
1990		*ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12;
1991		return (0);
1992	case _PC_PATH_MAX:
1993		*ap->a_retval = PATH_MAX;
1994		return (0);
1995	case _PC_CHOWN_RESTRICTED:
1996		*ap->a_retval = 1;
1997		return (0);
1998	case _PC_NO_TRUNC:
1999		*ap->a_retval = 0;
2000		return (0);
2001	default:
2002		return (EINVAL);
2003	}
2004	/* NOTREACHED */
2005}
2006
2007static int
2008msdosfs_vptofh(ap)
2009	struct vop_vptofh_args /* {
2010		struct vnode *a_vp;
2011		struct fid *a_fhp;
2012	} */ *ap;
2013{
2014	struct denode *dep;
2015	struct defid *defhp;
2016
2017	dep = VTODE(ap->a_vp);
2018	defhp = (struct defid *)ap->a_fhp;
2019	defhp->defid_len = sizeof(struct defid);
2020	defhp->defid_dirclust = dep->de_dirclust;
2021	defhp->defid_dirofs = dep->de_diroffset;
2022	/* defhp->defid_gen = dep->de_gen; */
2023	return (0);
2024}
2025
2026/* Global vfs data structures for msdosfs */
2027struct vop_vector msdosfs_vnodeops = {
2028	.vop_default =		&default_vnodeops,
2029
2030	.vop_access =		msdosfs_access,
2031	.vop_bmap =		msdosfs_bmap,
2032	.vop_cachedlookup =	msdosfs_lookup,
2033	.vop_open =		msdosfs_open,
2034	.vop_close =		msdosfs_close,
2035	.vop_create =		msdosfs_create,
2036	.vop_fsync =		msdosfs_fsync,
2037	.vop_getattr =		msdosfs_getattr,
2038	.vop_inactive =		msdosfs_inactive,
2039	.vop_link =		msdosfs_link,
2040	.vop_lookup =		vfs_cache_lookup,
2041	.vop_mkdir =		msdosfs_mkdir,
2042	.vop_mknod =		msdosfs_mknod,
2043	.vop_pathconf =		msdosfs_pathconf,
2044	.vop_print =		msdosfs_print,
2045	.vop_read =		msdosfs_read,
2046	.vop_readdir =		msdosfs_readdir,
2047	.vop_reclaim =		msdosfs_reclaim,
2048	.vop_remove =		msdosfs_remove,
2049	.vop_rename =		msdosfs_rename,
2050	.vop_rmdir =		msdosfs_rmdir,
2051	.vop_setattr =		msdosfs_setattr,
2052	.vop_strategy =		msdosfs_strategy,
2053	.vop_symlink =		msdosfs_symlink,
2054	.vop_write =		msdosfs_write,
2055	.vop_vptofh =		msdosfs_vptofh,
2056};
2057