xref: /freebsd/sys/fs/msdosfs/msdosfs_vnops.c (revision 0ea3482342b4d7d6e71f3007ce4dafe445c639fd)
1 /*	$Id: msdosfs_vnops.c,v 1.26 1995/10/29 15:31:53 phk Exp $ */
2 /*	$NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $	*/
3 
4 /*-
5  * Copyright (C) 1994 Wolfgang Solfrank.
6  * Copyright (C) 1994 TooLs GmbH.
7  * All rights reserved.
8  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by TooLs GmbH.
21  * 4. The name of TooLs GmbH may not be used to endorse or promote products
22  *    derived from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 /*
36  * Written by Paul Popelka (paulp@uts.amdahl.com)
37  *
38  * You can do anything you want with this software, just don't say you wrote
39  * it, and don't remove this notice.
40  *
41  * This software is provided "as is".
42  *
43  * The author supplies this software to be publicly redistributed on the
44  * understanding that the author is not responsible for the correct
45  * functioning of this software in any circumstances and is not liable for
46  * any damages caused by this software.
47  *
48  * October 1992
49  */
50 
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/namei.h>
54 #include <sys/resourcevar.h>	/* defines plimit structure in proc struct */
55 #include <sys/kernel.h>
56 #include <sys/file.h>		/* define FWRITE ... */
57 #include <sys/stat.h>
58 #include <sys/buf.h>
59 #include <sys/proc.h>
60 #include <sys/mount.h>
61 #include <sys/vnode.h>
62 #include <miscfs/specfs/specdev.h> /* XXX */	/* defines v_rdev */
63 #include <sys/malloc.h>
64 #include <sys/dir.h>		/* defines dirent structure */
65 #include <sys/signalvar.h>
66 
67 #include <vm/vm.h>
68 
69 #include <msdosfs/bpb.h>
70 #include <msdosfs/direntry.h>
71 #include <msdosfs/denode.h>
72 #include <msdosfs/msdosfsmount.h>
73 #include <msdosfs/fat.h>
74 
75 /*
76  * Prototypes for MSDOSFS vnode operations
77  */
78 static int msdosfs_create __P((struct vop_create_args *));
79 static int msdosfs_mknod __P((struct vop_mknod_args *));
80 static int msdosfs_open __P((struct vop_open_args *));
81 static int msdosfs_close __P((struct vop_close_args *));
82 static int msdosfs_access __P((struct vop_access_args *));
83 static int msdosfs_getattr __P((struct vop_getattr_args *));
84 static int msdosfs_setattr __P((struct vop_setattr_args *));
85 static int msdosfs_read __P((struct vop_read_args *));
86 static int msdosfs_write __P((struct vop_write_args *));
87 static int msdosfs_ioctl __P((struct vop_ioctl_args *));
88 static int msdosfs_select __P((struct vop_select_args *));
89 static int msdosfs_mmap __P((struct vop_mmap_args *));
90 static int msdosfs_fsync __P((struct vop_fsync_args *));
91 static int msdosfs_seek __P((struct vop_seek_args *));
92 static int msdosfs_remove __P((struct vop_remove_args *));
93 static int msdosfs_link __P((struct vop_link_args *));
94 static int msdosfs_rename __P((struct vop_rename_args *));
95 static int msdosfs_mkdir __P((struct vop_mkdir_args *));
96 static int msdosfs_rmdir __P((struct vop_rmdir_args *));
97 static int msdosfs_symlink __P((struct vop_symlink_args *));
98 static int msdosfs_readdir __P((struct vop_readdir_args *));
99 static int msdosfs_readlink __P((struct vop_readlink_args *));
100 static int msdosfs_abortop __P((struct vop_abortop_args *));
101 static int msdosfs_lock __P((struct vop_lock_args *));
102 static int msdosfs_unlock __P((struct vop_unlock_args *));
103 static int msdosfs_bmap __P((struct vop_bmap_args *));
104 static int msdosfs_strategy __P((struct vop_strategy_args *));
105 static int msdosfs_print __P((struct vop_print_args *));
106 static int msdosfs_islocked __P((struct vop_islocked_args *));
107 static int msdosfs_advlock __P((struct vop_advlock_args *));
108 static int msdosfs_reallocblks __P((struct vop_reallocblks_args *));
109 
110 /*
111  * Some general notes:
112  *
113  * In the ufs filesystem the inodes, superblocks, and indirect blocks are
114  * read/written using the vnode for the filesystem. Blocks that represent
115  * the contents of a file are read/written using the vnode for the file
116  * (including directories when they are read/written as files). This
117  * presents problems for the dos filesystem because data that should be in
118  * an inode (if dos had them) resides in the directory itself.  Since we
119  * must update directory entries without the benefit of having the vnode
120  * for the directory we must use the vnode for the filesystem.  This means
121  * that when a directory is actually read/written (via read, write, or
122  * readdir, or seek) we must use the vnode for the filesystem instead of
123  * the vnode for the directory as would happen in ufs. This is to insure we
124  * retreive the correct block from the buffer cache since the hash value is
125  * based upon the vnode address and the desired block number.
126  */
127 
128 /*
129  * Create a regular file. On entry the directory to contain the file being
130  * created is locked.  We must release before we return. We must also free
131  * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
132  * only if the SAVESTART bit in cn_flags is clear on success.
133  */
134 static int
135 msdosfs_create(ap)
136 	struct vop_create_args /* {
137 		struct vnode *a_dvp;
138 		struct vnode **a_vpp;
139 		struct componentname *a_cnp;
140 		struct vattr *a_vap;
141 	} */ *ap;
142 {
143 	struct componentname *cnp = ap->a_cnp;
144 	struct denode ndirent;
145 	struct denode *dep;
146 	struct denode *pdep = VTODE(ap->a_dvp);
147 	struct timespec ts;
148 	int error;
149 
150 #ifdef MSDOSFS_DEBUG
151 	printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap);
152 #endif
153 
154 	/*
155 	 * Create a directory entry for the file, then call createde() to
156 	 * have it installed. NOTE: DOS files are always executable.  We
157 	 * use the absence of the owner write bit to make the file
158 	 * readonly.
159 	 */
160 #ifdef DIAGNOSTIC
161 	if ((cnp->cn_flags & SAVENAME) == 0)
162 		panic("msdosfs_create: no name");
163 #endif
164 	bzero(&ndirent, sizeof(ndirent));
165 	TIMEVAL_TO_TIMESPEC(&time, &ts);
166 	unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time);
167 	unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen);
168 	ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE)
169 				? ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
170 	ndirent.de_StartCluster = 0;
171 	ndirent.de_FileSize = 0;
172 	ndirent.de_dev = pdep->de_dev;
173 	ndirent.de_devvp = pdep->de_devvp;
174 	if ((error = createde(&ndirent, pdep, &dep)) == 0) {
175 		*ap->a_vpp = DETOV(dep);
176 		if ((cnp->cn_flags & SAVESTART) == 0)
177 			free(cnp->cn_pnbuf, M_NAMEI);
178 	} else {
179 		free(cnp->cn_pnbuf, M_NAMEI);
180 	}
181 	vput(ap->a_dvp);		/* release parent dir */
182 	return error;
183 }
184 
185 static int
186 msdosfs_mknod(ap)
187 	struct vop_mknod_args /* {
188 		struct vnode *a_dvp;
189 		struct vnode **a_vpp;
190 		struct componentname *a_cnp;
191 		struct vattr *a_vap;
192 	} */ *ap;
193 {
194 	int error;
195 
196 	switch (ap->a_vap->va_type) {
197 	case VDIR:
198 		error = msdosfs_mkdir((struct vop_mkdir_args *)ap);
199 		break;
200 
201 	case VREG:
202 		error = msdosfs_create((struct vop_create_args *)ap);
203 		break;
204 
205 	default:
206 		error = EINVAL;
207 		free(ap->a_cnp->cn_pnbuf, M_NAMEI);
208 		vput(ap->a_dvp);
209 		break;
210 	}
211 	return error;
212 }
213 
214 static int
215 msdosfs_open(ap)
216 	struct vop_open_args /* {
217 		struct vnode *a_vp;
218 		int a_mode;
219 		struct ucred *a_cred;
220 		struct proc *a_p;
221 	} */ *ap;
222 {
223 	return 0;
224 }
225 
226 static int
227 msdosfs_close(ap)
228 	struct vop_close_args /* {
229 		struct vnode *a_vp;
230 		int a_fflag;
231 		struct ucred *a_cred;
232 		struct proc *a_p;
233 	} */ *ap;
234 {
235 	struct vnode *vp = ap->a_vp;
236 	struct denode *dep = VTODE(vp);
237 
238 	if (vp->v_usecount > 1 && !(dep->de_flag & DE_LOCKED))
239 		DE_TIMES(dep, &time);
240 	return 0;
241 }
242 
243 static int
244 msdosfs_access(ap)
245 	struct vop_access_args /* {
246 		struct vnode *a_vp;
247 		int a_mode;
248 		struct ucred *a_cred;
249 		struct proc *a_p;
250 	} */ *ap;
251 {
252 	struct vnode *vp = ap->a_vp;
253 	struct denode *dep = VTODE(ap->a_vp);
254 	struct msdosfsmount *pmp = dep->de_pmp;
255 	struct ucred *cred = ap->a_cred;
256 	mode_t mask, file_mode, mode = ap->a_mode;
257 	register gid_t *gp;
258 	int i;
259 
260 	file_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
261 	    ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
262 	file_mode &= pmp->pm_mask;
263 
264 	/*
265 	 * Disallow write attempts on read-only file systems;
266 	 * unless the file is a socket, fifo, or a block or
267 	 * character device resident on the file system.
268 	 */
269 	if (mode & VWRITE) {
270 		switch (vp->v_type) {
271 		case VDIR:
272 		case VLNK:
273 		case VREG:
274 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
275 				return (EROFS);
276 			break;
277 		}
278 	}
279 
280 	/* User id 0 always gets access. */
281 	if (cred->cr_uid == 0)
282 		return 0;
283 
284 	mask = 0;
285 
286 	/* Otherwise, check the owner. */
287 	if (cred->cr_uid == pmp->pm_uid) {
288 		if (mode & VEXEC)
289 			mask |= S_IXUSR;
290 		if (mode & VREAD)
291 			mask |= S_IRUSR;
292 		if (mode & VWRITE)
293 			mask |= S_IWUSR;
294 		return (file_mode & mask) == mask ? 0 : EACCES;
295 	}
296 
297 	/* Otherwise, check the groups. */
298 	for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
299 		if (pmp->pm_gid == *gp) {
300 			if (mode & VEXEC)
301 				mask |= S_IXGRP;
302 			if (mode & VREAD)
303 				mask |= S_IRGRP;
304 			if (mode & VWRITE)
305 				mask |= S_IWGRP;
306 			return (file_mode & mask) == mask ? 0 : EACCES;
307 		}
308 
309 	/* Otherwise, check everyone else. */
310 	if (mode & VEXEC)
311 		mask |= S_IXOTH;
312 	if (mode & VREAD)
313 		mask |= S_IROTH;
314 	if (mode & VWRITE)
315 		mask |= S_IWOTH;
316 	return (file_mode & mask) == mask ? 0 : EACCES;
317 }
318 
319 static int
320 msdosfs_getattr(ap)
321 	struct vop_getattr_args /* {
322 		struct vnode *a_vp;
323 		struct vattr *a_vap;
324 		struct ucred *a_cred;
325 		struct proc *a_p;
326 	} */ *ap;
327 {
328 	u_int cn;
329 	struct denode *dep = VTODE(ap->a_vp);
330 	struct vattr *vap = ap->a_vap;
331 
332 	DE_TIMES(dep, &time);
333 	vap->va_fsid = dep->de_dev;
334 	/*
335 	 * The following computation of the fileid must be the same as that
336 	 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
337 	 * doesn't work.
338 	 */
339 	if (dep->de_Attributes & ATTR_DIRECTORY) {
340 		if ((cn = dep->de_StartCluster) == MSDOSFSROOT)
341 			cn = 1;
342 	} else {
343 		if ((cn = dep->de_dirclust) == MSDOSFSROOT)
344 			cn = 1;
345 		cn = (cn << 16) | (dep->de_diroffset & 0xffff);
346 	}
347 	vap->va_fileid = cn;
348 	vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
349 		((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
350 	vap->va_mode &= dep->de_pmp->pm_mask;
351 	if (dep->de_Attributes & ATTR_DIRECTORY)
352 		vap->va_mode |= S_IFDIR;
353 	vap->va_nlink = 1;
354 	vap->va_gid = dep->de_pmp->pm_gid;
355 	vap->va_uid = dep->de_pmp->pm_uid;
356 	vap->va_rdev = 0;
357 	vap->va_size = dep->de_FileSize;
358 	dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime);
359 	vap->va_mtime = vap->va_atime;
360 #if 0
361 #ifndef MSDOSFS_NODIRMOD
362 	if (vap->va_mode & S_IFDIR)
363 		TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime);
364 #endif
365 #endif
366 	vap->va_ctime = vap->va_atime;
367 	vap->va_flags = (dep->de_Attributes & ATTR_ARCHIVE) ? 0 : SF_ARCHIVED;
368 	vap->va_gen = 0;
369 	vap->va_blocksize = dep->de_pmp->pm_bpcluster;
370 	vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) &
371 	    			~(dep->de_pmp->pm_crbomask);
372 	vap->va_type = ap->a_vp->v_type;
373 	vap->va_filerev = dep->de_modrev;
374 	return 0;
375 }
376 
377 static int
378 msdosfs_setattr(ap)
379 	struct vop_setattr_args /* {
380 		struct vnode *a_vp;
381 		struct vattr *a_vap;
382 		struct ucred *a_cred;
383 		struct proc *a_p;
384 	} */ *ap;
385 {
386 	struct vnode *vp = ap->a_vp;
387 	struct denode *dep = VTODE(ap->a_vp);
388 	struct vattr *vap = ap->a_vap;
389 	struct ucred *cred = ap->a_cred;
390 	int error = 0;
391 
392 	/*
393 	 * Check for unsettable attributes.
394 	 */
395 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
396 	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
397 	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
398 	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
399 		return (EINVAL);
400 	}
401 	if (vap->va_flags != VNOVAL) {
402 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
403 			return (EROFS);
404 		if (cred->cr_uid != dep->de_pmp->pm_uid &&
405 		    (error = suser(cred, &ap->a_p->p_acflag)))
406 			return (error);
407 		/*
408 		 * We are very inconsistent about handling unsupported
409 		 * attributes.  We ignored the the access time and the
410 		 * read and execute bits.  We were strict for the other
411 		 * attributes.
412 		 *
413 		 * Here we are strict, stricter than ufs in not allowing
414 		 * users to attempt to set SF_SETTABLE bits or anyone to
415 		 * set unsupported bits.  However, we ignore attempts to
416 		 * set ATTR_ARCHIVE for directories `cp -pr' from a more
417 		 * sensible file system attempts it a lot.
418 		 */
419 		if (cred->cr_uid != 0) {
420 			if (vap->va_flags & SF_SETTABLE)
421 				return EPERM;
422 		}
423 		if (vap->va_flags & ~SF_ARCHIVED)
424 			return EINVAL;
425 		if (vap->va_flags & SF_ARCHIVED)
426 			dep->de_Attributes &= ~ATTR_ARCHIVE;
427 		else if (!(dep->de_Attributes & ATTR_DIRECTORY))
428 			dep->de_Attributes |= ATTR_ARCHIVE;
429 		dep->de_flag |= DE_MODIFIED;
430 	}
431 
432 	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (uid_t)VNOVAL) {
433 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
434 			return (EROFS);
435 		if ((cred->cr_uid != dep->de_pmp->pm_uid ||
436 		     vap->va_uid != dep->de_pmp->pm_uid ||
437 		     (vap->va_gid != dep->de_pmp->pm_gid &&
438 		      !groupmember(vap->va_gid, cred))) &&
439 		    (error = suser(cred, &ap->a_p->p_acflag)))
440 			return error;
441 		if (vap->va_uid != dep->de_pmp->pm_uid ||
442 		    vap->va_gid != dep->de_pmp->pm_gid)
443 			return EINVAL;
444 	}
445 	if (vap->va_size != VNOVAL) {
446 		/*
447 		 * Disallow write attempts on read-only file systems;
448 		 * unless the file is a socket, fifo, or a block or
449 		 * character device resident on the file system.
450 		 */
451 		switch (vp->v_type) {
452 		case VDIR:
453 			return (EISDIR);
454 		case VLNK:
455 		case VREG:
456 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
457 				return (EROFS);
458 			break;
459 		}
460 		error = detrunc(dep, vap->va_size, 0, cred, ap->a_p);
461 		if (error)
462 			return error;
463 	}
464 	if (vap->va_mtime.ts_sec != VNOVAL) {
465 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
466 			return (EROFS);
467 		if (cred->cr_uid != dep->de_pmp->pm_uid &&
468 		    (error = suser(cred, &ap->a_p->p_acflag)) &&
469 		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
470 		    (error = VOP_ACCESS(vp, VWRITE, cred, ap->a_p))))
471 			return error;
472 		dep->de_flag |= DE_UPDATE;
473 		error = deupdat(dep, &vap->va_mtime, 1);
474 		if (error)
475 			return error;
476 	}
477 
478 	/*
479 	 * DOS files only have the ability to have their writability
480 	 * attribute set, so we use the owner write bit to set the readonly
481 	 * attribute.
482 	 */
483 	error = 0;
484 	if (vap->va_mode != (u_short) VNOVAL) {
485 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
486 			return (EROFS);
487 		if (cred->cr_uid != dep->de_pmp->pm_uid &&
488 		    (error = suser(cred, &ap->a_p->p_acflag)))
489 			return error;
490 
491 		/* We ignore the read and execute bits */
492 		if (vap->va_mode & VWRITE)
493 			dep->de_Attributes &= ~ATTR_READONLY;
494 		else
495 			dep->de_Attributes |= ATTR_READONLY;
496 		dep->de_flag |= DE_MODIFIED;
497 	}
498 	return error;
499 }
500 
501 static int
502 msdosfs_read(ap)
503 	struct vop_read_args /* {
504 		struct vnode *a_vp;
505 		struct uio *a_uio;
506 		int a_ioflag;
507 		struct ucred *a_cred;
508 	} */ *ap;
509 {
510 	int error = 0;
511 	int diff;
512 	int isadir;
513 	long n;
514 	long on;
515 	daddr_t lbn;
516 	daddr_t rablock;
517 	int rasize;
518 	struct buf *bp;
519 	struct vnode *vp = ap->a_vp;
520 	struct denode *dep = VTODE(vp);
521 	struct msdosfsmount *pmp = dep->de_pmp;
522 	struct uio *uio = ap->a_uio;
523 
524 	/*
525 	 * If they didn't ask for any data, then we are done.
526 	 */
527 	if (uio->uio_resid == 0)
528 		return 0;
529 	if (uio->uio_offset < 0)
530 		return EINVAL;
531 
532 	isadir = dep->de_Attributes & ATTR_DIRECTORY;
533 	do {
534 		lbn = uio->uio_offset >> pmp->pm_cnshift;
535 		on = uio->uio_offset & pmp->pm_crbomask;
536 		n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
537 		diff = dep->de_FileSize - uio->uio_offset;
538 		if (diff <= 0)
539 			return 0;
540 		/* convert cluster # to block # if a directory */
541 		if (isadir) {
542 			error = pcbmap(dep, lbn, &lbn, 0);
543 			if (error)
544 				return error;
545 		}
546 		if (diff < n)
547 			n = diff;
548 		/*
549 		 * If we are operating on a directory file then be sure to
550 		 * do i/o with the vnode for the filesystem instead of the
551 		 * vnode for the directory.
552 		 */
553 		if (isadir) {
554 			error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster,
555 			    NOCRED, &bp);
556 		} else {
557 			rablock = lbn + 1;
558 			if (vp->v_lastr + 1 == lbn &&
559 			    rablock * pmp->pm_bpcluster < dep->de_FileSize) {
560 				rasize = pmp->pm_bpcluster;
561 				error = breadn(vp, lbn, pmp->pm_bpcluster,
562 					       &rablock, &rasize, 1,
563 					       NOCRED, &bp);
564 			} else {
565 				error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED,
566 					      &bp);
567 			}
568 			vp->v_lastr = lbn;
569 		}
570 		n = min(n, pmp->pm_bpcluster - bp->b_resid);
571 		if (error) {
572 			brelse(bp);
573 			return error;
574 		}
575 		error = uiomove(bp->b_data + on, (int) n, uio);
576 		/*
577 		 * If we have read everything from this block or have read
578 		 * to end of file then we are done with this block.  Mark
579 		 * it to say the buffer can be reused if need be.
580 		 */
581 #if 0
582 		if (n + on == pmp->pm_bpcluster ||
583 		    uio->uio_offset == dep->de_FileSize)
584 			bp->b_flags |= B_AGE;
585 #endif
586 		brelse(bp);
587 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
588 	return error;
589 }
590 
591 /*
592  * Write data to a file or directory.
593  */
594 static int
595 msdosfs_write(ap)
596 	struct vop_write_args /* {
597 		struct vnode *a_vp;
598 		struct uio *a_uio;
599 		int a_ioflag;
600 		struct ucred *a_cred;
601 	} */ *ap;
602 {
603 	int n;
604 	int isadir;
605 	int croffset;
606 	int resid;
607 	int osize;
608 	int error = 0;
609 	u_long count;
610 	daddr_t bn, lastcn;
611 	struct buf *bp;
612 	int ioflag = ap->a_ioflag;
613 	struct uio *uio = ap->a_uio;
614 	struct proc *p = uio->uio_procp;
615 	struct vnode *vp = ap->a_vp;
616 	struct vnode *thisvp;
617 	struct denode *dep = VTODE(vp);
618 	struct msdosfsmount *pmp = dep->de_pmp;
619 	struct ucred *cred = ap->a_cred;
620 	struct timespec ts;
621 
622 #ifdef MSDOSFS_DEBUG
623 	printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n",
624 	       vp, uio, ioflag, cred);
625 	printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n",
626 	       dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
627 #endif
628 
629 	switch (vp->v_type) {
630 	case VREG:
631 		if (ioflag & IO_APPEND)
632 			uio->uio_offset = dep->de_FileSize;
633 		isadir = 0;
634 		thisvp = vp;
635 		break;
636 
637 	case VDIR:
638 		if ((ioflag & IO_SYNC) == 0)
639 			panic("msdosfs_write(): non-sync directory update");
640 		isadir = 1;
641 		thisvp = pmp->pm_devvp;
642 		break;
643 
644 	default:
645 		panic("msdosfs_write(): bad file type");
646 		break;
647 	}
648 
649 	if (uio->uio_offset < 0)
650 		return EINVAL;
651 
652 	if (uio->uio_resid == 0)
653 		return 0;
654 
655 	/*
656 	 * If they've exceeded their filesize limit, tell them about it.
657 	 */
658 	if (vp->v_type == VREG && p &&
659 	    ((uio->uio_offset + uio->uio_resid) >
660 		p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) {
661 		psignal(p, SIGXFSZ);
662 		return EFBIG;
663 	}
664 
665 	/*
666 	 * If attempting to write beyond the end of the root directory we
667 	 * stop that here because the root directory can not grow.
668 	 */
669 	if ((dep->de_Attributes & ATTR_DIRECTORY) &&
670 	    dep->de_StartCluster == MSDOSFSROOT &&
671 	    (uio->uio_offset + uio->uio_resid) > dep->de_FileSize)
672 		return ENOSPC;
673 
674 	/*
675 	 * If the offset we are starting the write at is beyond the end of
676 	 * the file, then they've done a seek.  Unix filesystems allow
677 	 * files with holes in them, DOS doesn't so we must fill the hole
678 	 * with zeroed blocks.
679 	 */
680 	if (uio->uio_offset > dep->de_FileSize) {
681 		error = deextend(dep, uio->uio_offset, cred);
682 		if (error)
683 			return error;
684 	}
685 
686 	/*
687 	 * Remember some values in case the write fails.
688 	 */
689 	resid = uio->uio_resid;
690 	osize = dep->de_FileSize;
691 
692 
693 	/*
694 	 * If we write beyond the end of the file, extend it to its ultimate
695 	 * size ahead of the time to hopefully get a contiguous area.
696 	 */
697 	if (uio->uio_offset + resid > osize) {
698 		count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize);
699 		if ((error = extendfile(dep, count, NULL, NULL, 0))
700 		    && (error != ENOSPC || (ioflag & IO_UNIT)))
701 			goto errexit;
702 		lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
703 	} else
704 		lastcn = de_clcount(pmp, osize) - 1;
705 
706 	do {
707 		bn = de_blk(pmp, uio->uio_offset);
708 		if (isadir) {
709 			error = pcbmap(dep, bn, &bn, 0);
710 			if (error)
711 				break;
712 		} else if (bn > lastcn) {
713 			error = ENOSPC;
714 			break;
715 		}
716 
717 		if ((uio->uio_offset & pmp->pm_crbomask) == 0
718 		    && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset)
719 			|| uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
720 			/*
721 			 * If either the whole cluster gets written,
722 			 * or we write the cluster from its start beyond EOF,
723 			 * then no need to read data from disk.
724 			 */
725 			bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0);
726 			clrbuf(bp);
727 			/*
728 			 * Do the bmap now, since pcbmap needs buffers
729 			 * for the fat table. (see msdosfs_strategy)
730 			 */
731 			if (!isadir) {
732 				if (bp->b_blkno == bp->b_lblkno) {
733 					error = pcbmap(dep, bp->b_lblkno,
734 							   &bp->b_blkno, 0);
735 					if (error)
736 						bp->b_blkno = -1;
737 				}
738 				if (bp->b_blkno == -1) {
739 					brelse(bp);
740 					if (!error)
741 						error = EIO;		/* XXX */
742 					break;
743 				}
744 			}
745 		} else {
746 			/*
747 			 * The block we need to write into exists, so read it in.
748 			 */
749 			error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp);
750 			if (error)
751 				break;
752 		}
753 
754 		croffset = uio->uio_offset & pmp->pm_crbomask;
755 		n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
756 		if (uio->uio_offset + n > dep->de_FileSize) {
757 			dep->de_FileSize = uio->uio_offset + n;
758 			vnode_pager_setsize(vp, dep->de_FileSize);	/* why? */
759 		}
760 		/*
761 		 * Should these vnode_pager_* functions be done on dir
762 		 * files?
763 		 */
764 
765 		/*
766 		 * Copy the data from user space into the buf header.
767 		 */
768 		error = uiomove(bp->b_data + croffset, n, uio);
769 
770 		/*
771 		 * If they want this synchronous then write it and wait for
772 		 * it.  Otherwise, if on a cluster boundary write it
773 		 * asynchronously so we can move on to the next block
774 		 * without delay.  Otherwise do a delayed write because we
775 		 * may want to write somemore into the block later.
776 		 */
777 		if (ioflag & IO_SYNC)
778 			(void) bwrite(bp);
779 		else if (n + croffset == pmp->pm_bpcluster) {
780 			bawrite(bp);
781 		} else
782 			bdwrite(bp);
783 		dep->de_flag |= DE_UPDATE;
784 	} while (error == 0 && uio->uio_resid > 0);
785 
786 	/*
787 	 * If the write failed and they want us to, truncate the file back
788 	 * to the size it was before the write was attempted.
789 	 */
790 errexit:
791 	if (error) {
792 		if (ioflag & IO_UNIT) {
793 			detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL);
794 			uio->uio_offset -= resid - uio->uio_resid;
795 			uio->uio_resid = resid;
796 		} else {
797 			detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL);
798 			if (uio->uio_resid != resid)
799 				error = 0;
800 		}
801 	} else {
802 		TIMEVAL_TO_TIMESPEC(&time, &ts);
803 		error = deupdat(dep, &ts, 1);
804 	}
805 	return error;
806 }
807 
808 static int
809 msdosfs_ioctl(ap)
810 	struct vop_ioctl_args /* {
811 		struct vnode *a_vp;
812 		int a_command;
813 		caddr_t a_data;
814 		int a_fflag;
815 		struct ucred *a_cred;
816 		struct proc *a_p;
817 	} */ *ap;
818 {
819 	return ENOTTY;
820 }
821 
822 static int
823 msdosfs_select(ap)
824 	struct vop_select_args /* {
825 		struct vnode *a_vp;
826 		int a_which;
827 		int a_fflags;
828 		struct ucred *a_cred;
829 		struct proc *a_p;
830 	} */ *ap;
831 {
832 	return 1;		/* DOS filesystems never block? */
833 }
834 
835 static int
836 msdosfs_mmap(ap)
837 	struct vop_mmap_args /* {
838 		struct vnode *a_vp;
839 		int a_fflags;
840 		struct ucred *a_cred;
841 		struct proc *a_p;
842 	} */ *ap;
843 {
844 	return EINVAL;
845 }
846 
847 /*
848  * Flush the blocks of a file to disk.
849  *
850  * This function is worthless for vnodes that represent directories. Maybe we
851  * could just do a sync if they try an fsync on a directory file.
852  */
853 static int
854 msdosfs_fsync(ap)
855 	struct vop_fsync_args /* {
856 		struct vnode *a_vp;
857 		struct ucred *a_cred;
858 		int a_waitfor;
859 		struct proc *a_p;
860 	} */ *ap;
861 {
862 	register struct vnode *vp = ap->a_vp;
863 	register struct buf *bp;
864 	int wait = ap->a_waitfor == MNT_WAIT;
865 	struct timespec ts;
866 	struct buf *nbp;
867 	int s;
868 
869 	/*
870 	 * Flush all dirty buffers associated with a vnode.
871 	 */
872 loop:
873 	s = splbio();
874 	for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
875 		nbp = bp->b_vnbufs.le_next;
876 		if ((bp->b_flags & B_BUSY))
877 			continue;
878 		if ((bp->b_flags & B_DELWRI) == 0)
879 			panic("msdosfs_fsync: not dirty");
880 		bremfree(bp);
881 		bp->b_flags |= B_BUSY;
882 		splx(s);
883 		(void) bwrite(bp);
884 		goto loop;
885 	}
886 	while (vp->v_numoutput) {
887 		vp->v_flag |= VBWAIT;
888 		(void) tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "msdosfsn", 0);
889 	}
890 #ifdef DIAGNOSTIC
891 	if (vp->v_dirtyblkhd.lh_first) {
892 		vprint("msdosfs_fsync: dirty", vp);
893 		goto loop;
894 	}
895 #endif
896 	splx(s);
897 	TIMEVAL_TO_TIMESPEC(&time, &ts);
898 	return deupdat(VTODE(vp), &ts, wait);
899 }
900 
901 /*
902  * Now the whole work of extending a file is done in the write function.
903  * So nothing to do here.
904  */
905 static int
906 msdosfs_seek(ap)
907 	struct vop_seek_args /* {
908 		struct vnode *a_vp;
909 		off_t a_oldoff;
910 		off_t a_newoff;
911 		struct ucred *a_cred;
912 	} */ *ap;
913 {
914 	return 0;
915 }
916 
917 static int
918 msdosfs_remove(ap)
919 	struct vop_remove_args /* {
920 		struct vnode *a_dvp;
921 		struct vnode *a_vp;
922 		struct componentname *a_cnp;
923 	} */ *ap;
924 {
925 	int error;
926 	struct denode *dep = VTODE(ap->a_vp);
927 	struct denode *ddep = VTODE(ap->a_dvp);
928 
929 	error = removede(ddep,dep);
930 #ifdef MSDOSFS_DEBUG
931 	printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount);
932 #endif
933 	if (ddep == dep)
934 		vrele(ap->a_vp);
935 	else
936 		vput(ap->a_vp);	/* causes msdosfs_inactive() to be called
937 				 * via vrele() */
938 	vput(ap->a_dvp);
939 	return error;
940 }
941 
942 /*
943  * DOS filesystems don't know what links are. But since we already called
944  * msdosfs_lookup() with create and lockparent, the parent is locked so we
945  * have to free it before we return the error.
946  */
947 static int
948 msdosfs_link(ap)
949 	struct vop_link_args /* {
950 		struct vnode *a_tdvp;
951 		struct vnode *a_vp;
952 		struct componentname *a_cnp;
953 	} */ *ap;
954 {
955 	VOP_ABORTOP(ap->a_tdvp, ap->a_cnp);
956 	vput(ap->a_tdvp);
957 	return EOPNOTSUPP;
958 }
959 
960 /*
961  * Renames on files require moving the denode to a new hash queue since the
962  * denode's location is used to compute which hash queue to put the file
963  * in. Unless it is a rename in place.  For example "mv a b".
964  *
965  * What follows is the basic algorithm:
966  *
967  * if (file move) {
968  *	if (dest file exists) {
969  *		remove dest file
970  *	}
971  *	if (dest and src in same directory) {
972  *		rewrite name in existing directory slot
973  *	} else {
974  *		write new entry in dest directory
975  *		update offset and dirclust in denode
976  *		move denode to new hash chain
977  *		clear old directory entry
978  *	}
979  * } else {
980  *	directory move
981  *	if (dest directory exists) {
982  *		if (dest is not empty) {
983  *			return ENOTEMPTY
984  *		}
985  *		remove dest directory
986  *	}
987  *	if (dest and src in same directory) {
988  *		rewrite name in existing entry
989  *	} else {
990  *		be sure dest is not a child of src directory
991  *		write entry in dest directory
992  *		update "." and ".." in moved directory
993  *		clear old directory entry for moved directory
994  *	}
995  * }
996  *
997  * On entry:
998  *	source's parent directory is unlocked
999  *	source file or directory is unlocked
1000  *	destination's parent directory is locked
1001  *	destination file or directory is locked if it exists
1002  *
1003  * On exit:
1004  *	all denodes should be released
1005  *
1006  * Notes:
1007  * I'm not sure how the memory containing the pathnames pointed at by the
1008  * componentname structures is freed, there may be some memory bleeding
1009  * for each rename done.
1010  */
1011 static int
1012 msdosfs_rename(ap)
1013 	struct vop_rename_args /* {
1014 		struct vnode *a_fdvp;
1015 		struct vnode *a_fvp;
1016 		struct componentname *a_fcnp;
1017 		struct vnode *a_tdvp;
1018 		struct vnode *a_tvp;
1019 		struct componentname *a_tcnp;
1020 	} */ *ap;
1021 {
1022 	u_char toname[11];
1023 	int error;
1024 	int newparent = 0;
1025 	int sourceisadirectory = 0;
1026 	u_long cn;
1027 	daddr_t bn;
1028 	struct vnode *tvp = ap->a_tvp;
1029 	struct denode *fddep;	/* from file's parent directory	 */
1030 	struct denode *fdep;	/* from file or directory	 */
1031 	struct denode *tddep;	/* to file's parent directory	 */
1032 	struct denode *tdep;	/* to file or directory		 */
1033 	struct msdosfsmount *pmp;
1034 	struct direntry *dotdotp;
1035 	struct direntry *ep;
1036 	struct buf *bp;
1037 
1038 	fddep = VTODE(ap->a_fdvp);
1039 	fdep = VTODE(ap->a_fvp);
1040 	tddep = VTODE(ap->a_tdvp);
1041 	tdep = tvp ? VTODE(tvp) : NULL;
1042 	pmp = fddep->de_pmp;
1043 
1044 	/* Check for cross-device rename */
1045 	if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) ||
1046 	    (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) {
1047 		error = EXDEV;
1048 		goto bad;
1049 	}
1050 
1051 	/*
1052 	 * Convert the filename in tcnp into a dos filename. We copy this
1053 	 * into the denode and directory entry for the destination
1054 	 * file/directory.
1055 	 */
1056 	unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr,
1057 		   toname, ap->a_tcnp->cn_namelen);
1058 
1059 	/*
1060 	 * At this point this is the lock state of the denodes:
1061 	 *	fddep referenced
1062 	 *	fdep  referenced
1063 	 *	tddep locked
1064 	 *	tdep  locked if it exists
1065 	 */
1066 
1067 	/*
1068 	 * Be sure we are not renaming ".", "..", or an alias of ".". This
1069 	 * leads to a crippled directory tree.  It's pretty tough to do a
1070 	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
1071 	 * doesn't work if the ".." entry is missing.
1072 	 */
1073 	if (fdep->de_Attributes & ATTR_DIRECTORY) {
1074 		if ((ap->a_fcnp->cn_namelen == 1
1075 		     && ap->a_fcnp->cn_nameptr[0] == '.')
1076 		    || fddep == fdep
1077 		    || (ap->a_fcnp->cn_flags | ap->a_tcnp->cn_flags)
1078 		       & ISDOTDOT) {
1079 			VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp);
1080 			vput(ap->a_tdvp);
1081 			if (tvp)
1082 				vput(tvp);
1083 			VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp);
1084 			vrele(ap->a_fdvp);
1085 			vrele(ap->a_fvp);
1086 			return EINVAL;
1087 		}
1088 		sourceisadirectory = 1;
1089 	}
1090 
1091 	/*
1092 	 * If we are renaming a directory, and the directory is being moved
1093 	 * to another directory, then we must be sure the destination
1094 	 * directory is not in the subtree of the source directory.  This
1095 	 * could orphan everything under the source directory.
1096 	 * doscheckpath() unlocks the destination's parent directory so we
1097 	 * must look it up again to relock it.
1098 	 */
1099 	if (fddep->de_StartCluster != tddep->de_StartCluster)
1100 		newparent = 1;
1101 	if (sourceisadirectory && newparent) {
1102 		if (tdep) {
1103 			vput(ap->a_tvp);
1104 			tdep = NULL;
1105 		}
1106 		/* doscheckpath() vput()'s tddep */
1107 		error = doscheckpath(fdep, tddep);
1108 		tddep = NULL;
1109 		if (error)
1110 			goto bad;
1111 		if ((ap->a_tcnp->cn_flags & SAVESTART) == 0)
1112 			panic("msdosfs_rename(): lost to startdir");
1113 		error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp);
1114 		if (error)
1115 			goto bad;
1116 		tddep = VTODE(ap->a_tdvp);
1117 		tdep = tvp ? VTODE(tvp) : NULL;
1118 	}
1119 
1120 	/*
1121 	 * If the destination exists, then be sure its type (file or dir)
1122 	 * matches that of the source.  And, if it is a directory make sure
1123 	 * it is empty.  Then delete the destination.
1124 	 */
1125 	if (tdep) {
1126 		if (tdep->de_Attributes & ATTR_DIRECTORY) {
1127 			if (!sourceisadirectory) {
1128 				error = ENOTDIR;
1129 				goto bad;
1130 			}
1131 			if (!dosdirempty(tdep)) {
1132 				error = ENOTEMPTY;
1133 				goto bad;
1134 			}
1135 			cache_purge(DETOV(tddep));
1136 		} else {		/* destination is file */
1137 			if (sourceisadirectory) {
1138 				error = EISDIR;
1139 				goto bad;
1140 			}
1141 		}
1142 		error = removede(tddep,tdep);
1143 		if (error)
1144 			goto bad;
1145 		vput(ap->a_tvp);
1146 		tdep = NULL;
1147 	}
1148 
1149 	/*
1150 	 * If the source and destination are in the same directory then
1151 	 * just read in the directory entry, change the name in the
1152 	 * directory entry and write it back to disk.
1153 	 */
1154 	if (newparent == 0) {
1155 		/* tddep and fddep point to the same denode here */
1156 		VOP_LOCK(ap->a_fvp);	/* ap->a_fdvp is already locked */
1157 		error = readep(fddep->de_pmp, fdep->de_dirclust,
1158 				   fdep->de_diroffset, &bp, &ep);
1159 		if (error) {
1160 			VOP_UNLOCK(ap->a_fvp);
1161 			goto bad;
1162 		}
1163 		bcopy(toname, ep->deName, 11);
1164 		error = bwrite(bp);
1165 		if (error) {
1166 			VOP_UNLOCK(ap->a_fvp);
1167 			goto bad;
1168 		}
1169 		bcopy(toname, fdep->de_Name, 11);	/* update denode */
1170 		/*
1171 		 * fdep locked fddep and tddep point to the same denode
1172 		 * which is locked tdep is NULL
1173 		 */
1174 	} else {
1175 		u_long dirsize = 0L;
1176 
1177 		/*
1178 		 * If the source and destination are in different
1179 		 * directories, then mark the entry in the source directory
1180 		 * as deleted and write a new entry in the destination
1181 		 * directory.  Then move the denode to the correct hash
1182 		 * chain for its new location in the filesystem.  And, if
1183 		 * we moved a directory, then update its .. entry to point
1184 		 * to the new parent directory. If we moved a directory
1185 		 * will also insure that the directory entry on disk has a
1186 		 * filesize of zero.
1187 		 */
1188 		VOP_LOCK(ap->a_fvp);
1189 		bcopy(toname, fdep->de_Name, 11);	/* update denode */
1190 		if (fdep->de_Attributes & ATTR_DIRECTORY) {
1191 			dirsize = fdep->de_FileSize;
1192 			fdep->de_FileSize = 0;
1193 		}
1194 		error = createde(fdep, tddep, (struct denode **) 0);
1195 		if (fdep->de_Attributes & ATTR_DIRECTORY) {
1196 			fdep->de_FileSize = dirsize;
1197 		}
1198 		if (error) {
1199 			/* should put back filename */
1200 			VOP_UNLOCK(ap->a_fvp);
1201 			goto bad;
1202 		}
1203 		VOP_LOCK(ap->a_fdvp);
1204 		error = readep(fddep->de_pmp, fddep->de_fndclust,
1205 				   fddep->de_fndoffset, &bp, &ep);
1206 		if (error) {
1207 			VOP_UNLOCK(ap->a_fvp);
1208 			VOP_UNLOCK(ap->a_fdvp);
1209 			goto bad;
1210 		}
1211 		ep->deName[0] = SLOT_DELETED;
1212 		error = bwrite(bp);
1213 		if (error) {
1214 			VOP_UNLOCK(ap->a_fvp);
1215 			VOP_UNLOCK(ap->a_fdvp);
1216 			goto bad;
1217 		}
1218 		if (!sourceisadirectory) {
1219 			fdep->de_dirclust = tddep->de_fndclust;
1220 			fdep->de_diroffset = tddep->de_fndoffset;
1221 			reinsert(fdep);
1222 		}
1223 		VOP_UNLOCK(ap->a_fdvp);
1224 	}
1225 	/* fdep is still locked here */
1226 
1227 	/*
1228 	 * If we moved a directory to a new parent directory, then we must
1229 	 * fixup the ".." entry in the moved directory.
1230 	 */
1231 	if (sourceisadirectory && newparent) {
1232 		cn = fdep->de_StartCluster;
1233 		if (cn == MSDOSFSROOT) {
1234 			/* this should never happen */
1235 			panic("msdosfs_rename(): updating .. in root directory?");
1236 		} else {
1237 			bn = cntobn(pmp, cn);
1238 		}
1239 		error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
1240 			      NOCRED, &bp);
1241 		if (error) {
1242 			/* should really panic here, fs is corrupt */
1243 			VOP_UNLOCK(ap->a_fvp);
1244 			goto bad;
1245 		}
1246 		dotdotp = (struct direntry *) bp->b_data + 1;
1247 		putushort(dotdotp->deStartCluster, tddep->de_StartCluster);
1248 		error = bwrite(bp);
1249 		VOP_UNLOCK(ap->a_fvp);
1250 		if (error) {
1251 			/* should really panic here, fs is corrupt */
1252 			goto bad;
1253 		}
1254 	} else
1255 		VOP_UNLOCK(ap->a_fvp);
1256 bad:	;
1257 	vrele(DETOV(fdep));
1258 	vrele(DETOV(fddep));
1259 	if (tdep)
1260 		vput(DETOV(tdep));
1261 	if (tddep)
1262 		vput(DETOV(tddep));
1263 	return error;
1264 }
1265 
1266 static struct {
1267 	struct direntry dot;
1268 	struct direntry dotdot;
1269 }      dosdirtemplate = {
1270     {
1271 	".       ", "   ",		/* the . entry */
1272 	ATTR_DIRECTORY,			/* file attribute */
1273 	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},	/* resevered */
1274 	{210, 4}, {210, 4},		/* time and date */
1275 	{0, 0},				/* startcluster */
1276 	{0, 0, 0, 0},			/* filesize */
1277     },{
1278 	"..      ", "   ",		/* the .. entry */
1279 	ATTR_DIRECTORY,			/* file attribute */
1280 	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},	/* resevered */
1281 	{210, 4}, {210, 4},		/* time and date */
1282 	{0, 0},				/* startcluster */
1283 	{0, 0, 0, 0},			/* filesize */
1284     }
1285 };
1286 
1287 static int
1288 msdosfs_mkdir(ap)
1289 	struct vop_mkdir_args /* {
1290 		struct vnode *a_dvp;
1291 		struvt vnode **a_vpp;
1292 		struvt componentname *a_cnp;
1293 		struct vattr *a_vap;
1294 	} */ *ap;
1295 {
1296 	int bn;
1297 	int error;
1298 	u_long newcluster;
1299 	struct denode *pdep;
1300 	struct denode *ndep;
1301 	struct direntry *denp;
1302 	struct denode ndirent;
1303 	struct msdosfsmount *pmp;
1304 	struct buf *bp;
1305 	struct timespec ts;
1306 	u_short dDate, dTime;
1307 
1308 	pdep = VTODE(ap->a_dvp);
1309 
1310 	/*
1311 	 * If this is the root directory and there is no space left we
1312 	 * can't do anything.  This is because the root directory can not
1313 	 * change size.
1314 	 */
1315 	if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndclust == (u_long)-1) {
1316 		free(ap->a_cnp->cn_pnbuf, M_NAMEI);
1317 		vput(ap->a_dvp);
1318 		return ENOSPC;
1319 	}
1320 
1321 	pmp = pdep->de_pmp;
1322 
1323 	/*
1324 	 * Allocate a cluster to hold the about to be created directory.
1325 	 */
1326 	error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
1327 	if (error) {
1328 		free(ap->a_cnp->cn_pnbuf, M_NAMEI);
1329 		vput(ap->a_dvp);
1330 		return error;
1331 	}
1332 
1333 	/*
1334 	 * Now fill the cluster with the "." and ".." entries. And write
1335 	 * the cluster to disk.  This way it is there for the parent
1336 	 * directory to be pointing at if there were a crash.
1337 	 */
1338 	bn = cntobn(pmp, newcluster);
1339 	/* always succeeds */
1340 	bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0);
1341 	bzero(bp->b_data, pmp->pm_bpcluster);
1342 	bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
1343 	denp = (struct direntry *) bp->b_data;
1344 	putushort(denp->deStartCluster, newcluster);
1345 	TIMEVAL_TO_TIMESPEC(&time, &ts);
1346 	unix2dostime(&ts, &dDate, &dTime);
1347 	putushort(denp->deDate, dDate);
1348 	putushort(denp->deTime, dTime);
1349 	denp++;
1350 	putushort(denp->deStartCluster, pdep->de_StartCluster);
1351 	putushort(denp->deDate, dDate);
1352 	putushort(denp->deTime, dTime);
1353 	error = bwrite(bp);
1354 	if (error) {
1355 		clusterfree(pmp, newcluster, NULL);
1356 		free(ap->a_cnp->cn_pnbuf, M_NAMEI);
1357 		vput(ap->a_dvp);
1358 		return error;
1359 	}
1360 
1361 	/*
1362 	 * Now build up a directory entry pointing to the newly allocated
1363 	 * cluster.  This will be written to an empty slot in the parent
1364 	 * directory.
1365 	 */
1366 	ndep = &ndirent;
1367 	bzero(ndep, sizeof(*ndep));
1368 	unix2dosfn((u_char *)ap->a_cnp->cn_nameptr,
1369 		   ndep->de_Name, ap->a_cnp->cn_namelen);
1370 	TIMEVAL_TO_TIMESPEC(&time, &ts);
1371 	unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time);
1372 	ndep->de_StartCluster = newcluster;
1373 	ndep->de_Attributes = ATTR_DIRECTORY;
1374 
1375 	error = createde(ndep, pdep, &ndep);
1376 	if (error) {
1377 		clusterfree(pmp, newcluster, NULL);
1378 	} else {
1379 		*ap->a_vpp = DETOV(ndep);
1380 	}
1381 	free(ap->a_cnp->cn_pnbuf, M_NAMEI);
1382 #ifdef MSDOSFS_DEBUG
1383 	printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp);
1384 #endif
1385 	vput(ap->a_dvp);
1386 	return error;
1387 }
1388 
1389 static int
1390 msdosfs_rmdir(ap)
1391 	struct vop_rmdir_args /* {
1392 		struct vnode *a_dvp;
1393 		struct vnode *a_vp;
1394 		struct componentname *a_cnp;
1395 	} */ *ap;
1396 {
1397 	struct denode *ddep;
1398 	struct denode *dep;
1399 	int error = 0;
1400 
1401 	ddep = VTODE(ap->a_dvp);	/* parent dir of dir to delete	 */
1402 	dep = VTODE(ap->a_vp);/* directory to delete	 */
1403 
1404 	/*
1405 	 * Don't let "rmdir ." go thru.
1406 	 */
1407 	if (ddep == dep) {
1408 		vrele(ap->a_vp);
1409 		vput(ap->a_vp);
1410 		return EINVAL;
1411 	}
1412 
1413 	/*
1414 	 * Be sure the directory being deleted is empty.
1415 	 */
1416 	if (dosdirempty(dep) == 0) {
1417 		error = ENOTEMPTY;
1418 		goto out;
1419 	}
1420 
1421 	/*
1422 	 * Delete the entry from the directory.  For dos filesystems this
1423 	 * gets rid of the directory entry on disk, the in memory copy
1424 	 * still exists but the de_refcnt is <= 0.  This prevents it from
1425 	 * being found by deget().  When the vput() on dep is done we give
1426 	 * up access and eventually msdosfs_reclaim() will be called which
1427 	 * will remove it from the denode cache.
1428 	 */
1429 	error = removede(ddep,dep);
1430 	if (error)
1431 		goto out;
1432 
1433 	/*
1434 	 * This is where we decrement the link count in the parent
1435 	 * directory.  Since dos filesystems don't do this we just purge
1436 	 * the name cache and let go of the parent directory denode.
1437 	 */
1438 	cache_purge(DETOV(ddep));
1439 	vput(ap->a_dvp);
1440 	ap->a_dvp = NULL;
1441 
1442 	/*
1443 	 * Truncate the directory that is being deleted.
1444 	 */
1445 	error = detrunc(dep, (u_long) 0, IO_SYNC, NOCRED, NULL);
1446 	cache_purge(DETOV(dep));
1447 
1448 out:	;
1449 	if (ap->a_dvp)
1450 		vput(ap->a_dvp);
1451 	vput(ap->a_vp);
1452 	return error;
1453 }
1454 
1455 /*
1456  * DOS filesystems don't know what symlinks are.
1457  */
1458 static int
1459 msdosfs_symlink(ap)
1460 	struct vop_symlink_args /* {
1461 		struct vnode *a_dvp;
1462 		struct vnode **a_vpp;
1463 		struct componentname *a_cnp;
1464 		struct vattr *a_vap;
1465 		char *a_target;
1466 	} */ *ap;
1467 {
1468 	free(ap->a_cnp->cn_pnbuf, M_NAMEI);
1469 	vput(ap->a_dvp);
1470 	return EINVAL;
1471 }
1472 
1473 /*
1474  * Dummy dirents to simulate the "." and ".." entries of the root directory
1475  * in a dos filesystem.  Dos doesn't provide these. Note that each entry
1476  * must be the same size as a dos directory entry (32 bytes).
1477  */
1478 static struct dos_dirent {
1479 	u_long d_fileno;
1480 	u_short d_reclen;
1481 	u_char d_type;
1482 	u_char d_namlen;
1483 	u_char d_name[24];
1484 }          rootdots[2] = {
1485 
1486 	{
1487 		1,		/* d_fileno			 */
1488 		sizeof(struct direntry),	/* d_reclen			 */
1489 		DT_DIR,		/* d_type			 */
1490 		1,		/* d_namlen			 */
1491 		"."		/* d_name			 */
1492 	},
1493 	{
1494 		1,		/* d_fileno			 */
1495 		sizeof(struct direntry),	/* d_reclen			 */
1496 		DT_DIR,		/* d_type			 */
1497 		2,		/* d_namlen			 */
1498 		".."		/* d_name			 */
1499 	}
1500 };
1501 
1502 static int
1503 msdosfs_readdir(ap)
1504 	struct vop_readdir_args /* {
1505 		struct vnode *a_vp;
1506 		struct uio *a_uio;
1507 		struct ucred *a_cred;
1508 		int *a_eofflag;
1509 		u_int *a_cookies;
1510 		int a_ncookies;
1511 	} */ *ap;
1512 {
1513 	int error = 0;
1514 	int diff;
1515 	char pushout;
1516 	long n;
1517 	long on;
1518 	long lost;
1519 	long count;
1520 	u_long cn;
1521 	u_long fileno;
1522 	long bias = 0;
1523 	daddr_t bn;
1524 	daddr_t lbn;
1525 	struct buf *bp;
1526 	struct denode *dep = VTODE(ap->a_vp);
1527 	struct msdosfsmount *pmp = dep->de_pmp;
1528 	struct direntry *dentp;
1529 	struct dirent *prev;
1530 	struct dirent *crnt;
1531 	u_char dirbuf[512];	/* holds converted dos directories */
1532 	struct uio *uio = ap->a_uio;
1533 	off_t off;
1534 	int ncookies = 0;
1535 
1536 #ifdef MSDOSFS_DEBUG
1537 	printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n",
1538 	       ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
1539 #endif
1540 
1541 	/*
1542 	 * msdosfs_readdir() won't operate properly on regular files since
1543 	 * it does i/o only with the the filesystem vnode, and hence can
1544 	 * retrieve the wrong block from the buffer cache for a plain file.
1545 	 * So, fail attempts to readdir() on a plain file.
1546 	 */
1547 	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1548 		return ENOTDIR;
1549 
1550 	/*
1551 	 * If the user buffer is smaller than the size of one dos directory
1552 	 * entry or the file offset is not a multiple of the size of a
1553 	 * directory entry, then we fail the read.
1554 	 */
1555 	count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
1556 	lost = uio->uio_resid - count;
1557 	if (count < sizeof(struct direntry) ||
1558 	    (uio->uio_offset & (sizeof(struct direntry) - 1)))
1559 		return EINVAL;
1560 	uio->uio_resid = count;
1561 	uio->uio_iov->iov_len = count;
1562 	off = uio->uio_offset;
1563 
1564 	/*
1565 	 * If they are reading from the root directory then, we simulate
1566 	 * the . and .. entries since these don't exist in the root
1567 	 * directory.  We also set the offset bias to make up for having to
1568 	 * simulate these entries. By this I mean that at file offset 64 we
1569 	 * read the first entry in the root directory that lives on disk.
1570 	 */
1571 	if (dep->de_StartCluster == MSDOSFSROOT) {
1572 		/*
1573 		 * printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
1574 		 *	  uio->uio_offset);
1575 		 */
1576 		bias = 2 * sizeof(struct direntry);
1577 		if (uio->uio_offset < 2 * sizeof(struct direntry)) {
1578 			if (uio->uio_offset
1579 			    && uio->uio_offset != sizeof(struct direntry)) {
1580 				error = EINVAL;
1581 				goto out;
1582 			}
1583 			n = 1;
1584 			if (!uio->uio_offset) {
1585 				n = 2;
1586 				ncookies++;
1587 			}
1588 			ncookies++;
1589 			error = uiomove((char *) rootdots + uio->uio_offset,
1590 					n * sizeof(struct direntry), uio);
1591 		}
1592 	}
1593 	while (!error && uio->uio_resid > 0) {
1594 		lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift;
1595 		on = (uio->uio_offset - bias) & pmp->pm_crbomask;
1596 		n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
1597 		diff = dep->de_FileSize - (uio->uio_offset - bias);
1598 		if (diff <= 0) {
1599 			if(ap->a_eofflag)
1600 				*ap->a_eofflag = 1;
1601 			if(ap->a_ncookies != NULL) {
1602 				u_int *cookies;
1603 
1604 				MALLOC(cookies, u_int *, 1 * sizeof(u_int),
1605 				       M_TEMP, M_WAITOK);
1606 				*ap->a_ncookies = 0;
1607 				*ap->a_cookies = cookies;
1608 			}
1609 			return 0;
1610 		}
1611 		if (diff < n)
1612 			n = diff;
1613 		error = pcbmap(dep, lbn, &bn, &cn);
1614 		if (error)
1615 			break;
1616 		error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp);
1617 		n = min(n, pmp->pm_bpcluster - bp->b_resid);
1618 		if (error) {
1619 			brelse(bp);
1620 			return error;
1621 		}
1622 
1623 		/*
1624 		 * code to convert from dos directory entries to ufs
1625 		 * directory entries
1626 		 */
1627 		pushout = 0;
1628 		dentp = (struct direntry *)(bp->b_data + on);
1629 		prev = 0;
1630 		crnt = (struct dirent *) dirbuf;
1631 		while ((char *) dentp < bp->b_data + on + n) {
1632 			/*
1633 			 * printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1634 			 *	  dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1635 			 */
1636 			/*
1637 			 * If we have an empty entry or a slot from a
1638 			 * deleted file, or a volume label entry just
1639 			 * concatenate its space onto the end of the
1640 			 * previous entry or, manufacture an empty entry if
1641 			 * there is no previous entry.
1642 			 */
1643 			if (dentp->deName[0] == SLOT_EMPTY ||
1644 			    dentp->deName[0] == SLOT_DELETED ||
1645 			    (dentp->deAttributes & ATTR_VOLUME)) {
1646 				if (prev) {
1647 					prev->d_reclen += sizeof(struct direntry);
1648 				} else {
1649 					prev = crnt;
1650 					prev->d_fileno = 0;
1651 					prev->d_reclen = sizeof(struct direntry);
1652 					prev->d_type = DT_UNKNOWN;
1653 					prev->d_namlen = 0;
1654 					prev->d_name[0] = 0;
1655 					ncookies++;
1656 				}
1657 			} else {
1658 				/*
1659 				 * this computation of d_fileno must match
1660 				 * the computation of va_fileid in
1661 				 * msdosfs_getattr
1662 				 */
1663 				if (dentp->deAttributes & ATTR_DIRECTORY) {
1664 					/* if this is the root directory */
1665 					fileno = getushort(dentp->deStartCluster);
1666 					if (fileno == MSDOSFSROOT)
1667 						fileno = 1;
1668 				} else {
1669 					/*
1670 					 * if the file's dirent lives in
1671 					 * root dir
1672 					 */
1673 					if ((fileno = cn) == MSDOSFSROOT)
1674 						fileno = 1;
1675 					fileno = (fileno << 16) |
1676 					    ((dentp - (struct direntry *) bp->b_data) & 0xffff);
1677 				}
1678 				crnt->d_fileno = fileno;
1679 				crnt->d_reclen = sizeof(struct direntry);
1680 				crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY)
1681 					         ? DT_DIR : DT_REG;
1682 				crnt->d_namlen = dos2unixfn(dentp->deName,
1683 							    (u_char *)crnt->d_name);
1684 				/*
1685 				 * printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n",
1686 				 *	  crnt->d_name, crnt->d_fileno, dentp->deAttributes,
1687 				 *	  dentp->deStartCluster);
1688 				 */
1689 				prev = crnt;
1690 				ncookies++;
1691 			}
1692 			dentp++;
1693 
1694 			crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry));
1695 			pushout = 1;
1696 
1697 			/*
1698 			 * If our intermediate buffer is full then copy its
1699 			 * contents to user space.  I would just use the
1700 			 * buffer the buf header points to but, I'm afraid
1701 			 * that when we brelse() it someone else might find
1702 			 * it in the cache and think its contents are
1703 			 * valid.  Maybe there is a way to invalidate the
1704 			 * buffer before brelse()'ing it.
1705 			 */
1706 			if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) {
1707 				pushout = 0;
1708 				error = uiomove(dirbuf, sizeof(dirbuf), uio);
1709 				if (error)
1710 					break;
1711 				prev = 0;
1712 				crnt = (struct dirent *) dirbuf;
1713 			}
1714 		}
1715 		if (pushout) {
1716 			pushout = 0;
1717 			error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf,
1718 			    uio);
1719 		}
1720 
1721 #if 0
1722 		/*
1723 		 * If we have read everything from this block or have read
1724 		 * to end of file then we are done with this block.  Mark
1725 		 * it to say the buffer can be reused if need be.
1726 		 */
1727 		if (n + on == pmp->pm_bpcluster ||
1728 		    (uio->uio_offset - bias) == dep->de_FileSize)
1729 			bp->b_flags |= B_AGE;
1730 #endif /* if 0 */
1731 		brelse(bp);
1732 		if (n == 0)
1733 			break;
1734 	}
1735 out:	;
1736 	uio->uio_resid += lost;
1737 	if (!error && ap->a_ncookies != NULL) {
1738 		struct dirent* dpStart;
1739 		struct dirent* dpEnd;
1740 		struct dirent* dp;
1741 		u_int *cookies;
1742 		u_int *cookiep;
1743 
1744 		if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
1745 			panic("msdosfs_readdir: unexpected uio from NFS server");
1746 		dpStart = (struct dirent *)
1747 		     (uio->uio_iov->iov_base - (uio->uio_offset - off));
1748 		dpEnd = (struct dirent *) uio->uio_iov->iov_base;
1749 		MALLOC(cookies, u_int *, ncookies * sizeof(u_int),
1750 		       M_TEMP, M_WAITOK);
1751 		for (dp = dpStart, cookiep = cookies;
1752 		     dp < dpEnd;
1753 		     dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
1754 			off += dp->d_reclen;
1755 			*cookiep++ = (u_int) off;
1756 		}
1757 		*ap->a_ncookies = ncookies;
1758 		*ap->a_cookies = cookies;
1759 	}
1760 
1761 	/*
1762 	 * Set the eofflag (NFS uses it)
1763 	 */
1764 	if (ap->a_eofflag)
1765 		if (dep->de_FileSize - (uio->uio_offset - bias) <= 0)
1766 			*ap->a_eofflag = 1;
1767 		else
1768 			*ap->a_eofflag = 0;
1769 
1770 	return error;
1771 }
1772 
1773 /*
1774  * DOS filesystems don't know what symlinks are.
1775  */
1776 static int
1777 msdosfs_readlink(ap)
1778 	struct vop_readlink_args /* {
1779 		struct vnode *a_vp;
1780 		struct uio *a_uio;
1781 		struct ucred *a_cred;
1782 	} */ *ap;
1783 {
1784 	return EINVAL;
1785 }
1786 
1787 static int
1788 msdosfs_abortop(ap)
1789 	struct vop_abortop_args /* {
1790 		struct vnode *a_dvp;
1791 		struct componentname *a_cnp;
1792 	} */ *ap;
1793 {
1794 	if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
1795 		FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
1796 	return 0;
1797 }
1798 
1799 static int
1800 msdosfs_lock(ap)
1801 	struct vop_lock_args /* {
1802 		struct vnode *a_vp;
1803 	} */ *ap;
1804 {
1805 	struct denode *dep = VTODE(ap->a_vp);
1806 
1807 	while (dep->de_flag & DE_LOCKED) {
1808 		dep->de_flag |= DE_WANTED;
1809 		if (dep->de_lockholder == curproc->p_pid)
1810 			panic("msdosfs_lock: locking against myself");
1811 		dep->de_lockwaiter = curproc->p_pid;
1812 		(void) tsleep((caddr_t) dep, PINOD, "msdlck", 0);
1813 	}
1814 	dep->de_lockwaiter = 0;
1815 	dep->de_lockholder = curproc->p_pid;
1816 	dep->de_flag |= DE_LOCKED;
1817 	return 0;
1818 }
1819 
1820 static int
1821 msdosfs_unlock(ap)
1822 	struct vop_unlock_args /* {
1823 		struct vnode *vp;
1824 	} */ *ap;
1825 {
1826 	struct denode *dep = VTODE(ap->a_vp);
1827 
1828 	if (!(dep->de_flag & DE_LOCKED))
1829 		panic("msdosfs_unlock: denode not locked");
1830 	dep->de_lockholder = 0;
1831 	dep->de_flag &= ~DE_LOCKED;
1832 	if (dep->de_flag & DE_WANTED) {
1833 		dep->de_flag &= ~DE_WANTED;
1834 		wakeup((caddr_t) dep);
1835 	}
1836 	return 0;
1837 }
1838 
1839 static int
1840 msdosfs_islocked(ap)
1841 	struct vop_islocked_args /* {
1842 		struct vnode *a_vp;
1843 	} */ *ap;
1844 {
1845 	return VTODE(ap->a_vp)->de_flag & DE_LOCKED ? 1 : 0;
1846 }
1847 
1848 /*
1849  * vp  - address of vnode file the file
1850  * bn  - which cluster we are interested in mapping to a filesystem block number.
1851  * vpp - returns the vnode for the block special file holding the filesystem
1852  *	 containing the file of interest
1853  * bnp - address of where to return the filesystem relative block number
1854  */
1855 static int
1856 msdosfs_bmap(ap)
1857 	struct vop_bmap_args /* {
1858 		struct vnode *a_vp;
1859 		daddr_t a_bn;
1860 		struct vnode **a_vpp;
1861 		daddr_t *a_bnp;
1862 		int *a_runp;
1863 		int *a_runb;
1864 	} */ *ap;
1865 {
1866 	struct denode *dep = VTODE(ap->a_vp);
1867 
1868 	if (ap->a_vpp != NULL)
1869 		*ap->a_vpp = dep->de_devvp;
1870 	if (ap->a_bnp == NULL)
1871 		return 0;
1872 	if (ap->a_runp) {
1873 		/*
1874 		 * Sequential clusters should be counted here.
1875 		 */
1876 		*ap->a_runp = 0;
1877 	}
1878 	if (ap->a_runb) {
1879 		*ap->a_runb = 0;
1880 	}
1881 	return pcbmap(dep, ap->a_bn, ap->a_bnp, 0);
1882 }
1883 
1884 static int
1885 msdosfs_reallocblks(ap)
1886 	struct vop_reallocblks_args /* {
1887 		struct vnode *a_vp;
1888 		struct cluster_save *a_buflist;
1889 	} */ *ap;
1890 {
1891 	/* Currently no support for clustering */		/* XXX */
1892 	return ENOSPC;
1893 }
1894 
1895 static int
1896 msdosfs_strategy(ap)
1897 	struct vop_strategy_args /* {
1898 		struct buf *a_bp;
1899 	} */ *ap;
1900 {
1901 	struct buf *bp = ap->a_bp;
1902 	struct denode *dep = VTODE(bp->b_vp);
1903 	struct vnode *vp;
1904 	int error = 0;
1905 
1906 	if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR)
1907 		panic("msdosfs_strategy: spec");
1908 	/*
1909 	 * If we don't already know the filesystem relative block number
1910 	 * then get it using pcbmap().  If pcbmap() returns the block
1911 	 * number as -1 then we've got a hole in the file.  DOS filesystems
1912 	 * don't allow files with holes, so we shouldn't ever see this.
1913 	 */
1914 	if (bp->b_blkno == bp->b_lblkno) {
1915 		error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0);
1916 		if (error)
1917 			bp->b_blkno = -1;
1918 		if (bp->b_blkno == -1)
1919 			clrbuf(bp);
1920 	}
1921 	if (bp->b_blkno == -1) {
1922 		biodone(bp);
1923 		return error;
1924 	}
1925 #ifdef DIAGNOSTIC
1926 #endif
1927 	/*
1928 	 * Read/write the block from/to the disk that contains the desired
1929 	 * file block.
1930 	 */
1931 	vp = dep->de_devvp;
1932 	bp->b_dev = vp->v_rdev;
1933 	VOCALL(vp->v_op, VOFFSET(vop_strategy), ap);
1934 	return 0;
1935 }
1936 
1937 static int
1938 msdosfs_print(ap)
1939 	struct vop_print_args /* {
1940 		struct vnode *vp;
1941 	} */ *ap;
1942 {
1943 	struct denode *dep = VTODE(ap->a_vp);
1944 
1945 	printf(
1946 	    "tag VT_MSDOSFS, startcluster %d, dircluster %ld, diroffset %ld ",
1947 	       dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1948 	printf(" dev %d, %d, %s\n",
1949 	       major(dep->de_dev), minor(dep->de_dev),
1950 	       dep->de_flag & DE_LOCKED ? "(LOCKED)" : "");
1951 	if (dep->de_lockholder) {
1952 		printf("    owner pid %d", (int)dep->de_lockholder);
1953 		if (dep->de_lockwaiter)
1954 			printf(" waiting pid %d", (int)dep->de_lockwaiter);
1955 		printf("\n");
1956 	}
1957 	return 0;
1958 }
1959 
1960 static int
1961 msdosfs_advlock(ap)
1962 	struct vop_advlock_args /* {
1963 		struct vnode *a_vp;
1964 		caddr_t a_id;
1965 		int a_op;
1966 		struct flock *a_fl;
1967 		int a_flags;
1968 	} */ *ap;
1969 {
1970 	return EINVAL;		/* we don't do locking yet		 */
1971 }
1972 
1973 static int
1974 msdosfs_pathconf(ap)
1975 	struct vop_pathconf_args /* {
1976 		struct vnode *a_vp;
1977 		int a_name;
1978 		int *a_retval;
1979 	} */ *ap;
1980 {
1981 	switch (ap->a_name) {
1982 	case _PC_LINK_MAX:
1983 		*ap->a_retval = 1;
1984 		return 0;
1985 	case _PC_NAME_MAX:
1986 		*ap->a_retval = 12;
1987 		return 0;
1988 	case _PC_PATH_MAX:
1989 		*ap->a_retval = PATH_MAX; /* 255? */
1990 		return 0;
1991 	case _PC_CHOWN_RESTRICTED:
1992 		*ap->a_retval = 1;
1993 		return 0;
1994 	case _PC_NO_TRUNC:
1995 		*ap->a_retval = 0;
1996 		return 0;
1997 	default:
1998 		return EINVAL;
1999 	}
2000 }
2001 
2002 /* Global vfs data structures for msdosfs */
2003 vop_t **msdosfs_vnodeop_p;
2004 static struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
2005 	{ &vop_default_desc, (vop_t *)vn_default_error },
2006 	{ &vop_lookup_desc, (vop_t *)msdosfs_lookup },		/* lookup */
2007 	{ &vop_create_desc, (vop_t *)msdosfs_create },		/* create */
2008 	{ &vop_mknod_desc, (vop_t *)msdosfs_mknod },		/* mknod */
2009 	{ &vop_open_desc, (vop_t *)msdosfs_open },		/* open */
2010 	{ &vop_close_desc, (vop_t *)msdosfs_close },		/* close */
2011 	{ &vop_access_desc, (vop_t *)msdosfs_access },		/* access */
2012 	{ &vop_getattr_desc, (vop_t *)msdosfs_getattr },	/* getattr */
2013 	{ &vop_setattr_desc, (vop_t *)msdosfs_setattr },	/* setattr */
2014 	{ &vop_read_desc, (vop_t *)msdosfs_read },		/* read */
2015 	{ &vop_write_desc, (vop_t *)msdosfs_write },		/* write */
2016 	{ &vop_ioctl_desc, (vop_t *)msdosfs_ioctl },		/* ioctl */
2017 	{ &vop_select_desc, (vop_t *)msdosfs_select },		/* select */
2018 	{ &vop_mmap_desc, (vop_t *)msdosfs_mmap },		/* mmap */
2019 	{ &vop_fsync_desc, (vop_t *)msdosfs_fsync },		/* fsync */
2020 	{ &vop_seek_desc, (vop_t *)msdosfs_seek },		/* seek */
2021 	{ &vop_remove_desc, (vop_t *)msdosfs_remove },		/* remove */
2022 	{ &vop_link_desc, (vop_t *)msdosfs_link },		/* link */
2023 	{ &vop_rename_desc, (vop_t *)msdosfs_rename },		/* rename */
2024 	{ &vop_mkdir_desc, (vop_t *)msdosfs_mkdir },		/* mkdir */
2025 	{ &vop_rmdir_desc, (vop_t *)msdosfs_rmdir },		/* rmdir */
2026 	{ &vop_symlink_desc, (vop_t *)msdosfs_symlink },	/* symlink */
2027 	{ &vop_readdir_desc, (vop_t *)msdosfs_readdir },	/* readdir */
2028 	{ &vop_readlink_desc, (vop_t *)msdosfs_readlink },	/* readlink */
2029 	{ &vop_abortop_desc, (vop_t *)msdosfs_abortop },	/* abortop */
2030 	{ &vop_inactive_desc, (vop_t *)msdosfs_inactive },	/* inactive */
2031 	{ &vop_reclaim_desc, (vop_t *)msdosfs_reclaim },	/* reclaim */
2032 	{ &vop_lock_desc, (vop_t *)msdosfs_lock },		/* lock */
2033 	{ &vop_unlock_desc, (vop_t *)msdosfs_unlock },		/* unlock */
2034 	{ &vop_bmap_desc, (vop_t *)msdosfs_bmap },		/* bmap */
2035 	{ &vop_strategy_desc, (vop_t *)msdosfs_strategy },	/* strategy */
2036 	{ &vop_print_desc, (vop_t *)msdosfs_print },		/* print */
2037 	{ &vop_islocked_desc, (vop_t *)msdosfs_islocked },	/* islocked */
2038 	{ &vop_pathconf_desc, (vop_t *)msdosfs_pathconf },	/* pathconf */
2039 	{ &vop_advlock_desc, (vop_t *)msdosfs_advlock },	/* advlock */
2040 	{ &vop_reallocblks_desc, (vop_t *)msdosfs_reallocblks },	/* reallocblks */
2041 	{ &vop_bwrite_desc, (vop_t *)vn_bwrite },		/* bwrite */
2042 	{ NULL, NULL }
2043 };
2044 static struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
2045 	{ &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
2046 
2047 VNODEOP_SET(msdosfs_vnodeop_opv_desc);
2048