xref: /openbsd-src/sys/msdosfs/msdosfs_vnops.c (revision 5a0ec8146b3a8f74af8f596985d293fb896d1dcb)
1*5a0ec814Smiod /*	$OpenBSD: msdosfs_vnops.c,v 1.143 2024/10/18 05:52:32 miod Exp $	*/
2b099d67bSprovos /*	$NetBSD: msdosfs_vnops.c,v 1.63 1997/10/17 11:24:19 ws Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*-
5979947e6Stom  * Copyright (C) 2005 Thomas Wang.
6b099d67bSprovos  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
7b099d67bSprovos  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
8df930be7Sderaadt  * All rights reserved.
9df930be7Sderaadt  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
10df930be7Sderaadt  *
11df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt  * modification, are permitted provided that the following conditions
13df930be7Sderaadt  * are met:
14df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
19df930be7Sderaadt  * 3. All advertising materials mentioning features or use of this software
20df930be7Sderaadt  *    must display the following acknowledgement:
21df930be7Sderaadt  *	This product includes software developed by TooLs GmbH.
22df930be7Sderaadt  * 4. The name of TooLs GmbH may not be used to endorse or promote products
23df930be7Sderaadt  *    derived from this software without specific prior written permission.
24df930be7Sderaadt  *
25df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
26df930be7Sderaadt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27df930be7Sderaadt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28df930be7Sderaadt  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29df930be7Sderaadt  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30df930be7Sderaadt  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
31df930be7Sderaadt  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32df930be7Sderaadt  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
33df930be7Sderaadt  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
34df930be7Sderaadt  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35df930be7Sderaadt  */
36df930be7Sderaadt /*
37df930be7Sderaadt  * Written by Paul Popelka (paulp@uts.amdahl.com)
38df930be7Sderaadt  *
39df930be7Sderaadt  * You can do anything you want with this software, just don't say you wrote
40df930be7Sderaadt  * it, and don't remove this notice.
41df930be7Sderaadt  *
42df930be7Sderaadt  * This software is provided "as is".
43df930be7Sderaadt  *
44df930be7Sderaadt  * The author supplies this software to be publicly redistributed on the
45df930be7Sderaadt  * understanding that the author is not responsible for the correct
46df930be7Sderaadt  * functioning of this software in any circumstances and is not liable for
47df930be7Sderaadt  * any damages caused by this software.
48df930be7Sderaadt  *
49df930be7Sderaadt  * October 1992
50df930be7Sderaadt  */
51df930be7Sderaadt 
52df930be7Sderaadt #include <sys/param.h>
53df930be7Sderaadt #include <sys/systm.h>
54df930be7Sderaadt #include <sys/namei.h>
55df930be7Sderaadt #include <sys/resourcevar.h>	/* defines plimit structure in proc struct */
564b1f64dcSguenther #include <sys/fcntl.h>		/* define FWRITE ... */
574b1f64dcSguenther #include <sys/file.h>
58df930be7Sderaadt #include <sys/stat.h>
59df930be7Sderaadt #include <sys/buf.h>
60df930be7Sderaadt #include <sys/proc.h>
61df930be7Sderaadt #include <sys/mount.h>
62df930be7Sderaadt #include <sys/vnode.h>
63fde894e5Stedu #include <sys/lock.h>
64879b3eabSniklas #include <sys/signalvar.h>
65544451c3Sderaadt #include <sys/specdev.h> /* XXX */	/* defines v_rdev */
66df930be7Sderaadt #include <sys/malloc.h>
67dbe27ba0Stedu #include <sys/pool.h>
68fc1baee8Stholo #include <sys/dirent.h>		/* defines dirent structure */
6916bf7bd1Sderaadt #include <sys/lockf.h>
70782ebdf8Stedu #include <sys/unistd.h>
71df930be7Sderaadt 
72df930be7Sderaadt #include <msdosfs/bpb.h>
73df930be7Sderaadt #include <msdosfs/direntry.h>
74df930be7Sderaadt #include <msdosfs/denode.h>
75df930be7Sderaadt #include <msdosfs/msdosfsmount.h>
76df930be7Sderaadt #include <msdosfs/fat.h>
77df930be7Sderaadt 
78979947e6Stom static uint32_t fileidhash(uint64_t);
79979947e6Stom 
8090aa86e4Smpi int msdosfs_bmaparray(struct vnode *, uint32_t, daddr_t *, int *);
814eda5a5eSrobert int msdosfs_kqfilter(void *);
829dc3dd48Sjsg int filt_msdosfsread(struct knote *, long);
839dc3dd48Sjsg int filt_msdosfswrite(struct knote *, long);
844eda5a5eSrobert int filt_msdosfsvnode(struct knote *, long);
854eda5a5eSrobert void filt_msdosfsdetach(struct knote *);
864eda5a5eSrobert 
87df930be7Sderaadt /*
88df930be7Sderaadt  * Some general notes:
89df930be7Sderaadt  *
90df930be7Sderaadt  * In the ufs filesystem the inodes, superblocks, and indirect blocks are
91df930be7Sderaadt  * read/written using the vnode for the filesystem. Blocks that represent
92df930be7Sderaadt  * the contents of a file are read/written using the vnode for the file
93df930be7Sderaadt  * (including directories when they are read/written as files). This
94df930be7Sderaadt  * presents problems for the dos filesystem because data that should be in
95df930be7Sderaadt  * an inode (if dos had them) resides in the directory itself.  Since we
96df930be7Sderaadt  * must update directory entries without the benefit of having the vnode
97df930be7Sderaadt  * for the directory we must use the vnode for the filesystem.  This means
98df930be7Sderaadt  * that when a directory is actually read/written (via read, write, or
99df930be7Sderaadt  * readdir, or seek) we must use the vnode for the filesystem instead of
100df930be7Sderaadt  * the vnode for the directory as would happen in ufs. This is to insure we
101d8acb55aSaaron  * retrieve the correct block from the buffer cache since the hash value is
102df930be7Sderaadt  * based upon the vnode address and the desired block number.
103df930be7Sderaadt  */
104df930be7Sderaadt 
105df930be7Sderaadt /*
106df930be7Sderaadt  * Create a regular file. On entry the directory to contain the file being
107df930be7Sderaadt  * created is locked.  We must release before we return. We must also free
108df930be7Sderaadt  * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
109df930be7Sderaadt  * only if the SAVESTART bit in cn_flags is clear on success.
110df930be7Sderaadt  */
111df930be7Sderaadt int
1127d80fe84Sjasper msdosfs_create(void *v)
113879b3eabSniklas {
11499bc9d31Sderaadt 	struct vop_create_args *ap = v;
115df930be7Sderaadt 	struct componentname *cnp = ap->a_cnp;
116df930be7Sderaadt 	struct denode ndirent;
117df930be7Sderaadt 	struct denode *dep;
118df930be7Sderaadt 	struct denode *pdep = VTODE(ap->a_dvp);
119df930be7Sderaadt 	int error;
12078b4a1c4Smillert 	struct timespec ts;
121df930be7Sderaadt 
122df930be7Sderaadt #ifdef MSDOSFS_DEBUG
12381ad5374Skrw 	printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
124df930be7Sderaadt #endif
125df930be7Sderaadt 
126df930be7Sderaadt 	/*
127df930be7Sderaadt 	 * If this is the root directory and there is no space left we
128df930be7Sderaadt 	 * can't do anything.  This is because the root directory can not
129df930be7Sderaadt 	 * change size.
130df930be7Sderaadt 	 */
13116bf7bd1Sderaadt 	if (pdep->de_StartCluster == MSDOSFSROOT
13216bf7bd1Sderaadt 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
133df930be7Sderaadt 		error = ENOSPC;
134df930be7Sderaadt 		goto bad;
135df930be7Sderaadt 	}
136df930be7Sderaadt 
137df930be7Sderaadt 	/*
138df930be7Sderaadt 	 * Create a directory entry for the file, then call createde() to
139df930be7Sderaadt 	 * have it installed. NOTE: DOS files are always executable.  We
140df930be7Sderaadt 	 * use the absence of the owner write bit to make the file
141df930be7Sderaadt 	 * readonly.
142df930be7Sderaadt 	 */
143df930be7Sderaadt #ifdef DIAGNOSTIC
144df930be7Sderaadt 	if ((cnp->cn_flags & HASBUF) == 0)
145df930be7Sderaadt 		panic("msdosfs_create: no name");
146df930be7Sderaadt #endif
147df930be7Sderaadt 	bzero(&ndirent, sizeof(ndirent));
148879b3eabSniklas 	if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
14916bf7bd1Sderaadt 		goto bad;
15016bf7bd1Sderaadt 
151df930be7Sderaadt 	ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ?
152df930be7Sderaadt 				ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
153df930be7Sderaadt 	ndirent.de_StartCluster = 0;
154df930be7Sderaadt 	ndirent.de_FileSize = 0;
155df930be7Sderaadt 	ndirent.de_dev = pdep->de_dev;
156df930be7Sderaadt 	ndirent.de_devvp = pdep->de_devvp;
15716bf7bd1Sderaadt 	ndirent.de_pmp = pdep->de_pmp;
15816bf7bd1Sderaadt 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
159c2475275Stholo 	getnanotime(&ts);
16078b4a1c4Smillert 	DETIMES(&ndirent, &ts, &ts, &ts);
161879b3eabSniklas 	if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
162df930be7Sderaadt 		goto bad;
163df930be7Sderaadt 	if ((cnp->cn_flags & SAVESTART) == 0)
164dbe27ba0Stedu 		pool_put(&namei_pool, cnp->cn_pnbuf);
1654eda5a5eSrobert 	VN_KNOTE(ap->a_dvp, NOTE_WRITE);
166df930be7Sderaadt 	*ap->a_vpp = DETOV(dep);
167df930be7Sderaadt 	return (0);
168df930be7Sderaadt 
169df930be7Sderaadt bad:
170dbe27ba0Stedu 	pool_put(&namei_pool, cnp->cn_pnbuf);
171df930be7Sderaadt 	return (error);
172df930be7Sderaadt }
173df930be7Sderaadt 
174df930be7Sderaadt int
1757d80fe84Sjasper msdosfs_mknod(void *v)
176879b3eabSniklas {
17799bc9d31Sderaadt 	struct vop_mknod_args *ap = v;
178df930be7Sderaadt 
179dbe27ba0Stedu 	pool_put(&namei_pool, ap->a_cnp->cn_pnbuf);
1804eda5a5eSrobert 	VN_KNOTE(ap->a_dvp, NOTE_WRITE);
181df930be7Sderaadt 	return (EINVAL);
182df930be7Sderaadt }
183df930be7Sderaadt 
184df930be7Sderaadt int
1857d80fe84Sjasper msdosfs_open(void *v)
186879b3eabSniklas {
187df930be7Sderaadt 	return (0);
188df930be7Sderaadt }
189df930be7Sderaadt 
190df930be7Sderaadt int
1917d80fe84Sjasper msdosfs_close(void *v)
192879b3eabSniklas {
19399bc9d31Sderaadt 	struct vop_close_args *ap = v;
194df930be7Sderaadt 	struct vnode *vp = ap->a_vp;
195df930be7Sderaadt 	struct denode *dep = VTODE(vp);
19678b4a1c4Smillert 	struct timespec ts;
197df930be7Sderaadt 
19807feb63cScsapuntz 	if (vp->v_usecount > 1 && !VOP_ISLOCKED(vp)) {
199c2475275Stholo 		getnanotime(&ts);
20078b4a1c4Smillert 		DETIMES(dep, &ts, &ts, &ts);
20178b4a1c4Smillert 	}
202df930be7Sderaadt 	return (0);
203df930be7Sderaadt }
204df930be7Sderaadt 
205df930be7Sderaadt int
2067d80fe84Sjasper msdosfs_access(void *v)
207879b3eabSniklas {
20899bc9d31Sderaadt 	struct vop_access_args *ap = v;
209df930be7Sderaadt 	struct denode *dep = VTODE(ap->a_vp);
210df930be7Sderaadt 	struct msdosfsmount *pmp = dep->de_pmp;
211df930be7Sderaadt 	mode_t dosmode;
212df930be7Sderaadt 
213348d43f1Sderaadt 	dosmode = (S_IRUSR | S_IRGRP | S_IROTH);
214df930be7Sderaadt 	if ((dep->de_Attributes & ATTR_READONLY) == 0)
215df930be7Sderaadt 		dosmode |= (S_IWUSR | S_IWGRP | S_IWOTH);
216c5450bbbSkrw 	if (dep->de_Attributes & ATTR_DIRECTORY)
217c5450bbbSkrw 		dosmode |= (S_IXUSR | S_IXGRP | S_IXOTH);
218c8faf75dSnatano 	dosmode &= pmp->pm_mask;
219df930be7Sderaadt 
220141c07a8Smillert 	return (vaccess(ap->a_vp->v_type, dosmode, pmp->pm_uid, pmp->pm_gid,
221141c07a8Smillert 	    ap->a_mode, ap->a_cred));
222df930be7Sderaadt }
223df930be7Sderaadt 
224df930be7Sderaadt int
2257d80fe84Sjasper msdosfs_getattr(void *v)
226879b3eabSniklas {
22799bc9d31Sderaadt 	struct vop_getattr_args *ap = v;
228df930be7Sderaadt 	struct denode *dep = VTODE(ap->a_vp);
229b099d67bSprovos 	struct msdosfsmount *pmp = dep->de_pmp;
230df930be7Sderaadt 	struct vattr *vap = ap->a_vap;
23178b4a1c4Smillert 	struct timespec ts;
23282fa9538Stedu 	uint32_t fileid;
233df930be7Sderaadt 
234c2475275Stholo 	getnanotime(&ts);
23578b4a1c4Smillert 	DETIMES(dep, &ts, &ts, &ts);
236df930be7Sderaadt 	vap->va_fsid = dep->de_dev;
237979947e6Stom 
238df930be7Sderaadt 	/*
239979947e6Stom 	 * The following computation of the fileid must be the same as
240979947e6Stom 	 * that used in msdosfs_readdir() to compute d_fileno. If not,
241979947e6Stom 	 * pwd doesn't work.
242979947e6Stom 	 *
243979947e6Stom 	 * We now use the starting cluster number as the fileid/fileno.
244979947e6Stom 	 * This works for both files and directories (including the root
245979947e6Stom 	 * directory, on FAT32).  Even on FAT32, this will at most be a
246979947e6Stom 	 * 28-bit number, as the high 4 bits of FAT32 cluster numbers
247979947e6Stom 	 * are reserved.
248979947e6Stom 	 *
249979947e6Stom 	 * However, we do need to do something for 0-length files, which
250979947e6Stom 	 * will not have a starting cluster number.
251979947e6Stom 	 *
252979947e6Stom 	 * These files cannot be directories, since (except for /, which
253979947e6Stom 	 * is special-cased anyway) directories contain entries for . and
254979947e6Stom 	 * .., so must have non-zero length.
255979947e6Stom 	 *
256979947e6Stom 	 * In this case, we just create a non-cryptographic hash of the
257979947e6Stom 	 * original fileid calculation, and set the top bit.
258979947e6Stom 	 *
259979947e6Stom 	 * This algorithm has the benefit that all directories, and all
260979947e6Stom 	 * non-zero-length files, will have fileids that are persistent
261979947e6Stom 	 * across mounts and reboots, and that cannot collide (as long
262979947e6Stom 	 * as the filesystem is not corrupt).  Zero-length files will
263979947e6Stom 	 * have fileids that are persistent, but that may collide.  We
264979947e6Stom 	 * will just have to live with that.
265df930be7Sderaadt 	 */
266979947e6Stom 	fileid = dep->de_StartCluster;
267979947e6Stom 
268df930be7Sderaadt 	if (dep->de_Attributes & ATTR_DIRECTORY) {
269979947e6Stom 		/* Special-case root */
270b099d67bSprovos 		if (dep->de_StartCluster == MSDOSFSROOT)
271979947e6Stom 			fileid = FAT32(pmp) ? pmp->pm_rootdirblk : 1;
272df930be7Sderaadt 	} else {
273979947e6Stom 		if (dep->de_FileSize == 0) {
274979947e6Stom 			uint32_t dirsperblk;
275979947e6Stom 			uint64_t fileid64;
276979947e6Stom 
277979947e6Stom 			dirsperblk = pmp->pm_BytesPerSec /
278979947e6Stom 			    sizeof(struct direntry);
279979947e6Stom 
280979947e6Stom 			fileid64 = (dep->de_dirclust == MSDOSFSROOT) ?
281979947e6Stom 			    roottobn(pmp, 0) : cntobn(pmp, dep->de_dirclust);
282979947e6Stom 			fileid64 *= dirsperblk;
283979947e6Stom 			fileid64 += dep->de_diroffset / sizeof(struct direntry);
284979947e6Stom 
285979947e6Stom 			fileid = fileidhash(fileid64);
286df930be7Sderaadt 		}
287979947e6Stom 	}
288979947e6Stom 
289b099d67bSprovos 	vap->va_fileid = fileid;
290c8faf75dSnatano 	vap->va_mode = (S_IRUSR|S_IRGRP|S_IROTH);
291c8faf75dSnatano 	if ((dep->de_Attributes & ATTR_READONLY) == 0)
292c8faf75dSnatano 		vap->va_mode |= (S_IWUSR|S_IWGRP|S_IWOTH);
293e8b9805cStedu 	if (dep->de_Attributes & ATTR_DIRECTORY) {
294df930be7Sderaadt 		vap->va_mode |= S_IFDIR;
295e8b9805cStedu 		vap->va_mode |= (vap->va_mode & S_IRUSR) ? S_IXUSR : 0;
296e8b9805cStedu 		vap->va_mode |= (vap->va_mode & S_IRGRP) ? S_IXGRP : 0;
297e8b9805cStedu 		vap->va_mode |= (vap->va_mode & S_IROTH) ? S_IXOTH : 0;
298e8b9805cStedu 	}
299c8faf75dSnatano 	vap->va_mode &= dep->de_pmp->pm_mask;
300df930be7Sderaadt 	vap->va_nlink = 1;
301df930be7Sderaadt 	vap->va_gid = dep->de_pmp->pm_gid;
302df930be7Sderaadt 	vap->va_uid = dep->de_pmp->pm_uid;
303df930be7Sderaadt 	vap->va_rdev = 0;
304df930be7Sderaadt 	vap->va_size = dep->de_FileSize;
305b099d67bSprovos 	dos2unixtime(dep->de_MDate, dep->de_MTime, 0, &vap->va_mtime);
30616bf7bd1Sderaadt 	if (dep->de_pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
307b099d67bSprovos 		dos2unixtime(dep->de_ADate, 0, 0, &vap->va_atime);
308b099d67bSprovos 		dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CTimeHundredth, &vap->va_ctime);
30916bf7bd1Sderaadt 	} else {
31016bf7bd1Sderaadt 		vap->va_atime = vap->va_mtime;
31116bf7bd1Sderaadt 		vap->va_ctime = vap->va_mtime;
31216bf7bd1Sderaadt 	}
313df930be7Sderaadt 	vap->va_flags = 0;
314df930be7Sderaadt 	if ((dep->de_Attributes & ATTR_ARCHIVE) == 0)
315df930be7Sderaadt 		vap->va_flags |= SF_ARCHIVED;
316df930be7Sderaadt 	vap->va_gen = 0;
317df930be7Sderaadt 	vap->va_blocksize = dep->de_pmp->pm_bpcluster;
318df930be7Sderaadt 	vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) &
319df930be7Sderaadt 				~(dep->de_pmp->pm_crbomask);
320df930be7Sderaadt 	vap->va_type = ap->a_vp->v_type;
321df930be7Sderaadt 	return (0);
322df930be7Sderaadt }
323df930be7Sderaadt 
324df930be7Sderaadt int
3257d80fe84Sjasper msdosfs_setattr(void *v)
326879b3eabSniklas {
32799bc9d31Sderaadt 	struct vop_setattr_args *ap = v;
328d8893a82Smartynas 	struct vnode *vp = ap->a_vp;
329df930be7Sderaadt 	struct denode *dep = VTODE(ap->a_vp);
330d8893a82Smartynas 	struct msdosfsmount *pmp = dep->de_pmp;
331df930be7Sderaadt 	struct vattr *vap = ap->a_vap;
332df930be7Sderaadt 	struct ucred *cred = ap->a_cred;
333d8893a82Smartynas 	int error = 0;
334df930be7Sderaadt 
335df930be7Sderaadt #ifdef MSDOSFS_DEBUG
33681ad5374Skrw 	printf("msdosfs_setattr(): vp %p, vap %p, cred %p, p %p\n",
337df930be7Sderaadt 	    ap->a_vp, vap, cred, ap->a_p);
338df930be7Sderaadt #endif
339df930be7Sderaadt 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
340df930be7Sderaadt 	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
341df930be7Sderaadt 	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
342d8893a82Smartynas 	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
343df930be7Sderaadt #ifdef MSDOSFS_DEBUG
344df930be7Sderaadt 		printf("msdosfs_setattr(): returning EINVAL\n");
34581ad5374Skrw 		printf("    va_type %d, va_nlink %x, va_fsid %ld, "
34681ad5374Skrw 		    "va_fileid %llx\n", vap->va_type, vap->va_nlink,
34781ad5374Skrw 		    vap->va_fsid, (unsigned long long)vap->va_fileid);
34881ad5374Skrw 		printf("    va_blocksize %lx, va_rdev %x, va_bytes %llx, "
34981ad5374Skrw 		    "va_gen %lx\n", vap->va_blocksize,
35081ad5374Skrw 		    (unsigned int)vap->va_rdev,
35181ad5374Skrw 		    (unsigned long long)vap->va_bytes, vap->va_gen);
352df930be7Sderaadt #endif
353df930be7Sderaadt 		return (EINVAL);
354df930be7Sderaadt 	}
355d8893a82Smartynas 	if (vap->va_flags != VNOVAL) {
356d8893a82Smartynas 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
357d8893a82Smartynas 			return (EINVAL);
358d8893a82Smartynas 		if (cred->cr_uid != pmp->pm_uid) {
359d8893a82Smartynas 			error = suser_ucred(cred);
360879b3eabSniklas 			if (error)
361df930be7Sderaadt 				return (error);
362df930be7Sderaadt 		}
363d8893a82Smartynas 		/*
364d8893a82Smartynas 		 * We are very inconsistent about handling unsupported
365d8893a82Smartynas 		 * attributes.  We ignored the access time and the
366d8893a82Smartynas 		 * read and execute bits.  We were strict for the other
367d8893a82Smartynas 		 * attributes.
368d8893a82Smartynas 		 *
369d8893a82Smartynas 		 * Here we are strict, stricter than ufs in not allowing
370d8893a82Smartynas 		 * users to attempt to set SF_SETTABLE bits or anyone to
371d8893a82Smartynas 		 * set unsupported bits.  However, we ignore attempts to
372d8893a82Smartynas 		 * set ATTR_ARCHIVE for directories `cp -pr' from a more
373d8893a82Smartynas 		 * sensible filesystem attempts it a lot.
374d8893a82Smartynas 		 */
375d8893a82Smartynas 		if (vap->va_flags & SF_SETTABLE) {
376d8893a82Smartynas 			error = suser_ucred(cred);
377d8893a82Smartynas 			if (error)
378d8893a82Smartynas 				return (error);
379d8893a82Smartynas 		}
380d8893a82Smartynas 		if (vap->va_flags & ~SF_ARCHIVED)
381d8893a82Smartynas 			return EOPNOTSUPP;
382d8893a82Smartynas 		if (vap->va_flags & SF_ARCHIVED)
383d8893a82Smartynas 			dep->de_Attributes &= ~ATTR_ARCHIVE;
384d8893a82Smartynas 		else if (!(dep->de_Attributes & ATTR_DIRECTORY))
385d8893a82Smartynas 			dep->de_Attributes |= ATTR_ARCHIVE;
386d8893a82Smartynas 		dep->de_flag |= DE_MODIFIED;
387d8893a82Smartynas 	}
388d8893a82Smartynas 
389d8893a82Smartynas 	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
390d8893a82Smartynas 		uid_t uid;
391d8893a82Smartynas 		gid_t gid;
392d8893a82Smartynas 
393d8893a82Smartynas 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
394d8893a82Smartynas 			return (EINVAL);
395d8893a82Smartynas 		uid = vap->va_uid;
396d8893a82Smartynas 		if (uid == (uid_t)VNOVAL)
397d8893a82Smartynas 			uid = pmp->pm_uid;
398d8893a82Smartynas 		gid = vap->va_gid;
399d8893a82Smartynas 		if (gid == (gid_t)VNOVAL)
400d8893a82Smartynas 			gid = pmp->pm_gid;
401d8893a82Smartynas 		if (cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid ||
402d8893a82Smartynas 		    (gid != pmp->pm_gid && !groupmember(gid, cred))) {
403d8893a82Smartynas 			error = suser_ucred(cred);
404d8893a82Smartynas 			if (error)
405d8893a82Smartynas 				return (error);
406d8893a82Smartynas 		}
407d8893a82Smartynas 		if (uid != pmp->pm_uid || gid != pmp->pm_gid)
408d8893a82Smartynas 			return EINVAL;
409d8893a82Smartynas 	}
410d8893a82Smartynas 
411d8893a82Smartynas 	if (vap->va_size != VNOVAL) {
412d8893a82Smartynas 		switch (vp->v_type) {
413d8893a82Smartynas 		case VDIR:
414d8893a82Smartynas 			return (EISDIR);
415d8893a82Smartynas 		case VREG:
416d8893a82Smartynas 			/*
417d8893a82Smartynas 			 * Truncation is only supported for regular files,
418d8893a82Smartynas 			 * Disallow it if the filesystem is read-only.
419d8893a82Smartynas 			 */
420d8893a82Smartynas 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
421d8893a82Smartynas 				return (EINVAL);
422d8893a82Smartynas 			break;
423d8893a82Smartynas 		default:
424d8893a82Smartynas 			/*
425d8893a82Smartynas 			 * According to POSIX, the result is unspecified
426d8893a82Smartynas 			 * for file types other than regular files,
427d8893a82Smartynas 			 * directories and shared memory objects.  We
428d8893a82Smartynas 			 * don't support any file types except regular
429d8893a82Smartynas 			 * files and directories in this file system, so
430d8893a82Smartynas 			 * this (default) case is unreachable and can do
431d8893a82Smartynas 			 * anything.  Keep falling through to detrunc()
432d8893a82Smartynas 			 * for now.
433d8893a82Smartynas 			 */
434d8893a82Smartynas 			break;
435d8893a82Smartynas 		}
436d8893a82Smartynas 		error = detrunc(dep, vap->va_size, 0, cred, ap->a_p);
437d8893a82Smartynas 		if (error)
438d8893a82Smartynas 			return error;
439d8893a82Smartynas 	}
4404707cbe3Sguenther 	if ((vap->va_vaflags & VA_UTIMES_CHANGE) ||
4414707cbe3Sguenther 	    vap->va_atime.tv_nsec != VNOVAL ||
4424707cbe3Sguenther 	    vap->va_mtime.tv_nsec != VNOVAL) {
443d8893a82Smartynas 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
444d8893a82Smartynas 			return (EINVAL);
445d8893a82Smartynas 		if (cred->cr_uid != pmp->pm_uid &&
446ad392958Stedu 		    (error = suser_ucred(cred)) &&
447df930be7Sderaadt 		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
448df930be7Sderaadt 		    (error = VOP_ACCESS(ap->a_vp, VWRITE, cred, ap->a_p))))
449df930be7Sderaadt 			return (error);
450d8893a82Smartynas 		if (vp->v_type != VDIR) {
451d8893a82Smartynas 			if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
4524707cbe3Sguenther 			    vap->va_atime.tv_nsec != VNOVAL) {
453d8893a82Smartynas 				dep->de_flag &= ~DE_ACCESS;
454d8893a82Smartynas 				unix2dostime(&vap->va_atime, &dep->de_ADate,
455d8893a82Smartynas 				    NULL, NULL);
456d8893a82Smartynas 			}
4574707cbe3Sguenther 			if (vap->va_mtime.tv_nsec != VNOVAL) {
458d8893a82Smartynas 				dep->de_flag &= ~DE_UPDATE;
459d8893a82Smartynas 				unix2dostime(&vap->va_mtime, &dep->de_MDate,
460d8893a82Smartynas 				    &dep->de_MTime, NULL);
461d8893a82Smartynas 			}
462df930be7Sderaadt 			dep->de_Attributes |= ATTR_ARCHIVE;
463df930be7Sderaadt 			dep->de_flag |= DE_MODIFIED;
464df930be7Sderaadt 		}
465d8893a82Smartynas 	}
466df930be7Sderaadt 	/*
467df930be7Sderaadt 	 * DOS files only have the ability to have their writability
468df930be7Sderaadt 	 * attribute set, so we use the owner write bit to set the readonly
469df930be7Sderaadt 	 * attribute.
470df930be7Sderaadt 	 */
471df930be7Sderaadt 	if (vap->va_mode != (mode_t)VNOVAL) {
472d8893a82Smartynas 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
473d8893a82Smartynas 			return (EINVAL);
474d8893a82Smartynas 		if (cred->cr_uid != pmp->pm_uid) {
475d8893a82Smartynas 			error = suser_ucred(cred);
476d8893a82Smartynas 			if (error)
477df930be7Sderaadt 				return (error);
478d8893a82Smartynas 		}
479d8893a82Smartynas 		if (vp->v_type != VDIR) {
480df930be7Sderaadt 			/* We ignore the read and execute bits. */
481df930be7Sderaadt 			if (vap->va_mode & VWRITE)
482df930be7Sderaadt 				dep->de_Attributes &= ~ATTR_READONLY;
483df930be7Sderaadt 			else
484df930be7Sderaadt 				dep->de_Attributes |= ATTR_READONLY;
485df930be7Sderaadt 			dep->de_Attributes |= ATTR_ARCHIVE;
486df930be7Sderaadt 			dep->de_flag |= DE_MODIFIED;
487df930be7Sderaadt 		}
488d8893a82Smartynas 	}
4894eda5a5eSrobert 	VN_KNOTE(ap->a_vp, NOTE_ATTRIB);
49016bf7bd1Sderaadt 	return (deupdat(dep, 1));
491df930be7Sderaadt }
492df930be7Sderaadt 
493df930be7Sderaadt int
4947d80fe84Sjasper msdosfs_read(void *v)
495879b3eabSniklas {
49699bc9d31Sderaadt 	struct vop_read_args *ap = v;
497df930be7Sderaadt 	struct vnode *vp = ap->a_vp;
498df930be7Sderaadt 	struct denode *dep = VTODE(vp);
499df930be7Sderaadt 	struct msdosfsmount *pmp = dep->de_pmp;
500df930be7Sderaadt 	struct uio *uio = ap->a_uio;
50190aa86e4Smpi 	int isadir, error = 0;
50290aa86e4Smpi 	uint32_t n, diff, size, on;
50390aa86e4Smpi 	struct buf *bp;
50490aa86e4Smpi 	uint32_t cn;
50590aa86e4Smpi 	daddr_t bn;
506df930be7Sderaadt 
507df930be7Sderaadt 	/*
508df930be7Sderaadt 	 * If they didn't ask for any data, then we are done.
509df930be7Sderaadt 	 */
510df930be7Sderaadt 	if (uio->uio_resid == 0)
511df930be7Sderaadt 		return (0);
512df930be7Sderaadt 	if (uio->uio_offset < 0)
513df930be7Sderaadt 		return (EINVAL);
514df930be7Sderaadt 
5151414b0faSart 	isadir = dep->de_Attributes & ATTR_DIRECTORY;
516df930be7Sderaadt 	do {
517a0186565Stom 		if (uio->uio_offset >= dep->de_FileSize)
518a0186565Stom 			return (0);
519a0186565Stom 
52090aa86e4Smpi 		cn = de_cluster(pmp, uio->uio_offset);
52190aa86e4Smpi 		size = pmp->pm_bpcluster;
522df930be7Sderaadt 		on = uio->uio_offset & pmp->pm_crbomask;
52303e5328eSstefan 		n = ulmin(pmp->pm_bpcluster - on, uio->uio_resid);
524a0186565Stom 
525a0186565Stom 		/*
526a0186565Stom 		 * de_FileSize is uint32_t, and we know that uio_offset <
527a0186565Stom 		 * de_FileSize, so uio->uio_offset < 2^32.  Therefore
528a0186565Stom 		 * the cast to uint32_t on the next line is safe.
529a0186565Stom 		 */
530a0186565Stom 		diff = dep->de_FileSize - (uint32_t)uio->uio_offset;
531df930be7Sderaadt 		if (diff < n)
532df930be7Sderaadt 			n = diff;
533a0186565Stom 
534df930be7Sderaadt 		/*
535df930be7Sderaadt 		 * If we are operating on a directory file then be sure to
536df930be7Sderaadt 		 * do i/o with the vnode for the filesystem instead of the
537df930be7Sderaadt 		 * vnode for the directory.
538df930be7Sderaadt 		 */
5391414b0faSart 		if (isadir) {
54090aa86e4Smpi 			/* convert cluster # to block # */
54190aa86e4Smpi 			error = pcbmap(dep, cn, &bn, 0, &size);
54290aa86e4Smpi 			if (error)
54390aa86e4Smpi 				return (error);
54490aa86e4Smpi 			error = bread(pmp->pm_devvp, bn, size, &bp);
5451414b0faSart 		} else {
54690aa86e4Smpi 			if (de_cn2off(pmp, cn + 1) >= dep->de_FileSize)
54790aa86e4Smpi 				error = bread(vp, cn, size, &bp);
5481414b0faSart 			else
54990aa86e4Smpi 				error = bread_cluster(vp, cn, size, &bp);
5501414b0faSart 		}
5511414b0faSart 		n = min(n, pmp->pm_bpcluster - bp->b_resid);
552df930be7Sderaadt 		if (error) {
553df930be7Sderaadt 			brelse(bp);
554df930be7Sderaadt 			return (error);
555df930be7Sderaadt 		}
55603e5328eSstefan 		error = uiomove(bp->b_data + on, n, uio);
557df930be7Sderaadt 		brelse(bp);
558df930be7Sderaadt 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
5591414b0faSart 	if (!isadir && !(vp->v_mount->mnt_flag & MNT_NOATIME))
5601414b0faSart 		dep->de_flag |= DE_ACCESS;
561df930be7Sderaadt 	return (error);
562df930be7Sderaadt }
563df930be7Sderaadt 
564df930be7Sderaadt /*
565df930be7Sderaadt  * Write data to a file or directory.
566df930be7Sderaadt  */
567df930be7Sderaadt int
5687d80fe84Sjasper msdosfs_write(void *v)
569879b3eabSniklas {
57099bc9d31Sderaadt 	struct vop_write_args *ap = v;
57103e5328eSstefan 	uint32_t n, croffset;
57203e5328eSstefan 	size_t resid;
573906c107fSguenther 	ssize_t overrun;
5744eda5a5eSrobert 	int extended = 0;
57582fa9538Stedu 	uint32_t osize;
576879b3eabSniklas 	int error = 0;
57790aa86e4Smpi 	uint32_t count, lastcn, cn;
5781414b0faSart 	struct buf *bp;
579df930be7Sderaadt 	int ioflag = ap->a_ioflag;
580df930be7Sderaadt 	struct uio *uio = ap->a_uio;
581df930be7Sderaadt 	struct vnode *vp = ap->a_vp;
5821414b0faSart 	struct vnode *thisvp;
583df930be7Sderaadt 	struct denode *dep = VTODE(vp);
584df930be7Sderaadt 	struct msdosfsmount *pmp = dep->de_pmp;
585df930be7Sderaadt 	struct ucred *cred = ap->a_cred;
586df930be7Sderaadt 
587df930be7Sderaadt #ifdef MSDOSFS_DEBUG
58881ad5374Skrw 	printf("msdosfs_write(vp %p, uio %p, ioflag %08x, cred %p\n",
589df930be7Sderaadt 	    vp, uio, ioflag, cred);
590df930be7Sderaadt 	printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n",
591df930be7Sderaadt 	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
592df930be7Sderaadt #endif
593df930be7Sderaadt 
59416bf7bd1Sderaadt 	switch (vp->v_type) {
59516bf7bd1Sderaadt 	case VREG:
596df930be7Sderaadt 		if (ioflag & IO_APPEND)
597df930be7Sderaadt 			uio->uio_offset = dep->de_FileSize;
5981414b0faSart 		thisvp = vp;
59916bf7bd1Sderaadt 		break;
60016bf7bd1Sderaadt 	case VDIR:
60116bf7bd1Sderaadt 		return EISDIR;
60216bf7bd1Sderaadt 	default:
603df930be7Sderaadt 		panic("msdosfs_write(): bad file type");
60416bf7bd1Sderaadt 	}
605df930be7Sderaadt 
606df930be7Sderaadt 	if (uio->uio_offset < 0)
607df930be7Sderaadt 		return (EINVAL);
608df930be7Sderaadt 
609df930be7Sderaadt 	if (uio->uio_resid == 0)
610df930be7Sderaadt 		return (0);
611df930be7Sderaadt 
6128b243da0Stom 	/* Don't bother to try to write files larger than the f/s limit */
6134d6ebfb3Sstefan 	if (uio->uio_offset > MSDOSFS_FILESIZE_MAX ||
6144d6ebfb3Sstefan 	    uio->uio_resid > (MSDOSFS_FILESIZE_MAX - uio->uio_offset))
6158b243da0Stom 		return (EFBIG);
6168b243da0Stom 
617543c93a8Sguenther 	/* do the filesize rlimit check */
618543c93a8Sguenther 	if ((error = vn_fsizechk(vp, uio, ioflag, &overrun)))
619543c93a8Sguenther 		return (error);
620df930be7Sderaadt 
621df930be7Sderaadt 	/*
622df930be7Sderaadt 	 * If the offset we are starting the write at is beyond the end of
623df930be7Sderaadt 	 * the file, then they've done a seek.  Unix filesystems allow
624df930be7Sderaadt 	 * files with holes in them, DOS doesn't so we must fill the hole
625df930be7Sderaadt 	 * with zeroed blocks.
626df930be7Sderaadt 	 */
627df930be7Sderaadt 	if (uio->uio_offset > dep->de_FileSize) {
628879b3eabSniklas 		if ((error = deextend(dep, uio->uio_offset, cred)) != 0)
629543c93a8Sguenther 			goto out;
630df930be7Sderaadt 	}
631df930be7Sderaadt 
632df930be7Sderaadt 	/*
633df930be7Sderaadt 	 * Remember some values in case the write fails.
634df930be7Sderaadt 	 */
635df930be7Sderaadt 	resid = uio->uio_resid;
636df930be7Sderaadt 	osize = dep->de_FileSize;
637df930be7Sderaadt 
638df930be7Sderaadt 	/*
639df930be7Sderaadt 	 * If we write beyond the end of the file, extend it to its ultimate
640df930be7Sderaadt 	 * size ahead of the time to hopefully get a contiguous area.
641df930be7Sderaadt 	 */
642df930be7Sderaadt 	if (uio->uio_offset + resid > osize) {
6434eda5a5eSrobert 		extended = 1;
644df930be7Sderaadt 		count = de_clcount(pmp, uio->uio_offset + resid) -
645df930be7Sderaadt 			de_clcount(pmp, osize);
646df930be7Sderaadt 		if ((error = extendfile(dep, count, NULL, NULL, 0)) &&
647df930be7Sderaadt 		    (error != ENOSPC || (ioflag & IO_UNIT)))
648df930be7Sderaadt 			goto errexit;
649df930be7Sderaadt 		lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
650df930be7Sderaadt 	} else
651df930be7Sderaadt 		lastcn = de_clcount(pmp, osize) - 1;
652df930be7Sderaadt 
653df930be7Sderaadt 	do {
65490aa86e4Smpi 		croffset = uio->uio_offset & pmp->pm_crbomask;
65590aa86e4Smpi 		cn = de_cluster(pmp, uio->uio_offset);
65690aa86e4Smpi 
65790aa86e4Smpi 		if (cn > lastcn) {
658df930be7Sderaadt 			error = ENOSPC;
659df930be7Sderaadt 			break;
660df930be7Sderaadt 		}
661df930be7Sderaadt 
66290aa86e4Smpi 		if (croffset == 0 &&
66390aa86e4Smpi 		    (de_cluster(pmp, uio->uio_offset + uio->uio_resid) > cn ||
66490aa86e4Smpi 		     (uio->uio_offset + uio->uio_resid) >= dep->de_FileSize)) {
665df930be7Sderaadt 			/*
6661414b0faSart 			 * If either the whole cluster gets written,
6671414b0faSart 			 * or we write the cluster from its start beyond EOF,
6681414b0faSart 			 * then no need to read data from disk.
669df930be7Sderaadt 			 */
670570df5c4Scheloha 			bp = getblk(thisvp, cn, pmp->pm_bpcluster, 0, INFSLP);
6711414b0faSart 			clrbuf(bp);
6721414b0faSart 			/*
6731414b0faSart 			 * Do the bmap now, since pcbmap needs buffers
6741414b0faSart 			 * for the fat table. (see msdosfs_strategy)
6751414b0faSart 			 */
6761414b0faSart 			if (bp->b_blkno == bp->b_lblkno) {
67790aa86e4Smpi 				error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0, 0);
6781414b0faSart 				if (error)
6791414b0faSart 					bp->b_blkno = -1;
6801414b0faSart 			}
6811414b0faSart 			if (bp->b_blkno == -1) {
6821414b0faSart 				brelse(bp);
6831414b0faSart 				if (!error)
6841414b0faSart 					error = EIO;		/* XXX */
685df930be7Sderaadt 				break;
6861414b0faSart 			}
6871414b0faSart 		} else {
6881414b0faSart 			/*
68990aa86e4Smpi 			 * The block we need to write into exists, so
69090aa86e4Smpi 			 * read it in.
6911414b0faSart 			 */
69290aa86e4Smpi 			error = bread(thisvp, cn, pmp->pm_bpcluster, &bp);
693879b3eabSniklas 			if (error) {
6941414b0faSart 				brelse(bp);
695df930be7Sderaadt 				break;
696df930be7Sderaadt 			}
69716bf7bd1Sderaadt 		}
6981414b0faSart 
69903e5328eSstefan 		n = ulmin(uio->uio_resid, pmp->pm_bpcluster - croffset);
7001414b0faSart 		if (uio->uio_offset + n > dep->de_FileSize) {
7011414b0faSart 			dep->de_FileSize = uio->uio_offset + n;
7021414b0faSart 			uvm_vnp_setsize(vp, dep->de_FileSize);
7031414b0faSart 		}
7041414b0faSart 		uvm_vnp_uncache(vp);
7051414b0faSart 		/*
7061414b0faSart 		 * Should these vnode_pager_* functions be done on dir
7071414b0faSart 		 * files?
7081414b0faSart 		 */
7091414b0faSart 
7101414b0faSart 		/*
7111414b0faSart 		 * Copy the data from user space into the buf header.
7121414b0faSart 		 */
71303e5328eSstefan 		error = uiomove(bp->b_data + croffset, n, uio);
7141414b0faSart 
7151414b0faSart 		/*
7161414b0faSart 		 * If they want this synchronous then write it and wait for
7171414b0faSart 		 * it.  Otherwise, if on a cluster boundary write it
7181414b0faSart 		 * asynchronously so we can move on to the next block
7191414b0faSart 		 * without delay.  Otherwise do a delayed write because we
7201414b0faSart 		 * may want to write some more into the block later.
7211414b0faSart 		 */
722728d3a0eStedu #if 0
723e1405772Sbeck 		if (ioflag & IO_NOCACHE)
724e1405772Sbeck 			bp->b_flags |= B_NOCACHE;
725728d3a0eStedu #endif
7261414b0faSart 		if (ioflag & IO_SYNC)
7271414b0faSart 			(void) bwrite(bp);
7281414b0faSart 		else if (n + croffset == pmp->pm_bpcluster)
7291414b0faSart 			bawrite(bp);
7301414b0faSart 		else
7311414b0faSart 			bdwrite(bp);
7325af79db2Sart 		dep->de_flag |= DE_UPDATE;
7331414b0faSart 	} while (error == 0 && uio->uio_resid > 0);
734df930be7Sderaadt 
7354eda5a5eSrobert 	if (resid > uio->uio_resid)
7364eda5a5eSrobert 		 VN_KNOTE(ap->a_vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
7374eda5a5eSrobert 
7384eda5a5eSrobert 	if (dep->de_FileSize < osize)
7394eda5a5eSrobert 		VN_KNOTE(ap->a_vp, NOTE_TRUNCATE);
7404eda5a5eSrobert 
741df930be7Sderaadt 	/*
742df930be7Sderaadt 	 * If the write failed and they want us to, truncate the file back
743df930be7Sderaadt 	 * to the size it was before the write was attempted.
744df930be7Sderaadt 	 */
745df930be7Sderaadt errexit:
746df930be7Sderaadt 	if (error) {
747df930be7Sderaadt 		if (ioflag & IO_UNIT) {
74819f38e0bScheloha 			detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, curproc);
749df930be7Sderaadt 			uio->uio_offset -= resid - uio->uio_resid;
750df930be7Sderaadt 			uio->uio_resid = resid;
751df930be7Sderaadt 		} else {
75219f38e0bScheloha 			detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, curproc);
753df930be7Sderaadt 			if (uio->uio_resid != resid)
754df930be7Sderaadt 				error = 0;
755df930be7Sderaadt 		}
75616bf7bd1Sderaadt 	} else if (ioflag & IO_SYNC)
757df930be7Sderaadt 		error = deupdat(dep, 1);
758543c93a8Sguenther 
759543c93a8Sguenther out:
760543c93a8Sguenther 	/* correct the result for writes clamped by vn_fsizechk() */
761543c93a8Sguenther 	uio->uio_resid += overrun;
762df930be7Sderaadt 	return (error);
763df930be7Sderaadt }
764df930be7Sderaadt 
765df930be7Sderaadt int
7667d80fe84Sjasper msdosfs_ioctl(void *v)
767879b3eabSniklas {
768df930be7Sderaadt 	return (ENOTTY);
769df930be7Sderaadt }
770df930be7Sderaadt 
771df930be7Sderaadt /*
772df930be7Sderaadt  * Flush the blocks of a file to disk.
773df930be7Sderaadt  *
774df930be7Sderaadt  * This function is worthless for vnodes that represent directories. Maybe we
775df930be7Sderaadt  * could just do a sync if they try an fsync on a directory file.
776df930be7Sderaadt  */
777df930be7Sderaadt int
7787d80fe84Sjasper msdosfs_fsync(void *v)
779879b3eabSniklas {
78099bc9d31Sderaadt 	struct vop_fsync_args *ap = v;
781df930be7Sderaadt 	struct vnode *vp = ap->a_vp;
782df930be7Sderaadt 
783df930be7Sderaadt 	vflushbuf(vp, ap->a_waitfor == MNT_WAIT);
784df930be7Sderaadt 	return (deupdat(VTODE(vp), ap->a_waitfor == MNT_WAIT));
785df930be7Sderaadt }
786df930be7Sderaadt 
787df930be7Sderaadt /*
78878b4a1c4Smillert  * Flush the blocks of a file to disk.
78978b4a1c4Smillert  *
79078b4a1c4Smillert  * This function is worthless for vnodes that represent directories. Maybe we
79178b4a1c4Smillert  * could just do a sync if they try an fsync on a directory file.
79278b4a1c4Smillert  */
79378b4a1c4Smillert int
7947d80fe84Sjasper msdosfs_remove(void *v)
795879b3eabSniklas {
79699bc9d31Sderaadt 	struct vop_remove_args *ap = v;
797df930be7Sderaadt 	struct denode *dep = VTODE(ap->a_vp);
798df930be7Sderaadt 	struct denode *ddep = VTODE(ap->a_dvp);
799df930be7Sderaadt 	int error;
800df930be7Sderaadt 
801d56d3b9eSderaadt 	if (ap->a_vp->v_type == VDIR)
802d56d3b9eSderaadt 		error = EPERM;
803d56d3b9eSderaadt 	else
804df930be7Sderaadt 		error = removede(ddep, dep);
8054eda5a5eSrobert 
8064eda5a5eSrobert 	VN_KNOTE(ap->a_vp, NOTE_DELETE);
8074eda5a5eSrobert 	VN_KNOTE(ap->a_dvp, NOTE_WRITE);
8084eda5a5eSrobert 
809df930be7Sderaadt #ifdef MSDOSFS_DEBUG
81081ad5374Skrw 	printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep,
81181ad5374Skrw 	    ap->a_vp->v_usecount);
812df930be7Sderaadt #endif
813df930be7Sderaadt 	return (error);
814df930be7Sderaadt }
815df930be7Sderaadt 
816df930be7Sderaadt /*
817df930be7Sderaadt  * DOS filesystems don't know what links are. But since we already called
818df930be7Sderaadt  * msdosfs_lookup() with create and lockparent, the parent is locked so we
819df930be7Sderaadt  * have to free it before we return the error.
820df930be7Sderaadt  */
821df930be7Sderaadt int
8227d80fe84Sjasper msdosfs_link(void *v)
823df930be7Sderaadt {
82499bc9d31Sderaadt 	struct vop_link_args *ap = v;
825df930be7Sderaadt 
826879b3eabSniklas 	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
827879b3eabSniklas 	vput(ap->a_dvp);
828df930be7Sderaadt 	return (EOPNOTSUPP);
829df930be7Sderaadt }
830df930be7Sderaadt 
831df930be7Sderaadt /*
832df930be7Sderaadt  * Renames on files require moving the denode to a new hash queue since the
833df930be7Sderaadt  * denode's location is used to compute which hash queue to put the file
834df930be7Sderaadt  * in. Unless it is a rename in place.  For example "mv a b".
835df930be7Sderaadt  *
836df930be7Sderaadt  * What follows is the basic algorithm:
837df930be7Sderaadt  *
838df930be7Sderaadt  * if (file move) {
839df930be7Sderaadt  *	if (dest file exists) {
840df930be7Sderaadt  *		remove dest file
841df930be7Sderaadt  *	}
842df930be7Sderaadt  *	if (dest and src in same directory) {
843df930be7Sderaadt  *		rewrite name in existing directory slot
844df930be7Sderaadt  *	} else {
845df930be7Sderaadt  *		write new entry in dest directory
846df930be7Sderaadt  *		update offset and dirclust in denode
847df930be7Sderaadt  *		move denode to new hash chain
848df930be7Sderaadt  *		clear old directory entry
849df930be7Sderaadt  *	}
850df930be7Sderaadt  * } else {
851df930be7Sderaadt  *	directory move
852df930be7Sderaadt  *	if (dest directory exists) {
853df930be7Sderaadt  *		if (dest is not empty) {
854df930be7Sderaadt  *			return ENOTEMPTY
855df930be7Sderaadt  *		}
856df930be7Sderaadt  *		remove dest directory
857df930be7Sderaadt  *	}
858df930be7Sderaadt  *	if (dest and src in same directory) {
859df930be7Sderaadt  *		rewrite name in existing entry
860df930be7Sderaadt  *	} else {
861df930be7Sderaadt  *		be sure dest is not a child of src directory
862df930be7Sderaadt  *		write entry in dest directory
863df930be7Sderaadt  *		update "." and ".." in moved directory
864df930be7Sderaadt  *		update offset and dirclust in denode
865df930be7Sderaadt  *		move denode to new hash chain
866df930be7Sderaadt  *		clear old directory entry for moved directory
867df930be7Sderaadt  *	}
868df930be7Sderaadt  * }
869df930be7Sderaadt  *
870df930be7Sderaadt  * On entry:
871df930be7Sderaadt  *	source's parent directory is unlocked
872df930be7Sderaadt  *	source file or directory is unlocked
873df930be7Sderaadt  *	destination's parent directory is locked
874df930be7Sderaadt  *	destination file or directory is locked if it exists
875df930be7Sderaadt  *
876df930be7Sderaadt  * On exit:
877df930be7Sderaadt  *	all denodes should be released
878df930be7Sderaadt  *
879df930be7Sderaadt  * Notes:
880df930be7Sderaadt  * I'm not sure how the memory containing the pathnames pointed at by the
881df930be7Sderaadt  * componentname structures is freed, there may be some memory bleeding
882df930be7Sderaadt  * for each rename done.
883df930be7Sderaadt  */
884df930be7Sderaadt int
8857d80fe84Sjasper msdosfs_rename(void *v)
886879b3eabSniklas {
88799bc9d31Sderaadt 	struct vop_rename_args *ap = v;
888df930be7Sderaadt 	struct vnode *tvp = ap->a_tvp;
889cc2a7e83Sjasper 	struct vnode *tdvp = ap->a_tdvp;
890df930be7Sderaadt 	struct vnode *fvp = ap->a_fvp;
891cc2a7e83Sjasper 	struct vnode *fdvp = ap->a_fdvp;
892cc2a7e83Sjasper 	struct componentname *tcnp = ap->a_tcnp;
893cc2a7e83Sjasper 	struct componentname *fcnp = ap->a_fcnp;
894cc2a7e83Sjasper 	struct denode *ip, *xp, *dp, *zp;
895df930be7Sderaadt 	u_char toname[11], oldname[11];
89682fa9538Stedu 	uint32_t from_diroffset, to_diroffset;
89716bf7bd1Sderaadt 	u_char to_count;
898df930be7Sderaadt 	int doingdirectory = 0, newparent = 0;
899df930be7Sderaadt 	int error;
90082fa9538Stedu 	uint32_t cn, pcl;
9011abdbfdeSderaadt 	daddr_t bn;
902df930be7Sderaadt 	struct msdosfsmount *pmp;
903df930be7Sderaadt 	struct direntry *dotdotp;
904df930be7Sderaadt 	struct buf *bp;
905df930be7Sderaadt 
906df930be7Sderaadt 	pmp = VFSTOMSDOSFS(fdvp->v_mount);
907df930be7Sderaadt 
908df930be7Sderaadt #ifdef DIAGNOSTIC
909df930be7Sderaadt 	if ((tcnp->cn_flags & HASBUF) == 0 ||
910df930be7Sderaadt 	    (fcnp->cn_flags & HASBUF) == 0)
911df930be7Sderaadt 		panic("msdosfs_rename: no name");
912df930be7Sderaadt #endif
913df930be7Sderaadt 	/*
914df930be7Sderaadt 	 * Check for cross-device rename.
915df930be7Sderaadt 	 */
916df930be7Sderaadt 	if ((fvp->v_mount != tdvp->v_mount) ||
917df930be7Sderaadt 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
918df930be7Sderaadt 		error = EXDEV;
919df930be7Sderaadt abortit:
920df930be7Sderaadt 		VOP_ABORTOP(tdvp, tcnp);
921df930be7Sderaadt 		if (tdvp == tvp)
922df930be7Sderaadt 			vrele(tdvp);
923df930be7Sderaadt 		else
924df930be7Sderaadt 			vput(tdvp);
925df930be7Sderaadt 		if (tvp)
926df930be7Sderaadt 			vput(tvp);
927df930be7Sderaadt 		VOP_ABORTOP(fdvp, fcnp);
928df930be7Sderaadt 		vrele(fdvp);
929df930be7Sderaadt 		vrele(fvp);
930df930be7Sderaadt 		return (error);
931df930be7Sderaadt 	}
932df930be7Sderaadt 
933b099d67bSprovos 	/*
934b099d67bSprovos 	 * If source and dest are the same, do nothing.
935b099d67bSprovos 	 */
936b099d67bSprovos 	if (tvp == fvp) {
937b099d67bSprovos 		error = 0;
938b099d67bSprovos 		goto abortit;
939b099d67bSprovos 	}
940b099d67bSprovos 
941df930be7Sderaadt 	/* */
9426e880534Svisa 	if ((error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY)) != 0)
943df930be7Sderaadt 		goto abortit;
944df930be7Sderaadt 	dp = VTODE(fdvp);
945df930be7Sderaadt 	ip = VTODE(fvp);
946df930be7Sderaadt 
947df930be7Sderaadt 	/*
948df930be7Sderaadt 	 * Be sure we are not renaming ".", "..", or an alias of ".". This
949df930be7Sderaadt 	 * leads to a crippled directory tree.  It's pretty tough to do a
950df930be7Sderaadt 	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
951df930be7Sderaadt 	 * doesn't work if the ".." entry is missing.
952df930be7Sderaadt 	 */
953df930be7Sderaadt 	if (ip->de_Attributes & ATTR_DIRECTORY) {
954df930be7Sderaadt 		/*
955df930be7Sderaadt 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
956df930be7Sderaadt 		 */
957df930be7Sderaadt 		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
958d56d3b9eSderaadt 		    dp == ip ||
959d56d3b9eSderaadt 		    (fcnp->cn_flags & ISDOTDOT) ||
960d56d3b9eSderaadt 		    (tcnp->cn_flags & ISDOTDOT) ||
961df930be7Sderaadt 		    (ip->de_flag & DE_RENAME)) {
96236bb23f1Svisa 			VOP_UNLOCK(fvp);
963df930be7Sderaadt 			error = EINVAL;
964df930be7Sderaadt 			goto abortit;
965df930be7Sderaadt 		}
966df930be7Sderaadt 		ip->de_flag |= DE_RENAME;
967df930be7Sderaadt 		doingdirectory++;
968df930be7Sderaadt 	}
9694eda5a5eSrobert 	VN_KNOTE(fdvp, NOTE_WRITE);	/* XXX right place? */
970df930be7Sderaadt 
971df930be7Sderaadt 	/*
972df930be7Sderaadt 	 * When the target exists, both the directory
973df930be7Sderaadt 	 * and target vnodes are returned locked.
974df930be7Sderaadt 	 */
975df930be7Sderaadt 	dp = VTODE(tdvp);
976df930be7Sderaadt 	xp = tvp ? VTODE(tvp) : NULL;
977df930be7Sderaadt 	/*
978df930be7Sderaadt 	 * Remember direntry place to use for destination
979df930be7Sderaadt 	 */
980df930be7Sderaadt 	to_diroffset = dp->de_fndoffset;
98116bf7bd1Sderaadt 	to_count = dp->de_fndcnt;
982df930be7Sderaadt 
983df930be7Sderaadt 	/*
984df930be7Sderaadt 	 * If ".." must be changed (ie the directory gets a new
985df930be7Sderaadt 	 * parent) then the source directory must not be in the
9862f4b598dStedu 	 * directory hierarchy above the target, as this would
987df930be7Sderaadt 	 * orphan everything below the source directory. Also
988df930be7Sderaadt 	 * the user must have write permission in the source so
989df930be7Sderaadt 	 * as to be able to change "..". We must repeat the call
990df930be7Sderaadt 	 * to namei, as the parent directory is unlocked by the
991df930be7Sderaadt 	 * call to doscheckpath().
992df930be7Sderaadt 	 */
993df930be7Sderaadt 	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
99436bb23f1Svisa 	VOP_UNLOCK(fvp);
995df930be7Sderaadt 	if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster)
996df930be7Sderaadt 		newparent = 1;
997df930be7Sderaadt 	vrele(fdvp);
998df930be7Sderaadt 	if (doingdirectory && newparent) {
999df930be7Sderaadt 		if (error)	/* write access check above */
1000c760a19bSart 			goto bad1;
1001df930be7Sderaadt 		if (xp != NULL)
1002df930be7Sderaadt 			vput(tvp);
100316bf7bd1Sderaadt 		/*
100416bf7bd1Sderaadt 		 * doscheckpath() vput()'s dp,
100516bf7bd1Sderaadt 		 * so we have to do a relookup afterwards
100616bf7bd1Sderaadt 		 */
1007879b3eabSniklas 		if ((error = doscheckpath(ip, dp)) != 0)
1008df930be7Sderaadt 			goto out;
1009df930be7Sderaadt 		if ((tcnp->cn_flags & SAVESTART) == 0)
1010df930be7Sderaadt 			panic("msdosfs_rename: lost to startdir");
10119baecc3aSthib 		if ((error = vfs_relookup(tdvp, &tvp, tcnp)) != 0)
1012df930be7Sderaadt 			goto out;
1013df930be7Sderaadt 		dp = VTODE(tdvp);
1014df930be7Sderaadt 		xp = tvp ? VTODE(tvp) : NULL;
1015df930be7Sderaadt 	}
1016df930be7Sderaadt 
10174eda5a5eSrobert 	VN_KNOTE(tdvp, NOTE_WRITE);
10184eda5a5eSrobert 
1019df930be7Sderaadt 	if (xp != NULL) {
1020df930be7Sderaadt 		/*
1021df930be7Sderaadt 		 * Target must be empty if a directory and have no links
1022df930be7Sderaadt 		 * to it. Also, ensure source and target are compatible
1023df930be7Sderaadt 		 * (both directories, or both not directories).
1024df930be7Sderaadt 		 */
1025df930be7Sderaadt 		if (xp->de_Attributes & ATTR_DIRECTORY) {
1026df930be7Sderaadt 			if (!dosdirempty(xp)) {
1027df930be7Sderaadt 				error = ENOTEMPTY;
1028c760a19bSart 				goto bad1;
1029df930be7Sderaadt 			}
1030df930be7Sderaadt 			if (!doingdirectory) {
1031df930be7Sderaadt 				error = ENOTDIR;
1032c760a19bSart 				goto bad1;
1033df930be7Sderaadt 			}
1034df930be7Sderaadt 			cache_purge(tdvp);
1035df930be7Sderaadt 		} else if (doingdirectory) {
1036df930be7Sderaadt 			error = EISDIR;
1037c760a19bSart 			goto bad1;
1038df930be7Sderaadt 		}
1039879b3eabSniklas 		if ((error = removede(dp, xp)) != 0)
1040c760a19bSart 			goto bad1;
10414eda5a5eSrobert 		VN_KNOTE(tvp, NOTE_DELETE);
1042df930be7Sderaadt 		vput(tvp);
1043df930be7Sderaadt 		xp = NULL;
1044df930be7Sderaadt 	}
1045df930be7Sderaadt 
1046df930be7Sderaadt 	/*
1047280a40ffSderaadt 	 * Convert the filename in tcnp into a dos filename. We copy this
1048280a40ffSderaadt 	 * into the denode and directory entry for the destination
1049280a40ffSderaadt 	 * file/directory.
1050280a40ffSderaadt 	 */
1051280a40ffSderaadt 	if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0)
1052c760a19bSart 		goto bad1;
1053280a40ffSderaadt 
1054280a40ffSderaadt 	/*
1055df930be7Sderaadt 	 * Since from wasn't locked at various places above,
1056df930be7Sderaadt 	 * have to do a relookup here.
1057df930be7Sderaadt 	 */
1058df930be7Sderaadt 	fcnp->cn_flags &= ~MODMASK;
1059df930be7Sderaadt 	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
1060df930be7Sderaadt 	if ((fcnp->cn_flags & SAVESTART) == 0)
1061df930be7Sderaadt 		panic("msdosfs_rename: lost from startdir");
1062df930be7Sderaadt 	if (!newparent)
106336bb23f1Svisa 		VOP_UNLOCK(tdvp);
10649baecc3aSthib 	(void) vfs_relookup(fdvp, &fvp, fcnp);
1065df930be7Sderaadt 	if (fvp == NULL) {
1066df930be7Sderaadt 		/*
1067df930be7Sderaadt 		 * From name has disappeared.
1068df930be7Sderaadt 		 */
1069df930be7Sderaadt 		if (doingdirectory)
1070df930be7Sderaadt 			panic("rename: lost dir entry");
1071df930be7Sderaadt 		vrele(ap->a_fvp);
1072df930be7Sderaadt 		if (newparent)
107336bb23f1Svisa 			VOP_UNLOCK(tdvp);
1074df930be7Sderaadt 		vrele(tdvp);
1075df930be7Sderaadt 		return 0;
1076df930be7Sderaadt 	}
1077df930be7Sderaadt 	xp = VTODE(fvp);
107816bf7bd1Sderaadt 	zp = VTODE(fdvp);
107916bf7bd1Sderaadt 	from_diroffset = zp->de_fndoffset;
1080df930be7Sderaadt 
1081df930be7Sderaadt 	/*
1082df930be7Sderaadt 	 * Ensure that the directory entry still exists and has not
1083df930be7Sderaadt 	 * changed till now. If the source is a file the entry may
1084df930be7Sderaadt 	 * have been unlinked or renamed. In either case there is
1085df930be7Sderaadt 	 * no further work to be done. If the source is a directory
1086df930be7Sderaadt 	 * then it cannot have been rmdir'ed or renamed; this is
1087df930be7Sderaadt 	 * prohibited by the DE_RENAME flag.
1088df930be7Sderaadt 	 */
1089df930be7Sderaadt 	if (xp != ip) {
1090df930be7Sderaadt 		if (doingdirectory)
1091df930be7Sderaadt 			panic("rename: lost dir entry");
1092df930be7Sderaadt 		vrele(ap->a_fvp);
1093df930be7Sderaadt 		if (newparent)
109436bb23f1Svisa 			VOP_UNLOCK(fdvp);
1095df930be7Sderaadt 		xp = NULL;
1096df930be7Sderaadt 	} else {
1097df930be7Sderaadt 		vrele(fvp);
1098df930be7Sderaadt 		xp = NULL;
1099df930be7Sderaadt 
1100df930be7Sderaadt 		/*
110116bf7bd1Sderaadt 		 * First write a new entry in the destination
1102df930be7Sderaadt 		 * directory and mark the entry in the source directory
1103df930be7Sderaadt 		 * as deleted.  Then move the denode to the correct hash
1104df930be7Sderaadt 		 * chain for its new location in the filesystem.  And, if
1105df930be7Sderaadt 		 * we moved a directory, then update its .. entry to point
1106df930be7Sderaadt 		 * to the new parent directory.
1107df930be7Sderaadt 		 */
1108df930be7Sderaadt 		bcopy(ip->de_Name, oldname, 11);
1109df930be7Sderaadt 		bcopy(toname, ip->de_Name, 11);	/* update denode */
1110df930be7Sderaadt 		dp->de_fndoffset = to_diroffset;
111116bf7bd1Sderaadt 		dp->de_fndcnt = to_count;
111214bf419fSkrw 		error = createde(ip, dp, NULL, tcnp);
1113879b3eabSniklas 		if (error) {
1114df930be7Sderaadt 			bcopy(oldname, ip->de_Name, 11);
111516bf7bd1Sderaadt 			if (newparent)
111636bb23f1Svisa 				VOP_UNLOCK(fdvp);
1117df930be7Sderaadt 			goto bad;
1118df930be7Sderaadt 		}
111916bf7bd1Sderaadt 		ip->de_refcnt++;
112016bf7bd1Sderaadt 		zp->de_fndoffset = from_diroffset;
1121879b3eabSniklas 		if ((error = removede(zp, ip)) != 0) {
1122df930be7Sderaadt 			/* XXX should really panic here, fs is corrupt */
112316bf7bd1Sderaadt 			if (newparent)
112436bb23f1Svisa 				VOP_UNLOCK(fdvp);
1125df930be7Sderaadt 			goto bad;
1126df930be7Sderaadt 		}
11275e895f77Spedro 
11285e895f77Spedro 		cache_purge(fvp);
11295e895f77Spedro 
1130df930be7Sderaadt 		if (!doingdirectory) {
1131879b3eabSniklas 			error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0,
1132879b3eabSniklas 				       &ip->de_dirclust, 0);
1133879b3eabSniklas 			if (error) {
113416bf7bd1Sderaadt 				/* XXX should really panic here, fs is corrupt */
113516bf7bd1Sderaadt 				if (newparent)
113636bb23f1Svisa 					VOP_UNLOCK(fdvp);
113716bf7bd1Sderaadt 				goto bad;
113816bf7bd1Sderaadt 			}
1139e53dbe28Skrw 			ip->de_diroffset = to_diroffset;
114016bf7bd1Sderaadt 			if (ip->de_dirclust != MSDOSFSROOT)
1141e53dbe28Skrw 				ip->de_diroffset &= pmp->pm_crbomask;
1142df930be7Sderaadt 		}
1143df930be7Sderaadt 		reinsert(ip);
114416bf7bd1Sderaadt 		if (newparent)
114536bb23f1Svisa 			VOP_UNLOCK(fdvp);
1146df930be7Sderaadt 	}
1147df930be7Sderaadt 
1148df930be7Sderaadt 	/*
1149df930be7Sderaadt 	 * If we moved a directory to a new parent directory, then we must
1150df930be7Sderaadt 	 * fixup the ".." entry in the moved directory.
1151df930be7Sderaadt 	 */
1152df930be7Sderaadt 	if (doingdirectory && newparent) {
1153df930be7Sderaadt 		cn = ip->de_StartCluster;
1154df930be7Sderaadt 		if (cn == MSDOSFSROOT) {
1155df930be7Sderaadt 			/* this should never happen */
115630ada397Smillert 			panic("msdosfs_rename: updating .. in root directory?");
1157df930be7Sderaadt 		} else
1158df930be7Sderaadt 			bn = cntobn(pmp, cn);
115993f62a9eStedu 		error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, &bp);
1160879b3eabSniklas 		if (error) {
1161df930be7Sderaadt 			/* XXX should really panic here, fs is corrupt */
116216bf7bd1Sderaadt 			brelse(bp);
1163df930be7Sderaadt 			goto bad;
1164df930be7Sderaadt 		}
1165a43f8970Stedu 		dotdotp = (struct direntry *)bp->b_data;
1166b05dc4d5Stom 		putushort(dotdotp[0].deStartCluster, cn);
1167a43f8970Stedu 		pcl = dp->de_StartCluster;
1168a43f8970Stedu 		if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1169a43f8970Stedu 			pcl = 0;
1170a43f8970Stedu 		putushort(dotdotp[1].deStartCluster, pcl);
1171a43f8970Stedu 		if (FAT32(pmp)) {
1172a43f8970Stedu 			putushort(dotdotp[0].deHighClust, cn >> 16);
1173a43f8970Stedu 			putushort(dotdotp[1].deHighClust, pcl >> 16);
1174a43f8970Stedu 		}
1175879b3eabSniklas 		if ((error = bwrite(bp)) != 0) {
1176df930be7Sderaadt 			/* XXX should really panic here, fs is corrupt */
1177df930be7Sderaadt 			goto bad;
1178df930be7Sderaadt 		}
1179df930be7Sderaadt 	}
1180df930be7Sderaadt 
11814eda5a5eSrobert 	VN_KNOTE(fvp, NOTE_RENAME);
11824eda5a5eSrobert 
1183df930be7Sderaadt bad:
118436bb23f1Svisa 	VOP_UNLOCK(fvp);
1185c760a19bSart 	vrele(fdvp);
1186c760a19bSart bad1:
1187df930be7Sderaadt 	if (xp)
1188df930be7Sderaadt 		vput(tvp);
1189df930be7Sderaadt 	vput(tdvp);
1190df930be7Sderaadt out:
1191df930be7Sderaadt 	ip->de_flag &= ~DE_RENAME;
1192df930be7Sderaadt 	vrele(fvp);
1193df930be7Sderaadt 	return (error);
1194df930be7Sderaadt 
1195df930be7Sderaadt }
1196df930be7Sderaadt 
1197df930be7Sderaadt struct {
1198df930be7Sderaadt 	struct direntry dot;
1199df930be7Sderaadt 	struct direntry dotdot;
1200df930be7Sderaadt } dosdirtemplate = {
1201879b3eabSniklas 	{	".       ", "   ",			/* the . entry */
1202df930be7Sderaadt 		ATTR_DIRECTORY,				/* file attribute */
1203bae0976aSderaadt 		CASE_LOWER_BASE | CASE_LOWER_EXT,	/* lower case */
1204bae0976aSderaadt 		0,					/* create time 100ths */
1205879b3eabSniklas 		{ 0, 0 }, { 0, 0 },			/* create time & date */
1206bae0976aSderaadt 		{ 0, 0 },				/* access date */
1207b099d67bSprovos 		{ 0, 0 },				/* high bits of start cluster */
1208879b3eabSniklas 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1209879b3eabSniklas 		{ 0, 0 },				/* startcluster */
1210879b3eabSniklas 		{ 0, 0, 0, 0 }				/* filesize */
1211879b3eabSniklas 	},
1212879b3eabSniklas 	{	"..      ", "   ",			/* the .. entry */
1213df930be7Sderaadt 		ATTR_DIRECTORY,				/* file attribute */
1214bae0976aSderaadt 		CASE_LOWER_BASE | CASE_LOWER_EXT,	/* lower case */
1215bae0976aSderaadt 		0,					/* create time 100ths */
1216879b3eabSniklas 		{ 0, 0 }, { 0, 0 },			/* create time & date */
1217bae0976aSderaadt 		{ 0, 0 },				/* access date */
1218b099d67bSprovos 		{ 0, 0 },				/* high bits of start cluster */
1219879b3eabSniklas 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1220879b3eabSniklas 		{ 0, 0 },				/* startcluster */
1221879b3eabSniklas 		{ 0, 0, 0, 0 }				/* filesize */
1222879b3eabSniklas 	}
1223df930be7Sderaadt };
1224df930be7Sderaadt 
1225df930be7Sderaadt int
12267d80fe84Sjasper msdosfs_mkdir(void *v)
1227879b3eabSniklas {
122899bc9d31Sderaadt 	struct vop_mkdir_args *ap = v;
1229df930be7Sderaadt 	struct componentname *cnp = ap->a_cnp;
1230df930be7Sderaadt 	struct denode ndirent;
1231df930be7Sderaadt 	struct denode *dep;
1232df930be7Sderaadt 	struct denode *pdep = VTODE(ap->a_dvp);
1233df930be7Sderaadt 	int error;
12341abdbfdeSderaadt 	daddr_t bn;
123582fa9538Stedu 	uint32_t newcluster, pcl;
1236df930be7Sderaadt 	struct direntry *denp;
1237df930be7Sderaadt 	struct msdosfsmount *pmp = pdep->de_pmp;
1238df930be7Sderaadt 	struct buf *bp;
123978b4a1c4Smillert 	struct timespec ts;
1240df930be7Sderaadt 
1241df930be7Sderaadt 	/*
1242df930be7Sderaadt 	 * If this is the root directory and there is no space left we
1243df930be7Sderaadt 	 * can't do anything.  This is because the root directory can not
1244df930be7Sderaadt 	 * change size.
1245df930be7Sderaadt 	 */
124616bf7bd1Sderaadt 	if (pdep->de_StartCluster == MSDOSFSROOT
124716bf7bd1Sderaadt 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
1248df930be7Sderaadt 		error = ENOSPC;
1249df930be7Sderaadt 		goto bad2;
1250df930be7Sderaadt 	}
1251df930be7Sderaadt 
1252df930be7Sderaadt 	/*
1253df930be7Sderaadt 	 * Allocate a cluster to hold the about to be created directory.
1254df930be7Sderaadt 	 */
1255d3889036Ssf 	error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
1256879b3eabSniklas 	if (error)
1257df930be7Sderaadt 		goto bad2;
1258df930be7Sderaadt 
1259df930be7Sderaadt 	bzero(&ndirent, sizeof(ndirent));
126078b4a1c4Smillert 	ndirent.de_pmp = pmp;
126178b4a1c4Smillert 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
1262c2475275Stholo 	getnanotime(&ts);
126378b4a1c4Smillert 	DETIMES(&ndirent, &ts, &ts, &ts);
1264df930be7Sderaadt 
1265df930be7Sderaadt 	/*
1266df930be7Sderaadt 	 * Now fill the cluster with the "." and ".." entries. And write
1267df930be7Sderaadt 	 * the cluster to disk.  This way it is there for the parent
1268df930be7Sderaadt 	 * directory to be pointing at if there were a crash.
1269df930be7Sderaadt 	 */
1270df930be7Sderaadt 	bn = cntobn(pmp, newcluster);
1271df930be7Sderaadt 	/* always succeeds */
1272570df5c4Scheloha 	bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, INFSLP);
1273df930be7Sderaadt 	bzero(bp->b_data, pmp->pm_bpcluster);
1274df930be7Sderaadt 	bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
1275df930be7Sderaadt 	denp = (struct direntry *)bp->b_data;
1276df930be7Sderaadt 	putushort(denp[0].deStartCluster, newcluster);
127716bf7bd1Sderaadt 	putushort(denp[0].deCDate, ndirent.de_CDate);
127816bf7bd1Sderaadt 	putushort(denp[0].deCTime, ndirent.de_CTime);
1279bae0976aSderaadt 	denp[0].deCTimeHundredth = ndirent.de_CTimeHundredth;
128016bf7bd1Sderaadt 	putushort(denp[0].deADate, ndirent.de_ADate);
128116bf7bd1Sderaadt 	putushort(denp[0].deMDate, ndirent.de_MDate);
128216bf7bd1Sderaadt 	putushort(denp[0].deMTime, ndirent.de_MTime);
1283b099d67bSprovos 	pcl = pdep->de_StartCluster;
1284b099d67bSprovos 	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
1285b099d67bSprovos 		pcl = 0;
1286b099d67bSprovos 	putushort(denp[1].deStartCluster, pcl);
128716bf7bd1Sderaadt 	putushort(denp[1].deCDate, ndirent.de_CDate);
128816bf7bd1Sderaadt 	putushort(denp[1].deCTime, ndirent.de_CTime);
1289bae0976aSderaadt 	denp[1].deCTimeHundredth = ndirent.de_CTimeHundredth;
129016bf7bd1Sderaadt 	putushort(denp[1].deADate, ndirent.de_ADate);
129116bf7bd1Sderaadt 	putushort(denp[1].deMDate, ndirent.de_MDate);
129216bf7bd1Sderaadt 	putushort(denp[1].deMTime, ndirent.de_MTime);
1293b099d67bSprovos 	if (FAT32(pmp)) {
1294b099d67bSprovos 		putushort(denp[0].deHighClust, newcluster >> 16);
1295b099d67bSprovos 		putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
1296b099d67bSprovos 	}
1297b099d67bSprovos 
1298879b3eabSniklas 	if ((error = bwrite(bp)) != 0)
1299df930be7Sderaadt 		goto bad;
1300df930be7Sderaadt 
1301df930be7Sderaadt 	/*
1302df930be7Sderaadt 	 * Now build up a directory entry pointing to the newly allocated
1303df930be7Sderaadt 	 * cluster.  This will be written to an empty slot in the parent
1304df930be7Sderaadt 	 * directory.
1305df930be7Sderaadt 	 */
1306df930be7Sderaadt #ifdef DIAGNOSTIC
1307df930be7Sderaadt 	if ((cnp->cn_flags & HASBUF) == 0)
1308df930be7Sderaadt 		panic("msdosfs_mkdir: no name");
1309df930be7Sderaadt #endif
1310879b3eabSniklas 	if ((error = uniqdosname(pdep, cnp, ndirent.de_Name)) != 0)
131116bf7bd1Sderaadt 		goto bad;
131216bf7bd1Sderaadt 
1313df930be7Sderaadt 	ndirent.de_Attributes = ATTR_DIRECTORY;
1314df930be7Sderaadt 	ndirent.de_StartCluster = newcluster;
1315df930be7Sderaadt 	ndirent.de_FileSize = 0;
1316df930be7Sderaadt 	ndirent.de_dev = pdep->de_dev;
1317df930be7Sderaadt 	ndirent.de_devvp = pdep->de_devvp;
1318879b3eabSniklas 	if ((error = createde(&ndirent, pdep, &dep, cnp)) != 0)
1319df930be7Sderaadt 		goto bad;
1320df930be7Sderaadt 	if ((cnp->cn_flags & SAVESTART) == 0)
1321dbe27ba0Stedu 		pool_put(&namei_pool, cnp->cn_pnbuf);
13224eda5a5eSrobert 	VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
1323df930be7Sderaadt 	vput(ap->a_dvp);
1324df930be7Sderaadt 	*ap->a_vpp = DETOV(dep);
1325df930be7Sderaadt 	return (0);
1326df930be7Sderaadt 
1327df930be7Sderaadt bad:
1328df930be7Sderaadt 	clusterfree(pmp, newcluster, NULL);
1329df930be7Sderaadt bad2:
1330dbe27ba0Stedu 	pool_put(&namei_pool, cnp->cn_pnbuf);
1331df930be7Sderaadt 	vput(ap->a_dvp);
1332df930be7Sderaadt 	return (error);
1333df930be7Sderaadt }
1334df930be7Sderaadt 
1335df930be7Sderaadt int
13367d80fe84Sjasper msdosfs_rmdir(void *v)
1337879b3eabSniklas {
133899bc9d31Sderaadt 	struct vop_rmdir_args *ap = v;
1339cc2a7e83Sjasper 	struct vnode *vp = ap->a_vp;
1340cc2a7e83Sjasper 	struct vnode *dvp = ap->a_dvp;
1341cc2a7e83Sjasper 	struct componentname *cnp = ap->a_cnp;
1342cc2a7e83Sjasper 	struct denode *ip, *dp;
1343df930be7Sderaadt 	int error;
1344df930be7Sderaadt 
1345df930be7Sderaadt 	ip = VTODE(vp);
1346df930be7Sderaadt 	dp = VTODE(dvp);
1347df930be7Sderaadt 	/*
1348df930be7Sderaadt 	 * Verify the directory is empty (and valid).
1349df930be7Sderaadt 	 * (Rmdir ".." won't be valid since
1350df930be7Sderaadt 	 *  ".." will contain a reference to
1351df930be7Sderaadt 	 *  the current directory and thus be
1352df930be7Sderaadt 	 *  non-empty.)
1353df930be7Sderaadt 	 */
1354df930be7Sderaadt 	error = 0;
1355df930be7Sderaadt 	if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) {
1356df930be7Sderaadt 		error = ENOTEMPTY;
1357df930be7Sderaadt 		goto out;
1358df930be7Sderaadt 	}
13594eda5a5eSrobert 
13604eda5a5eSrobert 	VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
13614eda5a5eSrobert 
1362df930be7Sderaadt 	/*
1363df930be7Sderaadt 	 * Delete the entry from the directory.  For dos filesystems this
1364df930be7Sderaadt 	 * gets rid of the directory entry on disk, the in memory copy
1365df930be7Sderaadt 	 * still exists but the de_refcnt is <= 0.  This prevents it from
1366df930be7Sderaadt 	 * being found by deget().  When the vput() on dep is done we give
1367df930be7Sderaadt 	 * up access and eventually msdosfs_reclaim() will be called which
1368df930be7Sderaadt 	 * will remove it from the denode cache.
1369df930be7Sderaadt 	 */
1370879b3eabSniklas 	if ((error = removede(dp, ip)) != 0)
1371df930be7Sderaadt 		goto out;
1372df930be7Sderaadt 	/*
1373df930be7Sderaadt 	 * This is where we decrement the link count in the parent
1374df930be7Sderaadt 	 * directory.  Since dos filesystems don't do this we just purge
1375df930be7Sderaadt 	 * the name cache and let go of the parent directory denode.
1376df930be7Sderaadt 	 */
1377df930be7Sderaadt 	cache_purge(dvp);
1378df930be7Sderaadt 	vput(dvp);
1379df930be7Sderaadt 	dvp = NULL;
1380df930be7Sderaadt 	/*
1381df930be7Sderaadt 	 * Truncate the directory that is being deleted.
1382df930be7Sderaadt 	 */
138382fa9538Stedu 	error = detrunc(ip, (uint32_t)0, IO_SYNC, cnp->cn_cred, cnp->cn_proc);
1384df930be7Sderaadt 	cache_purge(vp);
1385df930be7Sderaadt out:
1386df930be7Sderaadt 	if (dvp)
1387df930be7Sderaadt 		vput(dvp);
13884eda5a5eSrobert 	VN_KNOTE(vp, NOTE_DELETE);
1389df930be7Sderaadt 	vput(vp);
1390df930be7Sderaadt 	return (error);
1391df930be7Sderaadt }
1392df930be7Sderaadt 
1393df930be7Sderaadt /*
1394df930be7Sderaadt  * DOS filesystems don't know what symlinks are.
1395df930be7Sderaadt  */
1396df930be7Sderaadt int
13977d80fe84Sjasper msdosfs_symlink(void *v)
1398879b3eabSniklas {
139999bc9d31Sderaadt 	struct vop_symlink_args *ap = v;
1400df930be7Sderaadt 
1401879b3eabSniklas 	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
1402879b3eabSniklas 	vput(ap->a_dvp);
1403879b3eabSniklas 	return (EOPNOTSUPP);
1404df930be7Sderaadt }
1405df930be7Sderaadt 
1406df930be7Sderaadt int
14077d80fe84Sjasper msdosfs_readdir(void *v)
1408879b3eabSniklas {
140999bc9d31Sderaadt 	struct vop_readdir_args *ap = v;
1410df930be7Sderaadt 	int error = 0;
1411df930be7Sderaadt 	int diff;
1412df930be7Sderaadt 	long n;
1413df930be7Sderaadt 	int blsize;
1414df930be7Sderaadt 	long on;
1415df930be7Sderaadt 	long lost;
1416df930be7Sderaadt 	long count;
141782fa9538Stedu 	uint32_t dirsperblk;
141885716540Sderaadt 	uint32_t cn, lbn;
141982fa9538Stedu 	uint32_t fileno;
1420df930be7Sderaadt 	long bias = 0;
14211abdbfdeSderaadt 	daddr_t bn;
1422df930be7Sderaadt 	struct buf *bp;
1423df930be7Sderaadt 	struct denode *dep = VTODE(ap->a_vp);
1424df930be7Sderaadt 	struct msdosfsmount *pmp = dep->de_pmp;
1425df930be7Sderaadt 	struct direntry *dentp;
1426df930be7Sderaadt 	struct dirent dirbuf;
1427df930be7Sderaadt 	struct uio *uio = ap->a_uio;
142811d958bbSpedro 	off_t offset, wlast = -1;
142916bf7bd1Sderaadt 	int chksum = -1;
1430df930be7Sderaadt 
1431df930be7Sderaadt #ifdef MSDOSFS_DEBUG
143281ad5374Skrw 	printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
1433df930be7Sderaadt 	    ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
1434df930be7Sderaadt #endif
1435df930be7Sderaadt 
1436df930be7Sderaadt 	/*
1437df930be7Sderaadt 	 * msdosfs_readdir() won't operate properly on regular files since
1438135ee272Sderaadt 	 * it does i/o only with the filesystem vnode, and hence can
1439df930be7Sderaadt 	 * retrieve the wrong block from the buffer cache for a plain file.
1440df930be7Sderaadt 	 * So, fail attempts to readdir() on a plain file.
1441df930be7Sderaadt 	 */
1442df930be7Sderaadt 	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1443df930be7Sderaadt 		return (ENOTDIR);
1444df930be7Sderaadt 
1445df930be7Sderaadt 	/*
144616bf7bd1Sderaadt 	 * To be safe, initialize dirbuf
144716bf7bd1Sderaadt 	 */
1448ebee68f3Sguenther 	bzero(&dirbuf, sizeof(dirbuf));
144916bf7bd1Sderaadt 
145016bf7bd1Sderaadt 	/*
1451df930be7Sderaadt 	 * If the user buffer is smaller than the size of one dos directory
1452df930be7Sderaadt 	 * entry or the file offset is not a multiple of the size of a
1453df930be7Sderaadt 	 * directory entry, then we fail the read.
1454df930be7Sderaadt 	 */
1455df930be7Sderaadt 	count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
1456df930be7Sderaadt 	offset = uio->uio_offset;
1457df930be7Sderaadt 	if (count < sizeof(struct direntry) ||
1458df930be7Sderaadt 	    (offset & (sizeof(struct direntry) - 1)))
1459df930be7Sderaadt 		return (EINVAL);
1460df930be7Sderaadt 	lost = uio->uio_resid - count;
1461df930be7Sderaadt 	uio->uio_resid = count;
1462df930be7Sderaadt 
1463b099d67bSprovos 	dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
1464b099d67bSprovos 
1465df930be7Sderaadt 	/*
1466df930be7Sderaadt 	 * If they are reading from the root directory then, we simulate
1467df930be7Sderaadt 	 * the . and .. entries since these don't exist in the root
1468df930be7Sderaadt 	 * directory.  We also set the offset bias to make up for having to
1469df930be7Sderaadt 	 * simulate these entries. By this I mean that at file offset 64 we
1470df930be7Sderaadt 	 * read the first entry in the root directory that lives on disk.
1471df930be7Sderaadt 	 */
1472b099d67bSprovos 	if (dep->de_StartCluster == MSDOSFSROOT
1473b099d67bSprovos 	    || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
147478b4a1c4Smillert #if 0
147578b4a1c4Smillert 		printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
147678b4a1c4Smillert 		    offset);
147778b4a1c4Smillert #endif
1478df930be7Sderaadt 		bias = 2 * sizeof(struct direntry);
1479df930be7Sderaadt 		if (offset < bias) {
1480df930be7Sderaadt 			for (n = (int)offset / sizeof(struct direntry);
1481df930be7Sderaadt 			     n < 2; n++) {
1482b099d67bSprovos 			        if (FAT32(pmp))
1483979947e6Stom 				        dirbuf.d_fileno = pmp->pm_rootdirblk;
1484b099d67bSprovos 				else
1485df930be7Sderaadt 				        dirbuf.d_fileno = 1;
1486df930be7Sderaadt 				dirbuf.d_type = DT_DIR;
1487df930be7Sderaadt 				switch (n) {
1488df930be7Sderaadt 				case 0:
1489df930be7Sderaadt 					dirbuf.d_namlen = 1;
1490636aa9a4Sderaadt 					strlcpy(dirbuf.d_name, ".",
1491636aa9a4Sderaadt 					    sizeof dirbuf.d_name);
1492df930be7Sderaadt 					break;
1493df930be7Sderaadt 				case 1:
1494df930be7Sderaadt 					dirbuf.d_namlen = 2;
1495636aa9a4Sderaadt 					strlcpy(dirbuf.d_name, "..",
1496636aa9a4Sderaadt 					    sizeof dirbuf.d_name);
1497df930be7Sderaadt 					break;
1498df930be7Sderaadt 				}
14990c0430f8Sniklas 				dirbuf.d_reclen = DIRENT_SIZE(&dirbuf);
150091a535ffSguenther 				dirbuf.d_off = offset +
150191a535ffSguenther 				    sizeof(struct direntry);
1502df930be7Sderaadt 				if (uio->uio_resid < dirbuf.d_reclen)
1503df930be7Sderaadt 					goto out;
150403e5328eSstefan 				error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1505879b3eabSniklas 				if (error)
1506df930be7Sderaadt 					goto out;
150791a535ffSguenther 				offset = dirbuf.d_off;
1508df930be7Sderaadt 			}
1509df930be7Sderaadt 		}
1510df930be7Sderaadt 	}
1511df930be7Sderaadt 
1512df930be7Sderaadt 	while (uio->uio_resid > 0) {
151316bf7bd1Sderaadt 		lbn = de_cluster(pmp, offset - bias);
1514df930be7Sderaadt 		on = (offset - bias) & pmp->pm_crbomask;
15151414b0faSart 		n = min(pmp->pm_bpcluster - on, uio->uio_resid);
1516df930be7Sderaadt 		diff = dep->de_FileSize - (offset - bias);
1517df930be7Sderaadt 		if (diff <= 0)
1518df930be7Sderaadt 			break;
15191414b0faSart 		n = min(n, diff);
1520879b3eabSniklas 		if ((error = pcbmap(dep, lbn, &bn, &cn, &blsize)) != 0)
1521df930be7Sderaadt 			break;
152293f62a9eStedu 		error = bread(pmp->pm_devvp, bn, blsize, &bp);
1523879b3eabSniklas 		if (error) {
1524df930be7Sderaadt 			brelse(bp);
1525df930be7Sderaadt 			return (error);
1526df930be7Sderaadt 		}
15271414b0faSart 		n = min(n, blsize - bp->b_resid);
1528df930be7Sderaadt 
1529df930be7Sderaadt 		/*
1530df930be7Sderaadt 		 * Convert from dos directory entries to fs-independent
1531df930be7Sderaadt 		 * directory entries.
1532df930be7Sderaadt 		 */
1533df930be7Sderaadt 		for (dentp = (struct direntry *)(bp->b_data + on);
1534df930be7Sderaadt 		     (char *)dentp < bp->b_data + on + n;
153516bf7bd1Sderaadt 		     dentp++, offset += sizeof(struct direntry)) {
153678b4a1c4Smillert #if 0
153778b4a1c4Smillert 			printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
153878b4a1c4Smillert 			    dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
153978b4a1c4Smillert #endif
1540df930be7Sderaadt 			/*
1541df930be7Sderaadt 			 * If this is an unused entry, we can stop.
1542df930be7Sderaadt 			 */
1543df930be7Sderaadt 			if (dentp->deName[0] == SLOT_EMPTY) {
1544df930be7Sderaadt 				brelse(bp);
1545df930be7Sderaadt 				goto out;
1546df930be7Sderaadt 			}
1547df930be7Sderaadt 			/*
154816bf7bd1Sderaadt 			 * Skip deleted entries.
1549df930be7Sderaadt 			 */
155016bf7bd1Sderaadt 			if (dentp->deName[0] == SLOT_DELETED) {
155116bf7bd1Sderaadt 				chksum = -1;
155211d958bbSpedro 				wlast = -1;
155316bf7bd1Sderaadt 				continue;
155416bf7bd1Sderaadt 			}
155516bf7bd1Sderaadt 
155616bf7bd1Sderaadt 			/*
155716bf7bd1Sderaadt 			 * Handle Win95 long directory entries
155816bf7bd1Sderaadt 			 */
155916bf7bd1Sderaadt 			if (dentp->deAttributes == ATTR_WIN95) {
156011d958bbSpedro 				struct winentry *wep;
156116bf7bd1Sderaadt 				if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
156216bf7bd1Sderaadt 					continue;
156311d958bbSpedro 				wep = (struct winentry *)dentp;
156411d958bbSpedro 				chksum = win2unixfn(wep, &dirbuf, chksum);
156511d958bbSpedro 				if (wep->weCnt & WIN_LAST)
156611d958bbSpedro 					wlast = offset;
156716bf7bd1Sderaadt 				continue;
156816bf7bd1Sderaadt 			}
156916bf7bd1Sderaadt 
157016bf7bd1Sderaadt 			/*
157116bf7bd1Sderaadt 			 * Skip volume labels
157216bf7bd1Sderaadt 			 */
157316bf7bd1Sderaadt 			if (dentp->deAttributes & ATTR_VOLUME) {
157416bf7bd1Sderaadt 				chksum = -1;
157511d958bbSpedro 				wlast = -1;
1576df930be7Sderaadt 				continue;
1577df930be7Sderaadt 			}
1578979947e6Stom 
1579df930be7Sderaadt 			/*
1580df930be7Sderaadt 			 * This computation of d_fileno must match
1581df930be7Sderaadt 			 * the computation of va_fileid in
1582df930be7Sderaadt 			 * msdosfs_getattr.
1583df930be7Sderaadt 			 */
1584df930be7Sderaadt 			fileno = getushort(dentp->deStartCluster);
1585b099d67bSprovos 			if (FAT32(pmp))
1586979947e6Stom 			    fileno |= getushort(dentp->deHighClust) << 16;
1587979947e6Stom 
1588979947e6Stom 			if (dentp->deAttributes & ATTR_DIRECTORY) {
1589979947e6Stom 				/* Special-case root */
1590979947e6Stom 				if (fileno == MSDOSFSROOT)  {
159176d2c29eSniklas 					fileno = FAT32(pmp) ?
1592979947e6Stom 					    pmp->pm_rootdirblk : 1;
1593979947e6Stom 				}
1594979947e6Stom 
1595b099d67bSprovos 				dirbuf.d_fileno = fileno;
1596b099d67bSprovos 				dirbuf.d_type = DT_DIR;
1597df930be7Sderaadt 			} else {
1598979947e6Stom 				if (getulong(dentp->deFileSize) == 0) {
1599979947e6Stom 					uint64_t fileno64;
1600979947e6Stom 
1601979947e6Stom 					fileno64 = (cn == MSDOSFSROOT) ?
1602979947e6Stom 					    roottobn(pmp, 0) : cntobn(pmp, cn);
1603979947e6Stom 
1604979947e6Stom 					fileno64 *= dirsperblk;
1605979947e6Stom 					fileno64 += dentp -
1606979947e6Stom 					    (struct direntry *)bp->b_data;
1607979947e6Stom 
1608979947e6Stom 					fileno = fileidhash(fileno64);
1609979947e6Stom 				}
1610979947e6Stom 
1611df930be7Sderaadt 				dirbuf.d_fileno = fileno;
1612b099d67bSprovos 				dirbuf.d_type = DT_REG;
1613b099d67bSprovos 			}
1614979947e6Stom 
161516bf7bd1Sderaadt 			if (chksum != winChksum(dentp->deName))
1616df930be7Sderaadt 				dirbuf.d_namlen = dos2unixfn(dentp->deName,
161716bf7bd1Sderaadt 				    (u_char *)dirbuf.d_name,
161816bf7bd1Sderaadt 				    pmp->pm_flags & MSDOSFSMNT_SHORTNAME);
161916bf7bd1Sderaadt 			else
162016bf7bd1Sderaadt 				dirbuf.d_name[dirbuf.d_namlen] = 0;
162116bf7bd1Sderaadt 			chksum = -1;
16220c0430f8Sniklas 			dirbuf.d_reclen = DIRENT_SIZE(&dirbuf);
162391a535ffSguenther 			dirbuf.d_off = offset + sizeof(struct direntry);
1624df930be7Sderaadt 			if (uio->uio_resid < dirbuf.d_reclen) {
1625df930be7Sderaadt 				brelse(bp);
162611d958bbSpedro 				/* Remember long-name offset. */
162711d958bbSpedro 				if (wlast != -1)
162811d958bbSpedro 					offset = wlast;
1629df930be7Sderaadt 				goto out;
1630df930be7Sderaadt 			}
163111d958bbSpedro 			wlast = -1;
163203e5328eSstefan 			error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1633879b3eabSniklas 			if (error) {
1634df930be7Sderaadt 				brelse(bp);
1635df930be7Sderaadt 				goto out;
1636df930be7Sderaadt 			}
1637df930be7Sderaadt 		}
1638df930be7Sderaadt 		brelse(bp);
1639df930be7Sderaadt 	}
1640df930be7Sderaadt 
1641df930be7Sderaadt out:
1642df930be7Sderaadt 	uio->uio_offset = offset;
1643df930be7Sderaadt 	uio->uio_resid += lost;
1644df930be7Sderaadt 	if (dep->de_FileSize - (offset - bias) <= 0)
1645df930be7Sderaadt 		*ap->a_eofflag = 1;
1646df930be7Sderaadt 	else
1647df930be7Sderaadt 		*ap->a_eofflag = 0;
1648df930be7Sderaadt 	return (error);
1649df930be7Sderaadt }
1650df930be7Sderaadt 
1651df930be7Sderaadt /*
1652df930be7Sderaadt  * DOS filesystems don't know what symlinks are.
1653df930be7Sderaadt  */
1654df930be7Sderaadt int
16557d80fe84Sjasper msdosfs_readlink(void *v)
1656879b3eabSniklas {
1657879b3eabSniklas #if 0
1658df930be7Sderaadt 	struct vop_readlink_args /* {
1659df930be7Sderaadt 		struct vnode *a_vp;
1660df930be7Sderaadt 		struct uio *a_uio;
1661df930be7Sderaadt 		struct ucred *a_cred;
1662df930be7Sderaadt 	} */ *ap;
1663879b3eabSniklas #endif
1664df930be7Sderaadt 
1665df930be7Sderaadt 	return (EINVAL);
1666df930be7Sderaadt }
1667df930be7Sderaadt 
1668df930be7Sderaadt int
16697d80fe84Sjasper msdosfs_lock(void *v)
1670879b3eabSniklas {
167199bc9d31Sderaadt 	struct vop_lock_args *ap = v;
167207feb63cScsapuntz 	struct vnode *vp = ap->a_vp;
1673df930be7Sderaadt 
167426b8ec94Snatano 	return rrw_enter(&VTODE(vp)->de_lock, ap->a_flags & LK_RWFLAGS);
1675df930be7Sderaadt }
1676df930be7Sderaadt 
1677df930be7Sderaadt int
16787d80fe84Sjasper msdosfs_unlock(void *v)
1679879b3eabSniklas {
168099bc9d31Sderaadt 	struct vop_unlock_args *ap = v;
168107feb63cScsapuntz 	struct vnode *vp = ap->a_vp;
1682df930be7Sderaadt 
168326b8ec94Snatano 	rrw_exit(&VTODE(vp)->de_lock);
168426b8ec94Snatano 	return 0;
1685df930be7Sderaadt }
1686df930be7Sderaadt 
1687df930be7Sderaadt int
16887d80fe84Sjasper msdosfs_islocked(void *v)
1689879b3eabSniklas {
169099bc9d31Sderaadt 	struct vop_islocked_args *ap = v;
1691df930be7Sderaadt 
169226b8ec94Snatano 	return rrw_status(&VTODE(ap->a_vp)->de_lock);
1693df930be7Sderaadt }
1694df930be7Sderaadt 
1695df930be7Sderaadt /*
1696df930be7Sderaadt  * vp  - address of vnode file the file
169790aa86e4Smpi  * bn  - which cluster we are interested in mapping to a filesystem block number
1698df930be7Sderaadt  * vpp - returns the vnode for the block special file holding the filesystem
1699df930be7Sderaadt  *	 containing the file of interest
1700df930be7Sderaadt  * bnp - address of where to return the filesystem relative block number
1701df930be7Sderaadt  */
1702df930be7Sderaadt int
17037d80fe84Sjasper msdosfs_bmap(void *v)
1704879b3eabSniklas {
170599bc9d31Sderaadt 	struct vop_bmap_args *ap = v;
1706df930be7Sderaadt 	struct denode *dep = VTODE(ap->a_vp);
170790aa86e4Smpi 	uint32_t cn;
1708df930be7Sderaadt 
1709df930be7Sderaadt 	if (ap->a_vpp != NULL)
1710df930be7Sderaadt 		*ap->a_vpp = dep->de_devvp;
1711df930be7Sderaadt 	if (ap->a_bnp == NULL)
1712df930be7Sderaadt 		return (0);
171390aa86e4Smpi 
171490aa86e4Smpi 	cn = ap->a_bn;
171590aa86e4Smpi 	if (cn != ap->a_bn)
171690aa86e4Smpi 		return (EFBIG);
171790aa86e4Smpi 
171890aa86e4Smpi 	return (msdosfs_bmaparray(ap->a_vp, cn, ap->a_bnp, ap->a_runp));
1719f4aaab5fSsf }
172090aa86e4Smpi 
172190aa86e4Smpi int
172290aa86e4Smpi msdosfs_bmaparray(struct vnode *vp, uint32_t cn, daddr_t *bnp, int *runp)
172390aa86e4Smpi {
172490aa86e4Smpi 	struct denode *dep = VTODE(vp);
172590aa86e4Smpi 	struct msdosfsmount *pmp = dep->de_pmp;
172690aa86e4Smpi 	struct mount *mp;
172790aa86e4Smpi 	int error, maxrun = 0, run;
172890aa86e4Smpi 	daddr_t runbn;
172990aa86e4Smpi 
173090aa86e4Smpi 	mp = vp->v_mount;
173190aa86e4Smpi 
173290aa86e4Smpi 	if (runp) {
173390aa86e4Smpi 		/*
173490aa86e4Smpi 		 * XXX
173590aa86e4Smpi 		 * If MAXBSIZE is the largest transfer the disks can handle,
173690aa86e4Smpi 		 * we probably want maxrun to be 1 block less so that we
173790aa86e4Smpi 		 * don't create a block larger than the device can handle.
173890aa86e4Smpi 		 */
173990aa86e4Smpi 		*runp = 0;
174090aa86e4Smpi 		maxrun = min(MAXBSIZE / mp->mnt_stat.f_iosize - 1,
174190aa86e4Smpi 		    pmp->pm_maxcluster - cn);
174290aa86e4Smpi 	}
174390aa86e4Smpi 
174490aa86e4Smpi 	if ((error = pcbmap(dep, cn, bnp, 0, 0)) != 0)
174590aa86e4Smpi 		return (error);
174690aa86e4Smpi 
174790aa86e4Smpi 	for (run = 1; run <= maxrun; run++) {
174890aa86e4Smpi 		error = pcbmap(dep, cn + run, &runbn, 0, 0);
174990aa86e4Smpi 		if (error != 0 || (runbn != *bnp + de_cn2bn(pmp, run)))
175090aa86e4Smpi 			break;
175190aa86e4Smpi 	}
175290aa86e4Smpi 
175390aa86e4Smpi 	if (runp)
175490aa86e4Smpi 		*runp = run - 1;
175590aa86e4Smpi 
175690aa86e4Smpi 	return (0);
1757df930be7Sderaadt }
1758df930be7Sderaadt 
1759df930be7Sderaadt int
17607d80fe84Sjasper msdosfs_strategy(void *v)
1761879b3eabSniklas {
176299bc9d31Sderaadt 	struct vop_strategy_args *ap = v;
1763df930be7Sderaadt 	struct buf *bp = ap->a_bp;
1764df930be7Sderaadt 	struct denode *dep = VTODE(bp->b_vp);
1765df930be7Sderaadt 	struct vnode *vp;
1766df930be7Sderaadt 	int error = 0;
17674335127fSart 	int s;
1768df930be7Sderaadt 
1769df930be7Sderaadt 	if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR)
1770df930be7Sderaadt 		panic("msdosfs_strategy: spec");
1771df930be7Sderaadt 	/*
1772df930be7Sderaadt 	 * If we don't already know the filesystem relative block number
1773df930be7Sderaadt 	 * then get it using pcbmap().  If pcbmap() returns the block
1774df930be7Sderaadt 	 * number as -1 then we've got a hole in the file.  DOS filesystems
1775df930be7Sderaadt 	 * don't allow files with holes, so we shouldn't ever see this.
1776df930be7Sderaadt 	 */
1777df930be7Sderaadt 	if (bp->b_blkno == bp->b_lblkno) {
177890aa86e4Smpi 		error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0, 0);
1779879b3eabSniklas 		if (error)
1780df930be7Sderaadt 			bp->b_blkno = -1;
1781df930be7Sderaadt 		if (bp->b_blkno == -1)
1782df930be7Sderaadt 			clrbuf(bp);
1783df930be7Sderaadt 	}
1784df930be7Sderaadt 	if (bp->b_blkno == -1) {
17854335127fSart 		s = splbio();
1786df930be7Sderaadt 		biodone(bp);
17874335127fSart 		splx(s);
1788df930be7Sderaadt 		return (error);
1789df930be7Sderaadt 	}
17905af79db2Sart 
1791df930be7Sderaadt 	/*
1792df930be7Sderaadt 	 * Read/write the block from/to the disk that contains the desired
1793df930be7Sderaadt 	 * file block.
1794df930be7Sderaadt 	 */
17955af79db2Sart 
1796df930be7Sderaadt 	vp = dep->de_devvp;
1797df930be7Sderaadt 	bp->b_dev = vp->v_rdev;
1798f1993be3Svisa 	VOP_STRATEGY(vp, bp);
1799df930be7Sderaadt 	return (0);
1800df930be7Sderaadt }
1801df930be7Sderaadt 
1802df930be7Sderaadt int
18037d80fe84Sjasper msdosfs_print(void *v)
1804879b3eabSniklas {
1805*5a0ec814Smiod #if defined(DEBUG) || defined(DIAGNOSTIC) || defined(VFSLCKDEBUG)
180699bc9d31Sderaadt 	struct vop_print_args *ap = v;
1807df930be7Sderaadt 	struct denode *dep = VTODE(ap->a_vp);
1808df930be7Sderaadt 
1809d724e01aSderaadt 	printf(
18107e0e4efdSsf 	    "tag VT_MSDOSFS, startcluster %u, dircluster %u, diroffset %u ",
1811df930be7Sderaadt 	    dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1812df930be7Sderaadt 	printf(" dev %d, %d, %s\n",
1813df930be7Sderaadt 	    major(dep->de_dev), minor(dep->de_dev),
181407feb63cScsapuntz 	    VOP_ISLOCKED(ap->a_vp) ? "(LOCKED)" : "");
1815df930be7Sderaadt #ifdef DIAGNOSTIC
18163dab9bf7Sjasper 	printf("\n");
1817df930be7Sderaadt #endif
1818*5a0ec814Smiod #endif
181907feb63cScsapuntz 
1820879b3eabSniklas 	return (0);
1821df930be7Sderaadt }
1822df930be7Sderaadt 
1823df930be7Sderaadt int
18247d80fe84Sjasper msdosfs_advlock(void *v)
1825879b3eabSniklas {
182699bc9d31Sderaadt 	struct vop_advlock_args *ap = v;
1827cc2a7e83Sjasper 	struct denode *dep = VTODE(ap->a_vp);
1828df930be7Sderaadt 
182916bf7bd1Sderaadt 	return (lf_advlock(&dep->de_lockf, dep->de_FileSize, ap->a_id, ap->a_op,
183016bf7bd1Sderaadt 	    ap->a_fl, ap->a_flags));
1831df930be7Sderaadt }
1832df930be7Sderaadt 
1833df930be7Sderaadt int
18347d80fe84Sjasper msdosfs_pathconf(void *v)
1835879b3eabSniklas {
183699bc9d31Sderaadt 	struct vop_pathconf_args *ap = v;
183716bf7bd1Sderaadt 	struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp;
1838a71fda8cSmatthew 	int error = 0;
1839df930be7Sderaadt 
1840df930be7Sderaadt 	switch (ap->a_name) {
1841df930be7Sderaadt 	case _PC_LINK_MAX:
1842df930be7Sderaadt 		*ap->a_retval = 1;
1843a71fda8cSmatthew 		break;
1844df930be7Sderaadt 	case _PC_NAME_MAX:
184516bf7bd1Sderaadt 		*ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12;
1846a71fda8cSmatthew 		break;
1847df930be7Sderaadt 	case _PC_CHOWN_RESTRICTED:
1848df930be7Sderaadt 		*ap->a_retval = 1;
1849a71fda8cSmatthew 		break;
1850df930be7Sderaadt 	case _PC_NO_TRUNC:
1851df930be7Sderaadt 		*ap->a_retval = 0;
1852a71fda8cSmatthew 		break;
1853d4648cd6Sguenther 	case _PC_TIMESTAMP_RESOLUTION:
1854d4648cd6Sguenther 		*ap->a_retval = 2000000000;	/* 2 billion nanoseconds */
1855d4648cd6Sguenther 		break;
1856df930be7Sderaadt 	default:
1857a71fda8cSmatthew 		error = EINVAL;
1858a71fda8cSmatthew 		break;
1859df930be7Sderaadt 	}
1860a71fda8cSmatthew 
1861a71fda8cSmatthew 	return (error);
1862df930be7Sderaadt }
1863df930be7Sderaadt 
1864979947e6Stom /*
1865979947e6Stom  * Thomas Wang's hash function, severely hacked to always set the high
1866979947e6Stom  * bit on the number it returns (so no longer a proper hash function).
1867979947e6Stom  */
1868979947e6Stom static uint32_t
1869979947e6Stom fileidhash(uint64_t fileid)
1870979947e6Stom {
1871979947e6Stom 	uint64_t c1 = 0x6e5ea73858134343LL;
1872979947e6Stom 	uint64_t c2 = 0xb34e8f99a2ec9ef5LL;
1873979947e6Stom 
1874979947e6Stom 	/*
1875979947e6Stom 	 * We now have the original fileid value, as 64-bit value.
1876395cb243Stom 	 * We need to reduce it to 32-bits, with the top bit set.
1877979947e6Stom 	 */
1878979947e6Stom 	fileid ^= ((c1 ^ fileid) >> 32);
1879979947e6Stom 	fileid *= c1;
1880979947e6Stom 	fileid ^= ((c2 ^ fileid) >> 31);
1881979947e6Stom 	fileid *= c2;
1882979947e6Stom 	fileid ^= ((c1 ^ fileid) >> 32);
1883979947e6Stom 
1884979947e6Stom 	return (uint32_t)(fileid | 0x80000000);
1885979947e6Stom }
1886979947e6Stom 
1887df930be7Sderaadt /* Global vfs data structures for msdosfs */
18882d6b9e38Sclaudio const struct vops msdosfs_vops = {
1889dc81e71aSthib 	.vop_lookup	= msdosfs_lookup,
1890dc81e71aSthib 	.vop_create	= msdosfs_create,
1891dc81e71aSthib 	.vop_mknod	= msdosfs_mknod,
1892dc81e71aSthib 	.vop_open	= msdosfs_open,
1893dc81e71aSthib 	.vop_close	= msdosfs_close,
1894dc81e71aSthib 	.vop_access	= msdosfs_access,
1895dc81e71aSthib 	.vop_getattr	= msdosfs_getattr,
1896dc81e71aSthib 	.vop_setattr	= msdosfs_setattr,
1897dc81e71aSthib 	.vop_read	= msdosfs_read,
1898dc81e71aSthib 	.vop_write	= msdosfs_write,
1899dc81e71aSthib 	.vop_ioctl	= msdosfs_ioctl,
19004eda5a5eSrobert 	.vop_kqfilter	= msdosfs_kqfilter,
1901dc81e71aSthib 	.vop_fsync	= msdosfs_fsync,
1902dc81e71aSthib 	.vop_remove	= msdosfs_remove,
1903dc81e71aSthib 	.vop_link	= msdosfs_link,
1904dc81e71aSthib 	.vop_rename	= msdosfs_rename,
1905dc81e71aSthib 	.vop_mkdir	= msdosfs_mkdir,
1906dc81e71aSthib 	.vop_rmdir	= msdosfs_rmdir,
1907dc81e71aSthib 	.vop_symlink	= msdosfs_symlink,
1908dc81e71aSthib 	.vop_readdir	= msdosfs_readdir,
1909dc81e71aSthib 	.vop_readlink	= msdosfs_readlink,
1910dc81e71aSthib 	.vop_abortop	= vop_generic_abortop,
1911dc81e71aSthib 	.vop_inactive	= msdosfs_inactive,
1912dc81e71aSthib 	.vop_reclaim	= msdosfs_reclaim,
1913dc81e71aSthib 	.vop_lock	= msdosfs_lock,
1914dc81e71aSthib 	.vop_unlock	= msdosfs_unlock,
1915dc81e71aSthib 	.vop_bmap	= msdosfs_bmap,
1916dc81e71aSthib 	.vop_strategy	= msdosfs_strategy,
1917dc81e71aSthib 	.vop_print	= msdosfs_print,
1918dc81e71aSthib 	.vop_islocked	= msdosfs_islocked,
1919dc81e71aSthib 	.vop_pathconf	= msdosfs_pathconf,
1920dc81e71aSthib 	.vop_advlock	= msdosfs_advlock,
19210cc4a88dSespie 	.vop_bwrite	= vop_generic_bwrite,
19220cc4a88dSespie 	.vop_revoke	= vop_generic_revoke,
1923df930be7Sderaadt };
19244eda5a5eSrobert 
192594321eb4Svisa const struct filterops msdosfsread_filtops = {
1926b8213689Svisa 	.f_flags	= FILTEROP_ISFD,
192794321eb4Svisa 	.f_attach	= NULL,
192894321eb4Svisa 	.f_detach	= filt_msdosfsdetach,
192994321eb4Svisa 	.f_event	= filt_msdosfsread,
193094321eb4Svisa };
193194321eb4Svisa 
193294321eb4Svisa const struct filterops msdosfswrite_filtops = {
1933b8213689Svisa 	.f_flags	= FILTEROP_ISFD,
193494321eb4Svisa 	.f_attach	= NULL,
193594321eb4Svisa 	.f_detach	= filt_msdosfsdetach,
193694321eb4Svisa 	.f_event	= filt_msdosfswrite,
193794321eb4Svisa };
193894321eb4Svisa 
193994321eb4Svisa const struct filterops msdosfsvnode_filtops = {
1940b8213689Svisa 	.f_flags	= FILTEROP_ISFD,
194194321eb4Svisa 	.f_attach	= NULL,
194294321eb4Svisa 	.f_detach	= filt_msdosfsdetach,
194394321eb4Svisa 	.f_event	= filt_msdosfsvnode,
194494321eb4Svisa };
19454eda5a5eSrobert 
19464eda5a5eSrobert int
19474eda5a5eSrobert msdosfs_kqfilter(void *v)
19484eda5a5eSrobert {
19494eda5a5eSrobert 	struct vop_kqfilter_args *ap = v;
19504eda5a5eSrobert 	struct vnode *vp = ap->a_vp;
19514eda5a5eSrobert 	struct knote *kn = ap->a_kn;
19524eda5a5eSrobert 
19534eda5a5eSrobert 	switch (kn->kn_filter) {
19544eda5a5eSrobert 	case EVFILT_READ:
19559dc3dd48Sjsg 		kn->kn_fop = &msdosfsread_filtops;
19564eda5a5eSrobert 		break;
19574eda5a5eSrobert 	case EVFILT_WRITE:
19589dc3dd48Sjsg 		kn->kn_fop = &msdosfswrite_filtops;
19594eda5a5eSrobert 		break;
19604eda5a5eSrobert 	case EVFILT_VNODE:
19614eda5a5eSrobert 		kn->kn_fop = &msdosfsvnode_filtops;
19624eda5a5eSrobert 		break;
19634eda5a5eSrobert 	default:
19644eda5a5eSrobert 		return (EINVAL);
19654eda5a5eSrobert 	}
19664eda5a5eSrobert 
19674eda5a5eSrobert 	kn->kn_hook = (caddr_t)vp;
19684eda5a5eSrobert 
1969cc53a24cSmvs 	klist_insert_locked(&vp->v_klist, kn);
19704eda5a5eSrobert 
19714eda5a5eSrobert 	return (0);
19724eda5a5eSrobert }
19734eda5a5eSrobert 
19744eda5a5eSrobert void
19754eda5a5eSrobert filt_msdosfsdetach(struct knote *kn)
19764eda5a5eSrobert {
19774eda5a5eSrobert 	struct vnode *vp = (struct vnode *)kn->kn_hook;
19784eda5a5eSrobert 
1979cc53a24cSmvs 	klist_remove_locked(&vp->v_klist, kn);
19804eda5a5eSrobert }
19814eda5a5eSrobert 
19824eda5a5eSrobert int
19839dc3dd48Sjsg filt_msdosfsread(struct knote *kn, long hint)
19849dc3dd48Sjsg {
19859dc3dd48Sjsg 	struct vnode *vp = (struct vnode *)kn->kn_hook;
19869dc3dd48Sjsg 	struct denode *dep = VTODE(vp);
19879dc3dd48Sjsg 
19889dc3dd48Sjsg 	/*
19899dc3dd48Sjsg 	 * filesystem is gone, so set the EOF flag and schedule
19909dc3dd48Sjsg 	 * the knote for deletion.
19919dc3dd48Sjsg 	 */
19929dc3dd48Sjsg 	if (hint == NOTE_REVOKE) {
19939dc3dd48Sjsg 		kn->kn_flags |= (EV_EOF | EV_ONESHOT);
19949dc3dd48Sjsg 		return (1);
19959dc3dd48Sjsg 	}
19969dc3dd48Sjsg 
1997836f297bSanton 	kn->kn_data = dep->de_FileSize - foffset(kn->kn_fp);
19989dc3dd48Sjsg 	if (kn->kn_data == 0 && kn->kn_sfflags & NOTE_EOF) {
19999dc3dd48Sjsg 		kn->kn_fflags |= NOTE_EOF;
20009dc3dd48Sjsg 		return (1);
20019dc3dd48Sjsg 	}
20026e29a944Smpi 
20036ecc0d7fSvisa 	if (kn->kn_flags & (__EV_POLL | __EV_SELECT))
20046e29a944Smpi 		return (1);
20056e29a944Smpi 
20069dc3dd48Sjsg 	return (kn->kn_data != 0);
20079dc3dd48Sjsg }
20089dc3dd48Sjsg 
20099dc3dd48Sjsg int
20109dc3dd48Sjsg filt_msdosfswrite(struct knote *kn, long hint)
20114eda5a5eSrobert {
20124eda5a5eSrobert 	/*
20134eda5a5eSrobert 	 * filesystem is gone, so set the EOF flag and schedule
20144eda5a5eSrobert 	 * the knote for deletion.
20154eda5a5eSrobert 	 */
20164eda5a5eSrobert 	if (hint == NOTE_REVOKE) {
20174eda5a5eSrobert 		kn->kn_flags |= (EV_EOF | EV_ONESHOT);
20184eda5a5eSrobert 		return (1);
20194eda5a5eSrobert 	}
20204eda5a5eSrobert 
20219dc3dd48Sjsg 	kn->kn_data = 0;
20229dc3dd48Sjsg 	return (1);
20234eda5a5eSrobert }
20244eda5a5eSrobert 
20254eda5a5eSrobert int
20264eda5a5eSrobert filt_msdosfsvnode(struct knote *kn, long hint)
20274eda5a5eSrobert {
20284eda5a5eSrobert 	if (kn->kn_sfflags & hint)
20294eda5a5eSrobert 		kn->kn_fflags |= hint;
20304eda5a5eSrobert 	if (hint == NOTE_REVOKE) {
20314eda5a5eSrobert 		kn->kn_flags |= EV_EOF;
20324eda5a5eSrobert 		return (1);
20334eda5a5eSrobert 	}
20344eda5a5eSrobert 	return (kn->kn_fflags != 0);
20354eda5a5eSrobert }
2036