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