msdosfs_vnops.c revision 276648
1/* $FreeBSD: stable/10/sys/fs/msdosfs/msdosfs_vnops.c 276648 2015-01-04 00:46:06Z 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 = 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			vfs_bio_clrbuf(bp);
780			/*
781			 * Do the bmap now, since pcbmap needs buffers
782			 * for the fat table. (see msdosfs_strategy)
783			 */
784			if (bp->b_blkno == bp->b_lblkno) {
785				error = pcbmap(dep, bp->b_lblkno, &bn, 0, 0);
786				if (error)
787					bp->b_blkno = -1;
788				else
789					bp->b_blkno = bn;
790			}
791			if (bp->b_blkno == -1) {
792				brelse(bp);
793				if (!error)
794					error = EIO;		/* XXX */
795				break;
796			}
797		} else {
798			/*
799			 * The block we need to write into exists, so read it in.
800			 */
801			error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp);
802			if (error) {
803				brelse(bp);
804				break;
805			}
806		}
807
808		/*
809		 * Should these vnode_pager_* functions be done on dir
810		 * files?
811		 */
812
813		/*
814		 * Copy the data from user space into the buf header.
815		 */
816		error = uiomove(bp->b_data + croffset, n, uio);
817		if (error) {
818			brelse(bp);
819			break;
820		}
821
822		/* Prepare for clustered writes in some else clauses. */
823		if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
824			bp->b_flags |= B_CLUSTEROK;
825
826		/*
827		 * If IO_SYNC, then each buffer is written synchronously.
828		 * Otherwise, if we have a severe page deficiency then
829		 * write the buffer asynchronously.  Otherwise, if on a
830		 * cluster boundary then write the buffer asynchronously,
831		 * combining it with contiguous clusters if permitted and
832		 * possible, since we don't expect more writes into this
833		 * buffer soon.  Otherwise, do a delayed write because we
834		 * expect more writes into this buffer soon.
835		 */
836		if (ioflag & IO_SYNC)
837			(void)bwrite(bp);
838		else if (vm_page_count_severe() || buf_dirty_count_severe())
839			bawrite(bp);
840		else if (n + croffset == pmp->pm_bpcluster) {
841			if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
842				cluster_write(vp, bp, dep->de_FileSize,
843				    seqcount, 0);
844			else
845				bawrite(bp);
846		} else
847			bdwrite(bp);
848		dep->de_flag |= DE_UPDATE;
849	} while (error == 0 && uio->uio_resid > 0);
850
851	/*
852	 * If the write failed and they want us to, truncate the file back
853	 * to the size it was before the write was attempted.
854	 */
855errexit:
856	if (error) {
857		if (ioflag & IO_UNIT) {
858			detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
859			uio->uio_offset -= resid - uio->uio_resid;
860			uio->uio_resid = resid;
861		} else {
862			detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED);
863			if (uio->uio_resid != resid)
864				error = 0;
865		}
866	} else if (ioflag & IO_SYNC)
867		error = deupdat(dep, 1);
868	return (error);
869}
870
871/*
872 * Flush the blocks of a file to disk.
873 */
874static int
875msdosfs_fsync(ap)
876	struct vop_fsync_args /* {
877		struct vnode *a_vp;
878		struct ucred *a_cred;
879		int a_waitfor;
880		struct thread *a_td;
881	} */ *ap;
882{
883	struct vnode *devvp;
884	int allerror, error;
885
886	vop_stdfsync(ap);
887
888	/*
889	* If the syncing request comes from fsync(2), sync the entire
890	* FAT and any other metadata that happens to be on devvp.  We
891	* need this mainly for the FAT.  We write the FAT sloppily, and
892	* syncing it all now is the best we can easily do to get all
893	* directory entries associated with the file (not just the file)
894	* fully synced.  The other metadata includes critical metadata
895	* for all directory entries, but only in the MNT_ASYNC case.  We
896	* will soon sync all metadata in the file's directory entry.
897	* Non-critical metadata for associated directory entries only
898	* gets synced accidentally, as in most file systems.
899	*/
900	if (ap->a_waitfor == MNT_WAIT) {
901		devvp = VTODE(ap->a_vp)->de_pmp->pm_devvp;
902		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
903		allerror = VOP_FSYNC(devvp, MNT_WAIT, ap->a_td);
904		VOP_UNLOCK(devvp, 0);
905	} else
906		allerror = 0;
907
908	error = deupdat(VTODE(ap->a_vp), ap->a_waitfor == MNT_WAIT);
909	if (allerror == 0)
910		allerror = error;
911	return (allerror);
912}
913
914static int
915msdosfs_remove(ap)
916	struct vop_remove_args /* {
917		struct vnode *a_dvp;
918		struct vnode *a_vp;
919		struct componentname *a_cnp;
920	} */ *ap;
921{
922	struct denode *dep = VTODE(ap->a_vp);
923	struct denode *ddep = VTODE(ap->a_dvp);
924	int error;
925
926	if (ap->a_vp->v_type == VDIR)
927		error = EPERM;
928	else
929		error = removede(ddep, dep);
930#ifdef MSDOSFS_DEBUG
931	printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep, ap->a_vp->v_usecount);
932#endif
933	return (error);
934}
935
936/*
937 * DOS filesystems don't know what links are.
938 */
939static int
940msdosfs_link(ap)
941	struct vop_link_args /* {
942		struct vnode *a_tdvp;
943		struct vnode *a_vp;
944		struct componentname *a_cnp;
945	} */ *ap;
946{
947	return (EOPNOTSUPP);
948}
949
950/*
951 * Renames on files require moving the denode to a new hash queue since the
952 * denode's location is used to compute which hash queue to put the file
953 * in. Unless it is a rename in place.  For example "mv a b".
954 *
955 * What follows is the basic algorithm:
956 *
957 * if (file move) {
958 *	if (dest file exists) {
959 *		remove dest file
960 *	}
961 *	if (dest and src in same directory) {
962 *		rewrite name in existing directory slot
963 *	} else {
964 *		write new entry in dest directory
965 *		update offset and dirclust in denode
966 *		move denode to new hash chain
967 *		clear old directory entry
968 *	}
969 * } else {
970 *	directory move
971 *	if (dest directory exists) {
972 *		if (dest is not empty) {
973 *			return ENOTEMPTY
974 *		}
975 *		remove dest directory
976 *	}
977 *	if (dest and src in same directory) {
978 *		rewrite name in existing entry
979 *	} else {
980 *		be sure dest is not a child of src directory
981 *		write entry in dest directory
982 *		update "." and ".." in moved directory
983 *		clear old directory entry for moved directory
984 *	}
985 * }
986 *
987 * On entry:
988 *	source's parent directory is unlocked
989 *	source file or directory is unlocked
990 *	destination's parent directory is locked
991 *	destination file or directory is locked if it exists
992 *
993 * On exit:
994 *	all denodes should be released
995 */
996static int
997msdosfs_rename(ap)
998	struct vop_rename_args /* {
999		struct vnode *a_fdvp;
1000		struct vnode *a_fvp;
1001		struct componentname *a_fcnp;
1002		struct vnode *a_tdvp;
1003		struct vnode *a_tvp;
1004		struct componentname *a_tcnp;
1005	} */ *ap;
1006{
1007	struct vnode *tdvp = ap->a_tdvp;
1008	struct vnode *fvp = ap->a_fvp;
1009	struct vnode *fdvp = ap->a_fdvp;
1010	struct vnode *tvp = ap->a_tvp;
1011	struct componentname *tcnp = ap->a_tcnp;
1012	struct componentname *fcnp = ap->a_fcnp;
1013	struct denode *ip, *xp, *dp, *zp;
1014	u_char toname[12], oldname[11];
1015	u_long from_diroffset, to_diroffset;
1016	u_char to_count;
1017	int doingdirectory = 0, newparent = 0;
1018	int error;
1019	u_long cn, pcl;
1020	daddr_t bn;
1021	struct denode *fddep;	/* from file's parent directory	 */
1022	struct msdosfsmount *pmp;
1023	struct direntry *dotdotp;
1024	struct buf *bp;
1025
1026	fddep = VTODE(ap->a_fdvp);
1027	pmp = fddep->de_pmp;
1028
1029	pmp = VFSTOMSDOSFS(fdvp->v_mount);
1030
1031#ifdef DIAGNOSTIC
1032	if ((tcnp->cn_flags & HASBUF) == 0 ||
1033	    (fcnp->cn_flags & HASBUF) == 0)
1034		panic("msdosfs_rename: no name");
1035#endif
1036	/*
1037	 * Check for cross-device rename.
1038	 */
1039	if (fvp->v_mount != tdvp->v_mount ||
1040	    (tvp && fvp->v_mount != tvp->v_mount)) {
1041		error = EXDEV;
1042abortit:
1043		if (tdvp == tvp)
1044			vrele(tdvp);
1045		else
1046			vput(tdvp);
1047		if (tvp)
1048			vput(tvp);
1049		vrele(fdvp);
1050		vrele(fvp);
1051		return (error);
1052	}
1053
1054	/*
1055	 * If source and dest are the same, do nothing.
1056	 */
1057	if (tvp == fvp) {
1058		error = 0;
1059		goto abortit;
1060	}
1061
1062	error = vn_lock(fvp, LK_EXCLUSIVE);
1063	if (error)
1064		goto abortit;
1065	dp = VTODE(fdvp);
1066	ip = VTODE(fvp);
1067
1068	/*
1069	 * Be sure we are not renaming ".", "..", or an alias of ".". This
1070	 * leads to a crippled directory tree.  It's pretty tough to do a
1071	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
1072	 * doesn't work if the ".." entry is missing.
1073	 */
1074	if (ip->de_Attributes & ATTR_DIRECTORY) {
1075		/*
1076		 * Avoid ".", "..", and aliases of "." for obvious reasons.
1077		 */
1078		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
1079		    dp == ip ||
1080		    (fcnp->cn_flags & ISDOTDOT) ||
1081		    (tcnp->cn_flags & ISDOTDOT) ||
1082		    (ip->de_flag & DE_RENAME)) {
1083			VOP_UNLOCK(fvp, 0);
1084			error = EINVAL;
1085			goto abortit;
1086		}
1087		ip->de_flag |= DE_RENAME;
1088		doingdirectory++;
1089	}
1090
1091	/*
1092	 * When the target exists, both the directory
1093	 * and target vnodes are returned locked.
1094	 */
1095	dp = VTODE(tdvp);
1096	xp = tvp ? VTODE(tvp) : NULL;
1097	/*
1098	 * Remember direntry place to use for destination
1099	 */
1100	to_diroffset = dp->de_fndoffset;
1101	to_count = dp->de_fndcnt;
1102
1103	/*
1104	 * If ".." must be changed (ie the directory gets a new
1105	 * parent) then the source directory must not be in the
1106	 * directory hierarchy above the target, as this would
1107	 * orphan everything below the source directory. Also
1108	 * the user must have write permission in the source so
1109	 * as to be able to change "..". We must repeat the call
1110	 * to namei, as the parent directory is unlocked by the
1111	 * call to doscheckpath().
1112	 */
1113	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
1114	VOP_UNLOCK(fvp, 0);
1115	if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
1116		newparent = 1;
1117	if (doingdirectory && newparent) {
1118		if (error)	/* write access check above */
1119			goto bad;
1120		if (xp != NULL)
1121			vput(tvp);
1122		/*
1123		 * doscheckpath() vput()'s dp,
1124		 * so we have to do a relookup afterwards
1125		 */
1126		error = doscheckpath(ip, dp);
1127		if (error)
1128			goto out;
1129		if ((tcnp->cn_flags & SAVESTART) == 0)
1130			panic("msdosfs_rename: lost to startdir");
1131		error = relookup(tdvp, &tvp, tcnp);
1132		if (error)
1133			goto out;
1134		dp = VTODE(tdvp);
1135		xp = tvp ? VTODE(tvp) : NULL;
1136	}
1137
1138	if (xp != NULL) {
1139		/*
1140		 * Target must be empty if a directory and have no links
1141		 * to it. Also, ensure source and target are compatible
1142		 * (both directories, or both not directories).
1143		 */
1144		if (xp->de_Attributes & ATTR_DIRECTORY) {
1145			if (!dosdirempty(xp)) {
1146				error = ENOTEMPTY;
1147				goto bad;
1148			}
1149			if (!doingdirectory) {
1150				error = ENOTDIR;
1151				goto bad;
1152			}
1153			cache_purge(tdvp);
1154		} else if (doingdirectory) {
1155			error = EISDIR;
1156			goto bad;
1157		}
1158		error = removede(dp, xp);
1159		if (error)
1160			goto bad;
1161		vput(tvp);
1162		xp = NULL;
1163	}
1164
1165	/*
1166	 * Convert the filename in tcnp into a dos filename. We copy this
1167	 * into the denode and directory entry for the destination
1168	 * file/directory.
1169	 */
1170	error = uniqdosname(VTODE(tdvp), tcnp, toname);
1171	if (error)
1172		goto abortit;
1173
1174	/*
1175	 * Since from wasn't locked at various places above,
1176	 * have to do a relookup here.
1177	 */
1178	fcnp->cn_flags &= ~MODMASK;
1179	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
1180	if ((fcnp->cn_flags & SAVESTART) == 0)
1181		panic("msdosfs_rename: lost from startdir");
1182	if (!newparent)
1183		VOP_UNLOCK(tdvp, 0);
1184	if (relookup(fdvp, &fvp, fcnp) == 0)
1185		vrele(fdvp);
1186	if (fvp == NULL) {
1187		/*
1188		 * From name has disappeared.
1189		 */
1190		if (doingdirectory)
1191			panic("rename: lost dir entry");
1192		if (newparent)
1193			VOP_UNLOCK(tdvp, 0);
1194		vrele(tdvp);
1195		vrele(ap->a_fvp);
1196		return 0;
1197	}
1198	xp = VTODE(fvp);
1199	zp = VTODE(fdvp);
1200	from_diroffset = zp->de_fndoffset;
1201
1202	/*
1203	 * Ensure that the directory entry still exists and has not
1204	 * changed till now. If the source is a file the entry may
1205	 * have been unlinked or renamed. In either case there is
1206	 * no further work to be done. If the source is a directory
1207	 * then it cannot have been rmdir'ed or renamed; this is
1208	 * prohibited by the DE_RENAME flag.
1209	 */
1210	if (xp != ip) {
1211		if (doingdirectory)
1212			panic("rename: lost dir entry");
1213		VOP_UNLOCK(fvp, 0);
1214		if (newparent)
1215			VOP_UNLOCK(fdvp, 0);
1216		vrele(ap->a_fvp);
1217		xp = NULL;
1218	} else {
1219		vrele(fvp);
1220		xp = NULL;
1221
1222		/*
1223		 * First write a new entry in the destination
1224		 * directory and mark the entry in the source directory
1225		 * as deleted.  Then move the denode to the correct hash
1226		 * chain for its new location in the filesystem.  And, if
1227		 * we moved a directory, then update its .. entry to point
1228		 * to the new parent directory.
1229		 */
1230		bcopy(ip->de_Name, oldname, 11);
1231		bcopy(toname, ip->de_Name, 11);	/* update denode */
1232		dp->de_fndoffset = to_diroffset;
1233		dp->de_fndcnt = to_count;
1234		error = createde(ip, dp, (struct denode **)0, tcnp);
1235		if (error) {
1236			bcopy(oldname, ip->de_Name, 11);
1237			if (newparent)
1238				VOP_UNLOCK(fdvp, 0);
1239			VOP_UNLOCK(fvp, 0);
1240			goto bad;
1241		}
1242		/*
1243		 * If ip is for a directory, then its name should always
1244		 * be "." since it is for the directory entry in the
1245		 * directory itself (msdosfs_lookup() always translates
1246		 * to the "." entry so as to get a unique denode, except
1247		 * for the root directory there are different
1248		 * complications).  However, we just corrupted its name
1249		 * to pass the correct name to createde().  Undo this.
1250		 */
1251		if ((ip->de_Attributes & ATTR_DIRECTORY) != 0)
1252			bcopy(oldname, ip->de_Name, 11);
1253		ip->de_refcnt++;
1254		zp->de_fndoffset = from_diroffset;
1255		error = removede(zp, ip);
1256		if (error) {
1257			/* XXX should downgrade to ro here, fs is corrupt */
1258			if (newparent)
1259				VOP_UNLOCK(fdvp, 0);
1260			VOP_UNLOCK(fvp, 0);
1261			goto bad;
1262		}
1263		if (!doingdirectory) {
1264			error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
1265				       &ip->de_dirclust, 0);
1266			if (error) {
1267				/* XXX should downgrade to ro here, fs is corrupt */
1268				if (newparent)
1269					VOP_UNLOCK(fdvp, 0);
1270				VOP_UNLOCK(fvp, 0);
1271				goto bad;
1272			}
1273			if (ip->de_dirclust == MSDOSFSROOT)
1274				ip->de_diroffset = to_diroffset;
1275			else
1276				ip->de_diroffset = to_diroffset & pmp->pm_crbomask;
1277		}
1278		reinsert(ip);
1279		if (newparent)
1280			VOP_UNLOCK(fdvp, 0);
1281	}
1282
1283	/*
1284	 * If we moved a directory to a new parent directory, then we must
1285	 * fixup the ".." entry in the moved directory.
1286	 */
1287	if (doingdirectory && newparent) {
1288		cn = ip->de_StartCluster;
1289		if (cn == MSDOSFSROOT) {
1290			/* this should never happen */
1291			panic("msdosfs_rename(): updating .. in root directory?");
1292		} else
1293			bn = cntobn(pmp, cn);
1294		error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
1295			      NOCRED, &bp);
1296		if (error) {
1297			/* XXX should downgrade to ro here, fs is corrupt */
1298			brelse(bp);
1299			VOP_UNLOCK(fvp, 0);
1300			goto bad;
1301		}
1302		dotdotp = (struct direntry *)bp->b_data + 1;
1303		pcl = dp->de_StartCluster;
1304		if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1305			pcl = MSDOSFSROOT;
1306		putushort(dotdotp->deStartCluster, pcl);
1307		if (FAT32(pmp))
1308			putushort(dotdotp->deHighClust, pcl >> 16);
1309		if (DOINGASYNC(fvp))
1310			bdwrite(bp);
1311		else if ((error = bwrite(bp)) != 0) {
1312			/* XXX should downgrade to ro here, fs is corrupt */
1313			VOP_UNLOCK(fvp, 0);
1314			goto bad;
1315		}
1316	}
1317
1318	/*
1319	 * The msdosfs lookup is case insensitive. Several aliases may
1320	 * be inserted for a single directory entry. As a consequnce,
1321	 * name cache purge done by lookup for fvp when DELETE op for
1322	 * namei is specified, might be not enough to expunge all
1323	 * namecache entries that were installed for this direntry.
1324	 */
1325	cache_purge(fvp);
1326	VOP_UNLOCK(fvp, 0);
1327bad:
1328	if (xp)
1329		vput(tvp);
1330	vput(tdvp);
1331out:
1332	ip->de_flag &= ~DE_RENAME;
1333	vrele(fdvp);
1334	vrele(fvp);
1335	return (error);
1336
1337}
1338
1339static struct {
1340	struct direntry dot;
1341	struct direntry dotdot;
1342} dosdirtemplate = {
1343	{	".          ",				/* the . entry */
1344		ATTR_DIRECTORY,				/* file attribute */
1345		0,					/* reserved */
1346		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1347		{ 0, 0 },				/* access date */
1348		{ 0, 0 },				/* high bits of start cluster */
1349		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1350		{ 0, 0 },				/* startcluster */
1351		{ 0, 0, 0, 0 }				/* filesize */
1352	},
1353	{	"..         ",				/* the .. entry */
1354		ATTR_DIRECTORY,				/* file attribute */
1355		0,					/* reserved */
1356		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1357		{ 0, 0 },				/* access date */
1358		{ 0, 0 },				/* high bits of start cluster */
1359		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1360		{ 0, 0 },				/* startcluster */
1361		{ 0, 0, 0, 0 }				/* filesize */
1362	}
1363};
1364
1365static int
1366msdosfs_mkdir(ap)
1367	struct vop_mkdir_args /* {
1368		struct vnode *a_dvp;
1369		struct vnode **a_vpp;
1370		struvt componentname *a_cnp;
1371		struct vattr *a_vap;
1372	} */ *ap;
1373{
1374	struct componentname *cnp = ap->a_cnp;
1375	struct denode *dep;
1376	struct denode *pdep = VTODE(ap->a_dvp);
1377	struct direntry *denp;
1378	struct msdosfsmount *pmp = pdep->de_pmp;
1379	struct buf *bp;
1380	u_long newcluster, pcl;
1381	int bn;
1382	int error;
1383	struct denode ndirent;
1384	struct timespec ts;
1385
1386	/*
1387	 * If this is the root directory and there is no space left we
1388	 * can't do anything.  This is because the root directory can not
1389	 * change size.
1390	 */
1391	if (pdep->de_StartCluster == MSDOSFSROOT
1392	    && pdep->de_fndoffset >= pdep->de_FileSize) {
1393		error = ENOSPC;
1394		goto bad2;
1395	}
1396
1397	/*
1398	 * Allocate a cluster to hold the about to be created directory.
1399	 */
1400	error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
1401	if (error)
1402		goto bad2;
1403
1404	bzero(&ndirent, sizeof(ndirent));
1405	ndirent.de_pmp = pmp;
1406	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
1407	getnanotime(&ts);
1408	DETIMES(&ndirent, &ts, &ts, &ts);
1409
1410	/*
1411	 * Now fill the cluster with the "." and ".." entries. And write
1412	 * the cluster to disk.  This way it is there for the parent
1413	 * directory to be pointing at if there were a crash.
1414	 */
1415	bn = cntobn(pmp, newcluster);
1416	/* always succeeds */
1417	bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0);
1418	bzero(bp->b_data, pmp->pm_bpcluster);
1419	bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
1420	denp = (struct direntry *)bp->b_data;
1421	putushort(denp[0].deStartCluster, newcluster);
1422	putushort(denp[0].deCDate, ndirent.de_CDate);
1423	putushort(denp[0].deCTime, ndirent.de_CTime);
1424	denp[0].deCHundredth = ndirent.de_CHun;
1425	putushort(denp[0].deADate, ndirent.de_ADate);
1426	putushort(denp[0].deMDate, ndirent.de_MDate);
1427	putushort(denp[0].deMTime, ndirent.de_MTime);
1428	pcl = pdep->de_StartCluster;
1429	/*
1430	 * Although the root directory has a non-magic starting cluster
1431	 * number for FAT32, chkdsk and fsck_msdosfs still require
1432	 * references to it in dotdot entries to be magic.
1433	 */
1434	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1435		pcl = MSDOSFSROOT;
1436	putushort(denp[1].deStartCluster, pcl);
1437	putushort(denp[1].deCDate, ndirent.de_CDate);
1438	putushort(denp[1].deCTime, ndirent.de_CTime);
1439	denp[1].deCHundredth = ndirent.de_CHun;
1440	putushort(denp[1].deADate, ndirent.de_ADate);
1441	putushort(denp[1].deMDate, ndirent.de_MDate);
1442	putushort(denp[1].deMTime, ndirent.de_MTime);
1443	if (FAT32(pmp)) {
1444		putushort(denp[0].deHighClust, newcluster >> 16);
1445		putushort(denp[1].deHighClust, pcl >> 16);
1446	}
1447
1448	if (DOINGASYNC(ap->a_dvp))
1449		bdwrite(bp);
1450	else if ((error = bwrite(bp)) != 0)
1451		goto bad;
1452
1453	/*
1454	 * Now build up a directory entry pointing to the newly allocated
1455	 * cluster.  This will be written to an empty slot in the parent
1456	 * directory.
1457	 */
1458#ifdef DIAGNOSTIC
1459	if ((cnp->cn_flags & HASBUF) == 0)
1460		panic("msdosfs_mkdir: no name");
1461#endif
1462	error = uniqdosname(pdep, cnp, ndirent.de_Name);
1463	if (error)
1464		goto bad;
1465
1466	ndirent.de_Attributes = ATTR_DIRECTORY;
1467	ndirent.de_LowerCase = 0;
1468	ndirent.de_StartCluster = newcluster;
1469	ndirent.de_FileSize = 0;
1470	error = createde(&ndirent, pdep, &dep, cnp);
1471	if (error)
1472		goto bad;
1473	*ap->a_vpp = DETOV(dep);
1474	return (0);
1475
1476bad:
1477	clusterfree(pmp, newcluster, NULL);
1478bad2:
1479	return (error);
1480}
1481
1482static int
1483msdosfs_rmdir(ap)
1484	struct vop_rmdir_args /* {
1485		struct vnode *a_dvp;
1486		struct vnode *a_vp;
1487		struct componentname *a_cnp;
1488	} */ *ap;
1489{
1490	struct vnode *vp = ap->a_vp;
1491	struct vnode *dvp = ap->a_dvp;
1492	struct componentname *cnp = ap->a_cnp;
1493	struct denode *ip, *dp;
1494	int error;
1495
1496	ip = VTODE(vp);
1497	dp = VTODE(dvp);
1498
1499	/*
1500	 * Verify the directory is empty (and valid).
1501	 * (Rmdir ".." won't be valid since
1502	 *  ".." will contain a reference to
1503	 *  the current directory and thus be
1504	 *  non-empty.)
1505	 */
1506	error = 0;
1507	if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
1508		error = ENOTEMPTY;
1509		goto out;
1510	}
1511	/*
1512	 * Delete the entry from the directory.  For dos filesystems this
1513	 * gets rid of the directory entry on disk, the in memory copy
1514	 * still exists but the de_refcnt is <= 0.  This prevents it from
1515	 * being found by deget().  When the vput() on dep is done we give
1516	 * up access and eventually msdosfs_reclaim() will be called which
1517	 * will remove it from the denode cache.
1518	 */
1519	error = removede(dp, ip);
1520	if (error)
1521		goto out;
1522	/*
1523	 * This is where we decrement the link count in the parent
1524	 * directory.  Since dos filesystems don't do this we just purge
1525	 * the name cache.
1526	 */
1527	cache_purge(dvp);
1528	/*
1529	 * Truncate the directory that is being deleted.
1530	 */
1531	error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
1532	cache_purge(vp);
1533
1534out:
1535	return (error);
1536}
1537
1538/*
1539 * DOS filesystems don't know what symlinks are.
1540 */
1541static int
1542msdosfs_symlink(ap)
1543	struct vop_symlink_args /* {
1544		struct vnode *a_dvp;
1545		struct vnode **a_vpp;
1546		struct componentname *a_cnp;
1547		struct vattr *a_vap;
1548		char *a_target;
1549	} */ *ap;
1550{
1551	return (EOPNOTSUPP);
1552}
1553
1554static int
1555msdosfs_readdir(ap)
1556	struct vop_readdir_args /* {
1557		struct vnode *a_vp;
1558		struct uio *a_uio;
1559		struct ucred *a_cred;
1560		int *a_eofflag;
1561		int *a_ncookies;
1562		u_long **a_cookies;
1563	} */ *ap;
1564{
1565	struct mbnambuf nb;
1566	int error = 0;
1567	int diff;
1568	long n;
1569	int blsize;
1570	long on;
1571	u_long cn;
1572	uint64_t fileno;
1573	u_long dirsperblk;
1574	long bias = 0;
1575	daddr_t bn, lbn;
1576	struct buf *bp;
1577	struct denode *dep = VTODE(ap->a_vp);
1578	struct msdosfsmount *pmp = dep->de_pmp;
1579	struct direntry *dentp;
1580	struct dirent dirbuf;
1581	struct uio *uio = ap->a_uio;
1582	u_long *cookies = NULL;
1583	int ncookies = 0;
1584	off_t offset, off;
1585	int chksum = -1;
1586
1587#ifdef MSDOSFS_DEBUG
1588	printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
1589	    ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
1590#endif
1591
1592	/*
1593	 * msdosfs_readdir() won't operate properly on regular files since
1594	 * it does i/o only with the filesystem vnode, and hence can
1595	 * retrieve the wrong block from the buffer cache for a plain file.
1596	 * So, fail attempts to readdir() on a plain file.
1597	 */
1598	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1599		return (ENOTDIR);
1600
1601	/*
1602	 * To be safe, initialize dirbuf
1603	 */
1604	bzero(dirbuf.d_name, sizeof(dirbuf.d_name));
1605
1606	/*
1607	 * If the user buffer is smaller than the size of one dos directory
1608	 * entry or the file offset is not a multiple of the size of a
1609	 * directory entry, then we fail the read.
1610	 */
1611	off = offset = uio->uio_offset;
1612	if (uio->uio_resid < sizeof(struct direntry) ||
1613	    (offset & (sizeof(struct direntry) - 1)))
1614		return (EINVAL);
1615
1616	if (ap->a_ncookies) {
1617		ncookies = uio->uio_resid / 16;
1618		cookies = malloc(ncookies * sizeof(u_long), M_TEMP,
1619		       M_WAITOK);
1620		*ap->a_cookies = cookies;
1621		*ap->a_ncookies = ncookies;
1622	}
1623
1624	dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
1625
1626	/*
1627	 * If they are reading from the root directory then, we simulate
1628	 * the . and .. entries since these don't exist in the root
1629	 * directory.  We also set the offset bias to make up for having to
1630	 * simulate these entries. By this I mean that at file offset 64 we
1631	 * read the first entry in the root directory that lives on disk.
1632	 */
1633	if (dep->de_StartCluster == MSDOSFSROOT
1634	    || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
1635#if 0
1636		printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
1637		    offset);
1638#endif
1639		bias = 2 * sizeof(struct direntry);
1640		if (offset < bias) {
1641			for (n = (int)offset / sizeof(struct direntry);
1642			     n < 2; n++) {
1643				if (FAT32(pmp))
1644					fileno = (uint64_t)cntobn(pmp,
1645								 pmp->pm_rootdirblk)
1646							  * dirsperblk;
1647				else
1648					fileno = 1;
1649				if (pmp->pm_flags & MSDOSFS_LARGEFS) {
1650					dirbuf.d_fileno =
1651					    msdosfs_fileno_map(pmp->pm_mountp,
1652					    fileno);
1653				} else {
1654
1655					dirbuf.d_fileno = (uint32_t)fileno;
1656				}
1657				dirbuf.d_type = DT_DIR;
1658				switch (n) {
1659				case 0:
1660					dirbuf.d_namlen = 1;
1661					strcpy(dirbuf.d_name, ".");
1662					break;
1663				case 1:
1664					dirbuf.d_namlen = 2;
1665					strcpy(dirbuf.d_name, "..");
1666					break;
1667				}
1668				dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
1669				if (uio->uio_resid < dirbuf.d_reclen)
1670					goto out;
1671				error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1672				if (error)
1673					goto out;
1674				offset += sizeof(struct direntry);
1675				off = offset;
1676				if (cookies) {
1677					*cookies++ = offset;
1678					if (--ncookies <= 0)
1679						goto out;
1680				}
1681			}
1682		}
1683	}
1684
1685	mbnambuf_init(&nb);
1686	off = offset;
1687	while (uio->uio_resid > 0) {
1688		lbn = de_cluster(pmp, offset - bias);
1689		on = (offset - bias) & pmp->pm_crbomask;
1690		n = min(pmp->pm_bpcluster - on, uio->uio_resid);
1691		diff = dep->de_FileSize - (offset - bias);
1692		if (diff <= 0)
1693			break;
1694		n = min(n, diff);
1695		error = pcbmap(dep, lbn, &bn, &cn, &blsize);
1696		if (error)
1697			break;
1698		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
1699		if (error) {
1700			brelse(bp);
1701			return (error);
1702		}
1703		n = min(n, blsize - bp->b_resid);
1704		if (n == 0) {
1705			brelse(bp);
1706			return (EIO);
1707		}
1708
1709		/*
1710		 * Convert from dos directory entries to fs-independent
1711		 * directory entries.
1712		 */
1713		for (dentp = (struct direntry *)(bp->b_data + on);
1714		     (char *)dentp < bp->b_data + on + n;
1715		     dentp++, offset += sizeof(struct direntry)) {
1716#if 0
1717			printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1718			    dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1719#endif
1720			/*
1721			 * If this is an unused entry, we can stop.
1722			 */
1723			if (dentp->deName[0] == SLOT_EMPTY) {
1724				brelse(bp);
1725				goto out;
1726			}
1727			/*
1728			 * Skip deleted entries.
1729			 */
1730			if (dentp->deName[0] == SLOT_DELETED) {
1731				chksum = -1;
1732				mbnambuf_init(&nb);
1733				continue;
1734			}
1735
1736			/*
1737			 * Handle Win95 long directory entries
1738			 */
1739			if (dentp->deAttributes == ATTR_WIN95) {
1740				if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
1741					continue;
1742				chksum = win2unixfn(&nb,
1743				    (struct winentry *)dentp, chksum, pmp);
1744				continue;
1745			}
1746
1747			/*
1748			 * Skip volume labels
1749			 */
1750			if (dentp->deAttributes & ATTR_VOLUME) {
1751				chksum = -1;
1752				mbnambuf_init(&nb);
1753				continue;
1754			}
1755			/*
1756			 * This computation of d_fileno must match
1757			 * the computation of va_fileid in
1758			 * msdosfs_getattr.
1759			 */
1760			if (dentp->deAttributes & ATTR_DIRECTORY) {
1761				fileno = getushort(dentp->deStartCluster);
1762				if (FAT32(pmp))
1763					fileno |= getushort(dentp->deHighClust) << 16;
1764				/* if this is the root directory */
1765				if (fileno == MSDOSFSROOT)
1766					if (FAT32(pmp))
1767						fileno = (uint64_t)cntobn(pmp,
1768								pmp->pm_rootdirblk)
1769							 * dirsperblk;
1770					else
1771						fileno = 1;
1772				else
1773					fileno = (uint64_t)cntobn(pmp, fileno) *
1774					    dirsperblk;
1775				dirbuf.d_type = DT_DIR;
1776			} else {
1777				fileno = (uoff_t)offset /
1778				    sizeof(struct direntry);
1779				dirbuf.d_type = DT_REG;
1780			}
1781			if (pmp->pm_flags & MSDOSFS_LARGEFS) {
1782				dirbuf.d_fileno =
1783				    msdosfs_fileno_map(pmp->pm_mountp, fileno);
1784			} else
1785				dirbuf.d_fileno = (uint32_t)fileno;
1786
1787			if (chksum != winChksum(dentp->deName)) {
1788				dirbuf.d_namlen = dos2unixfn(dentp->deName,
1789				    (u_char *)dirbuf.d_name,
1790				    dentp->deLowerCase |
1791					((pmp->pm_flags & MSDOSFSMNT_SHORTNAME) ?
1792					(LCASE_BASE | LCASE_EXT) : 0),
1793				    pmp);
1794				mbnambuf_init(&nb);
1795			} else
1796				mbnambuf_flush(&nb, &dirbuf);
1797			chksum = -1;
1798			dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
1799			if (uio->uio_resid < dirbuf.d_reclen) {
1800				brelse(bp);
1801				goto out;
1802			}
1803			error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1804			if (error) {
1805				brelse(bp);
1806				goto out;
1807			}
1808			if (cookies) {
1809				*cookies++ = offset + sizeof(struct direntry);
1810				if (--ncookies <= 0) {
1811					brelse(bp);
1812					goto out;
1813				}
1814			}
1815			off = offset + sizeof(struct direntry);
1816		}
1817		brelse(bp);
1818	}
1819out:
1820	/* Subtract unused cookies */
1821	if (ap->a_ncookies)
1822		*ap->a_ncookies -= ncookies;
1823
1824	uio->uio_offset = off;
1825
1826	/*
1827	 * Set the eofflag (NFS uses it)
1828	 */
1829	if (ap->a_eofflag) {
1830		if (dep->de_FileSize - (offset - bias) <= 0)
1831			*ap->a_eofflag = 1;
1832		else
1833			*ap->a_eofflag = 0;
1834	}
1835	return (error);
1836}
1837
1838/*-
1839 * a_vp   - pointer to the file's vnode
1840 * a_bn   - logical block number within the file (cluster number for us)
1841 * a_bop  - where to return the bufobj of the special file containing the fs
1842 * a_bnp  - where to return the "physical" block number corresponding to a_bn
1843 *          (relative to the special file; units are blocks of size DEV_BSIZE)
1844 * a_runp - where to return the "run past" a_bn.  This is the count of logical
1845 *          blocks whose physical blocks (together with a_bn's physical block)
1846 *          are contiguous.
1847 * a_runb - where to return the "run before" a_bn.
1848 */
1849static int
1850msdosfs_bmap(ap)
1851	struct vop_bmap_args /* {
1852		struct vnode *a_vp;
1853		daddr_t a_bn;
1854		struct bufobj **a_bop;
1855		daddr_t *a_bnp;
1856		int *a_runp;
1857		int *a_runb;
1858	} */ *ap;
1859{
1860	struct denode *dep;
1861	struct mount *mp;
1862	struct msdosfsmount *pmp;
1863	struct vnode *vp;
1864	daddr_t runbn;
1865	u_long cn;
1866	int bnpercn, error, maxio, maxrun, run;
1867
1868	vp = ap->a_vp;
1869	dep = VTODE(vp);
1870	pmp = dep->de_pmp;
1871	if (ap->a_bop != NULL)
1872		*ap->a_bop = &pmp->pm_devvp->v_bufobj;
1873	if (ap->a_bnp == NULL)
1874		return (0);
1875	if (ap->a_runp != NULL)
1876		*ap->a_runp = 0;
1877	if (ap->a_runb != NULL)
1878		*ap->a_runb = 0;
1879	cn = ap->a_bn;
1880	if (cn != ap->a_bn)
1881		return (EFBIG);
1882	error = pcbmap(dep, cn, ap->a_bnp, NULL, NULL);
1883	if (error != 0 || (ap->a_runp == NULL && ap->a_runb == NULL))
1884		return (error);
1885
1886	mp = vp->v_mount;
1887	maxio = mp->mnt_iosize_max / mp->mnt_stat.f_iosize;
1888	bnpercn = de_cn2bn(pmp, 1);
1889	if (ap->a_runp != NULL) {
1890		maxrun = ulmin(maxio - 1, pmp->pm_maxcluster - cn);
1891		for (run = 1; run <= maxrun; run++) {
1892			if (pcbmap(dep, cn + run, &runbn, NULL, NULL) != 0 ||
1893			    runbn != *ap->a_bnp + run * bnpercn)
1894				break;
1895		}
1896		*ap->a_runp = run - 1;
1897	}
1898	if (ap->a_runb != NULL) {
1899		maxrun = ulmin(maxio - 1, cn);
1900		for (run = 1; run < maxrun; run++) {
1901			if (pcbmap(dep, cn - run, &runbn, NULL, NULL) != 0 ||
1902			    runbn != *ap->a_bnp - run * bnpercn)
1903				break;
1904		}
1905		*ap->a_runb = run - 1;
1906	}
1907	return (0);
1908}
1909
1910static int
1911msdosfs_strategy(ap)
1912	struct vop_strategy_args /* {
1913		struct vnode *a_vp;
1914		struct buf *a_bp;
1915	} */ *ap;
1916{
1917	struct buf *bp = ap->a_bp;
1918	struct denode *dep = VTODE(ap->a_vp);
1919	struct bufobj *bo;
1920	int error = 0;
1921	daddr_t blkno;
1922
1923	/*
1924	 * If we don't already know the filesystem relative block number
1925	 * then get it using pcbmap().  If pcbmap() returns the block
1926	 * number as -1 then we've got a hole in the file.  DOS filesystems
1927	 * don't allow files with holes, so we shouldn't ever see this.
1928	 */
1929	if (bp->b_blkno == bp->b_lblkno) {
1930		error = pcbmap(dep, bp->b_lblkno, &blkno, 0, 0);
1931		bp->b_blkno = blkno;
1932		if (error) {
1933			bp->b_error = error;
1934			bp->b_ioflags |= BIO_ERROR;
1935			bufdone(bp);
1936			return (0);
1937		}
1938		if ((long)bp->b_blkno == -1)
1939			vfs_bio_clrbuf(bp);
1940	}
1941	if (bp->b_blkno == -1) {
1942		bufdone(bp);
1943		return (0);
1944	}
1945	/*
1946	 * Read/write the block from/to the disk that contains the desired
1947	 * file block.
1948	 */
1949	bp->b_iooffset = dbtob(bp->b_blkno);
1950	bo = dep->de_pmp->pm_bo;
1951	BO_STRATEGY(bo, bp);
1952	return (0);
1953}
1954
1955static int
1956msdosfs_print(ap)
1957	struct vop_print_args /* {
1958		struct vnode *vp;
1959	} */ *ap;
1960{
1961	struct denode *dep = VTODE(ap->a_vp);
1962
1963	printf("\tstartcluster %lu, dircluster %lu, diroffset %lu, ",
1964	       dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1965	printf("on dev %s\n", devtoname(dep->de_pmp->pm_dev));
1966	return (0);
1967}
1968
1969static int
1970msdosfs_pathconf(ap)
1971	struct vop_pathconf_args /* {
1972		struct vnode *a_vp;
1973		int a_name;
1974		int *a_retval;
1975	} */ *ap;
1976{
1977	struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp;
1978
1979	switch (ap->a_name) {
1980	case _PC_LINK_MAX:
1981		*ap->a_retval = 1;
1982		return (0);
1983	case _PC_NAME_MAX:
1984		*ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12;
1985		return (0);
1986	case _PC_PATH_MAX:
1987		*ap->a_retval = PATH_MAX;
1988		return (0);
1989	case _PC_CHOWN_RESTRICTED:
1990		*ap->a_retval = 1;
1991		return (0);
1992	case _PC_NO_TRUNC:
1993		*ap->a_retval = 0;
1994		return (0);
1995	default:
1996		return (EINVAL);
1997	}
1998	/* NOTREACHED */
1999}
2000
2001static int
2002msdosfs_vptofh(ap)
2003	struct vop_vptofh_args /* {
2004		struct vnode *a_vp;
2005		struct fid *a_fhp;
2006	} */ *ap;
2007{
2008	struct denode *dep;
2009	struct defid *defhp;
2010
2011	dep = VTODE(ap->a_vp);
2012	defhp = (struct defid *)ap->a_fhp;
2013	defhp->defid_len = sizeof(struct defid);
2014	defhp->defid_dirclust = dep->de_dirclust;
2015	defhp->defid_dirofs = dep->de_diroffset;
2016	/* defhp->defid_gen = dep->de_gen; */
2017	return (0);
2018}
2019
2020/* Global vfs data structures for msdosfs */
2021struct vop_vector msdosfs_vnodeops = {
2022	.vop_default =		&default_vnodeops,
2023
2024	.vop_access =		msdosfs_access,
2025	.vop_bmap =		msdosfs_bmap,
2026	.vop_cachedlookup =	msdosfs_lookup,
2027	.vop_open =		msdosfs_open,
2028	.vop_close =		msdosfs_close,
2029	.vop_create =		msdosfs_create,
2030	.vop_fsync =		msdosfs_fsync,
2031	.vop_getattr =		msdosfs_getattr,
2032	.vop_inactive =		msdosfs_inactive,
2033	.vop_link =		msdosfs_link,
2034	.vop_lookup =		vfs_cache_lookup,
2035	.vop_mkdir =		msdosfs_mkdir,
2036	.vop_mknod =		msdosfs_mknod,
2037	.vop_pathconf =		msdosfs_pathconf,
2038	.vop_print =		msdosfs_print,
2039	.vop_read =		msdosfs_read,
2040	.vop_readdir =		msdosfs_readdir,
2041	.vop_reclaim =		msdosfs_reclaim,
2042	.vop_remove =		msdosfs_remove,
2043	.vop_rename =		msdosfs_rename,
2044	.vop_rmdir =		msdosfs_rmdir,
2045	.vop_setattr =		msdosfs_setattr,
2046	.vop_strategy =		msdosfs_strategy,
2047	.vop_symlink =		msdosfs_symlink,
2048	.vop_write =		msdosfs_write,
2049	.vop_vptofh =		msdosfs_vptofh,
2050};
2051