xref: /freebsd/sys/fs/msdosfs/msdosfs_vnops.c (revision 91b5592a1e1af97480d615cf508be05b5674d2f3)
1952a6212SJordan K. Hubbard /*	$NetBSD: msdosfs_vnops.c,v 1.68 1998/02/10 14:10:04 mrg Exp $	*/
227a0bc89SDoug Rabson 
327a0bc89SDoug Rabson /*-
4d63027b6SPedro F. Giffuni  * SPDX-License-Identifier: BSD-4-Clause
5d63027b6SPedro F. Giffuni  *
6952a6212SJordan K. Hubbard  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
7952a6212SJordan K. Hubbard  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
827a0bc89SDoug Rabson  * All rights reserved.
927a0bc89SDoug Rabson  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
1027a0bc89SDoug Rabson  *
1127a0bc89SDoug Rabson  * Redistribution and use in source and binary forms, with or without
1227a0bc89SDoug Rabson  * modification, are permitted provided that the following conditions
1327a0bc89SDoug Rabson  * are met:
1427a0bc89SDoug Rabson  * 1. Redistributions of source code must retain the above copyright
1527a0bc89SDoug Rabson  *    notice, this list of conditions and the following disclaimer.
1627a0bc89SDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
1727a0bc89SDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
1827a0bc89SDoug Rabson  *    documentation and/or other materials provided with the distribution.
1927a0bc89SDoug Rabson  * 3. All advertising materials mentioning features or use of this software
2027a0bc89SDoug Rabson  *    must display the following acknowledgement:
2127a0bc89SDoug Rabson  *	This product includes software developed by TooLs GmbH.
2227a0bc89SDoug Rabson  * 4. The name of TooLs GmbH may not be used to endorse or promote products
2327a0bc89SDoug Rabson  *    derived from this software without specific prior written permission.
2427a0bc89SDoug Rabson  *
2527a0bc89SDoug Rabson  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2627a0bc89SDoug Rabson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2727a0bc89SDoug Rabson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2827a0bc89SDoug Rabson  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2927a0bc89SDoug Rabson  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
3027a0bc89SDoug Rabson  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
3127a0bc89SDoug Rabson  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3227a0bc89SDoug Rabson  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3327a0bc89SDoug Rabson  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3427a0bc89SDoug Rabson  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3527a0bc89SDoug Rabson  */
36d167cf6fSWarner Losh /*-
3727a0bc89SDoug Rabson  * Written by Paul Popelka (paulp@uts.amdahl.com)
3827a0bc89SDoug Rabson  *
3927a0bc89SDoug Rabson  * You can do anything you want with this software, just don't say you wrote
4027a0bc89SDoug Rabson  * it, and don't remove this notice.
4127a0bc89SDoug Rabson  *
4227a0bc89SDoug Rabson  * This software is provided "as is".
4327a0bc89SDoug Rabson  *
4427a0bc89SDoug Rabson  * The author supplies this software to be publicly redistributed on the
4527a0bc89SDoug Rabson  * understanding that the author is not responsible for the correct
4627a0bc89SDoug Rabson  * functioning of this software in any circumstances and is not liable for
4727a0bc89SDoug Rabson  * any damages caused by this software.
4827a0bc89SDoug Rabson  *
4927a0bc89SDoug Rabson  * October 1992
5027a0bc89SDoug Rabson  */
5127a0bc89SDoug Rabson 
5227a0bc89SDoug Rabson #include <sys/param.h>
5327a0bc89SDoug Rabson #include <sys/systm.h>
54d2bb66baSBruce Evans #include <sys/bio.h>
55d2bb66baSBruce Evans #include <sys/buf.h>
56d2bb66baSBruce Evans #include <sys/clock.h>
57d2bb66baSBruce Evans #include <sys/dirent.h>
58eba34270SBruce Evans #include <sys/lock.h>
59be039c56SBruce Evans #include <sys/lockf.h>
60d2bb66baSBruce Evans #include <sys/malloc.h>
61d2bb66baSBruce Evans #include <sys/mount.h>
62eba34270SBruce Evans #include <sys/mutex.h>
6327a0bc89SDoug Rabson #include <sys/namei.h>
64acd3428bSRobert Watson #include <sys/priv.h>
65d2bb66baSBruce Evans #include <sys/stat.h>
6606965e96SKonstantin Belousov #include <sys/sysctl.h>
67b71fec07SBruce Evans #include <sys/unistd.h>
689ed01c32SGleb Smirnoff #include <sys/vmmeter.h>
6927a0bc89SDoug Rabson #include <sys/vnode.h>
70c3c6d51eSPoul-Henning Kamp 
71c3c6d51eSPoul-Henning Kamp #include <vm/vm.h>
72efeaf95aSDavid Greenman #include <vm/vm_extern.h>
7306965e96SKonstantin Belousov #include <vm/vnode_pager.h>
74a18b1f1dSJason Evans 
751166fb51SRuslan Ermilov #include <fs/msdosfs/bpb.h>
761166fb51SRuslan Ermilov #include <fs/msdosfs/direntry.h>
771166fb51SRuslan Ermilov #include <fs/msdosfs/denode.h>
781166fb51SRuslan Ermilov #include <fs/msdosfs/fat.h>
79d2bb66baSBruce Evans #include <fs/msdosfs/msdosfsmount.h>
80a98ca469SPoul-Henning Kamp 
81a98ca469SPoul-Henning Kamp /*
82a98ca469SPoul-Henning Kamp  * Prototypes for MSDOSFS vnode operations
83a98ca469SPoul-Henning Kamp  */
846fde64c7SPoul-Henning Kamp static vop_create_t	msdosfs_create;
856fde64c7SPoul-Henning Kamp static vop_mknod_t	msdosfs_mknod;
8672b3e305SPeter Edwards static vop_open_t	msdosfs_open;
876fde64c7SPoul-Henning Kamp static vop_close_t	msdosfs_close;
886fde64c7SPoul-Henning Kamp static vop_access_t	msdosfs_access;
896fde64c7SPoul-Henning Kamp static vop_getattr_t	msdosfs_getattr;
906fde64c7SPoul-Henning Kamp static vop_setattr_t	msdosfs_setattr;
916fde64c7SPoul-Henning Kamp static vop_read_t	msdosfs_read;
926fde64c7SPoul-Henning Kamp static vop_write_t	msdosfs_write;
936fde64c7SPoul-Henning Kamp static vop_fsync_t	msdosfs_fsync;
946fde64c7SPoul-Henning Kamp static vop_remove_t	msdosfs_remove;
956fde64c7SPoul-Henning Kamp static vop_link_t	msdosfs_link;
966fde64c7SPoul-Henning Kamp static vop_rename_t	msdosfs_rename;
976fde64c7SPoul-Henning Kamp static vop_mkdir_t	msdosfs_mkdir;
986fde64c7SPoul-Henning Kamp static vop_rmdir_t	msdosfs_rmdir;
996fde64c7SPoul-Henning Kamp static vop_symlink_t	msdosfs_symlink;
1006fde64c7SPoul-Henning Kamp static vop_readdir_t	msdosfs_readdir;
1016fde64c7SPoul-Henning Kamp static vop_bmap_t	msdosfs_bmap;
10206965e96SKonstantin Belousov static vop_getpages_t	msdosfs_getpages;
1036fde64c7SPoul-Henning Kamp static vop_strategy_t	msdosfs_strategy;
1046fde64c7SPoul-Henning Kamp static vop_print_t	msdosfs_print;
1056fde64c7SPoul-Henning Kamp static vop_pathconf_t	msdosfs_pathconf;
10610bcafe9SPawel Jakub Dawidek static vop_vptofh_t	msdosfs_vptofh;
107a98ca469SPoul-Henning Kamp 
10827a0bc89SDoug Rabson /*
10927a0bc89SDoug Rabson  * Some general notes:
11027a0bc89SDoug Rabson  *
11127a0bc89SDoug Rabson  * In the ufs filesystem the inodes, superblocks, and indirect blocks are
11227a0bc89SDoug Rabson  * read/written using the vnode for the filesystem. Blocks that represent
11327a0bc89SDoug Rabson  * the contents of a file are read/written using the vnode for the file
11427a0bc89SDoug Rabson  * (including directories when they are read/written as files). This
11527a0bc89SDoug Rabson  * presents problems for the dos filesystem because data that should be in
11627a0bc89SDoug Rabson  * an inode (if dos had them) resides in the directory itself.  Since we
11727a0bc89SDoug Rabson  * must update directory entries without the benefit of having the vnode
11827a0bc89SDoug Rabson  * for the directory we must use the vnode for the filesystem.  This means
11927a0bc89SDoug Rabson  * that when a directory is actually read/written (via read, write, or
12027a0bc89SDoug Rabson  * readdir, or seek) we must use the vnode for the filesystem instead of
12127a0bc89SDoug Rabson  * the vnode for the directory as would happen in ufs. This is to insure we
122b3a15dddSPedro F. Giffuni  * retrieve the correct block from the buffer cache since the hash value is
12327a0bc89SDoug Rabson  * based upon the vnode address and the desired block number.
12427a0bc89SDoug Rabson  */
12527a0bc89SDoug Rabson 
12627a0bc89SDoug Rabson /*
12727a0bc89SDoug Rabson  * Create a regular file. On entry the directory to contain the file being
12827a0bc89SDoug Rabson  * created is locked.  We must release before we return. We must also free
1298f7859e8SMateusz Guzik  * the pathname buffer pointed at by cnp->cn_pnbuf, always on error.
13027a0bc89SDoug Rabson  */
131a98ca469SPoul-Henning Kamp static int
msdosfs_create(struct vop_create_args * ap)13210c9700fSEd Maste msdosfs_create(struct vop_create_args *ap)
13327a0bc89SDoug Rabson {
13427a0bc89SDoug Rabson 	struct componentname *cnp = ap->a_cnp;
13527a0bc89SDoug Rabson 	struct denode ndirent;
13627a0bc89SDoug Rabson 	struct denode *dep;
13727a0bc89SDoug Rabson 	struct denode *pdep = VTODE(ap->a_dvp);
13827a0bc89SDoug Rabson 	struct timespec ts;
13927a0bc89SDoug Rabson 	int error;
14027a0bc89SDoug Rabson 
14127a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
142952a6212SJordan K. Hubbard 	printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
14327a0bc89SDoug Rabson #endif
14427a0bc89SDoug Rabson 
14527a0bc89SDoug Rabson 	/*
146952a6212SJordan K. Hubbard 	 * If this is the root directory and there is no space left we
147952a6212SJordan K. Hubbard 	 * can't do anything.  This is because the root directory can not
148952a6212SJordan K. Hubbard 	 * change size.
149952a6212SJordan K. Hubbard 	 */
150952a6212SJordan K. Hubbard 	if (pdep->de_StartCluster == MSDOSFSROOT
151952a6212SJordan K. Hubbard 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
152952a6212SJordan K. Hubbard 		error = ENOSPC;
153952a6212SJordan K. Hubbard 		goto bad;
154952a6212SJordan K. Hubbard 	}
155952a6212SJordan K. Hubbard 
156952a6212SJordan K. Hubbard 	/*
15727a0bc89SDoug Rabson 	 * Create a directory entry for the file, then call createde() to
15827a0bc89SDoug Rabson 	 * have it installed. NOTE: DOS files are always executable.  We
15927a0bc89SDoug Rabson 	 * use the absence of the owner write bit to make the file
16027a0bc89SDoug Rabson 	 * readonly.
16127a0bc89SDoug Rabson 	 */
1626a1c2e1fSEd Maste 	memset(&ndirent, 0, sizeof(ndirent));
163952a6212SJordan K. Hubbard 	error = uniqdosname(pdep, cnp, ndirent.de_Name);
164952a6212SJordan K. Hubbard 	if (error)
165952a6212SJordan K. Hubbard 		goto bad;
166952a6212SJordan K. Hubbard 
1677da1a731SKenneth D. Merry 	ndirent.de_Attributes = ATTR_ARCHIVE;
168bad3d41dSDmitrij Tejblum 	ndirent.de_LowerCase = 0;
16927a0bc89SDoug Rabson 	ndirent.de_StartCluster = 0;
17027a0bc89SDoug Rabson 	ndirent.de_FileSize = 0;
171952a6212SJordan K. Hubbard 	ndirent.de_pmp = pdep->de_pmp;
172952a6212SJordan K. Hubbard 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
173b732ceb6SPedro F. Giffuni 	vfs_timestamp(&ts);
174952a6212SJordan K. Hubbard 	DETIMES(&ndirent, &ts, &ts, &ts);
175952a6212SJordan K. Hubbard 	error = createde(&ndirent, pdep, &dep, cnp);
176952a6212SJordan K. Hubbard 	if (error)
177952a6212SJordan K. Hubbard 		goto bad;
178952a6212SJordan K. Hubbard 	*ap->a_vpp = DETOV(dep);
179789bdfdbSKonstantin Belousov 	if ((cnp->cn_flags & MAKEENTRY) != 0)
180789bdfdbSKonstantin Belousov 		cache_enter(ap->a_dvp, *ap->a_vpp, cnp);
181952a6212SJordan K. Hubbard 	return (0);
182952a6212SJordan K. Hubbard 
183952a6212SJordan K. Hubbard bad:
184952a6212SJordan K. Hubbard 	return (error);
18527a0bc89SDoug Rabson }
18627a0bc89SDoug Rabson 
187a98ca469SPoul-Henning Kamp static int
msdosfs_mknod(struct vop_mknod_args * ap)18810c9700fSEd Maste msdosfs_mknod(struct vop_mknod_args *ap)
18927a0bc89SDoug Rabson {
190c0f5121cSBruce Evans 
191952a6212SJordan K. Hubbard     return (EINVAL);
19227a0bc89SDoug Rabson }
19327a0bc89SDoug Rabson 
194a98ca469SPoul-Henning Kamp static int
msdosfs_open(struct vop_open_args * ap)19510c9700fSEd Maste msdosfs_open(struct vop_open_args *ap)
19672b3e305SPeter Edwards {
19772b3e305SPeter Edwards 	struct denode *dep = VTODE(ap->a_vp);
19872b3e305SPeter Edwards 	vnode_create_vobject(ap->a_vp, dep->de_FileSize, ap->a_td);
19972b3e305SPeter Edwards 	return 0;
20072b3e305SPeter Edwards }
20172b3e305SPeter Edwards 
20272b3e305SPeter Edwards static int
msdosfs_close(struct vop_close_args * ap)20310c9700fSEd Maste msdosfs_close(struct vop_close_args *ap)
20427a0bc89SDoug Rabson {
20527a0bc89SDoug Rabson 	struct vnode *vp = ap->a_vp;
20627a0bc89SDoug Rabson 	struct denode *dep = VTODE(vp);
207952a6212SJordan K. Hubbard 	struct timespec ts;
20827a0bc89SDoug Rabson 
2094d93c0beSJeff Roberson 	VI_LOCK(vp);
210952a6212SJordan K. Hubbard 	if (vp->v_usecount > 1) {
211b732ceb6SPedro F. Giffuni 		vfs_timestamp(&ts);
212952a6212SJordan K. Hubbard 		DETIMES(dep, &ts, &ts, &ts);
213952a6212SJordan K. Hubbard 	}
2144d93c0beSJeff Roberson 	VI_UNLOCK(vp);
21527a0bc89SDoug Rabson 	return 0;
21627a0bc89SDoug Rabson }
21727a0bc89SDoug Rabson 
218a98ca469SPoul-Henning Kamp static int
msdosfs_access(struct vop_access_args * ap)21910c9700fSEd Maste msdosfs_access(struct vop_access_args *ap)
22027a0bc89SDoug Rabson {
221d68a4190SDavid Greenman 	struct vnode *vp = ap->a_vp;
22227a0bc89SDoug Rabson 	struct denode *dep = VTODE(ap->a_vp);
22327a0bc89SDoug Rabson 	struct msdosfsmount *pmp = dep->de_pmp;
22415bc6b2bSEdward Tomasz Napierala 	mode_t file_mode;
22515bc6b2bSEdward Tomasz Napierala 	accmode_t accmode = ap->a_accmode;
22627a0bc89SDoug Rabson 
2277da1a731SKenneth D. Merry 	file_mode = S_IRWXU|S_IRWXG|S_IRWXO;
228c98a31caSTom Rhodes 	file_mode &= (vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
22927a0bc89SDoug Rabson 
230d68a4190SDavid Greenman 	/*
231b7837a91SBruce Evans 	 * Disallow writing to directories and regular files if the
232b7837a91SBruce Evans 	 * filesystem is read-only.
233d68a4190SDavid Greenman 	 */
23415bc6b2bSEdward Tomasz Napierala 	if (accmode & VWRITE) {
235d68a4190SDavid Greenman 		switch (vp->v_type) {
236d68a4190SDavid Greenman 		case VREG:
2377da1a731SKenneth D. Merry 		case VDIR:
238d68a4190SDavid Greenman 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
239d68a4190SDavid Greenman 				return (EROFS);
240d68a4190SDavid Greenman 			break;
241831a80b0SMatthew Dillon 		default:
242831a80b0SMatthew Dillon 			break;
243d68a4190SDavid Greenman 		}
244d68a4190SDavid Greenman 	}
245d68a4190SDavid Greenman 
246e39c53edSPoul-Henning Kamp 	return (vaccess(vp->v_type, file_mode, pmp->pm_uid, pmp->pm_gid,
247d292b194SMateusz Guzik 	    ap->a_accmode, ap->a_cred));
24827a0bc89SDoug Rabson }
24927a0bc89SDoug Rabson 
250a98ca469SPoul-Henning Kamp static int
msdosfs_getattr(struct vop_getattr_args * ap)25110c9700fSEd Maste msdosfs_getattr(struct vop_getattr_args *ap)
25227a0bc89SDoug Rabson {
25327a0bc89SDoug Rabson 	struct denode *dep = VTODE(ap->a_vp);
254952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp = dep->de_pmp;
25527a0bc89SDoug Rabson 	struct vattr *vap = ap->a_vap;
256952a6212SJordan K. Hubbard 	mode_t mode;
257952a6212SJordan K. Hubbard 	struct timespec ts;
258952a6212SJordan K. Hubbard 	u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
2593bc482ecSTim J. Robbins 	uint64_t fileid;
26027a0bc89SDoug Rabson 
261b732ceb6SPedro F. Giffuni 	vfs_timestamp(&ts);
262952a6212SJordan K. Hubbard 	DETIMES(dep, &ts, &ts, &ts);
263c72ae142SJohn Baldwin 	vap->va_fsid = dev2udev(pmp->pm_dev);
26427a0bc89SDoug Rabson 	/*
26527a0bc89SDoug Rabson 	 * The following computation of the fileid must be the same as that
26627a0bc89SDoug Rabson 	 * used in msdosfs_readdir() to compute d_fileno. If not, pwd
26727a0bc89SDoug Rabson 	 * doesn't work.
26827a0bc89SDoug Rabson 	 */
26927a0bc89SDoug Rabson 	if (dep->de_Attributes & ATTR_DIRECTORY) {
2703bc482ecSTim J. Robbins 		fileid = (uint64_t)cntobn(pmp, dep->de_StartCluster) *
2713bc482ecSTim J. Robbins 		    dirsperblk;
272952a6212SJordan K. Hubbard 		if (dep->de_StartCluster == MSDOSFSROOT)
273952a6212SJordan K. Hubbard 			fileid = 1;
27427a0bc89SDoug Rabson 	} else {
2753bc482ecSTim J. Robbins 		fileid = (uint64_t)cntobn(pmp, dep->de_dirclust) *
2763bc482ecSTim J. Robbins 		    dirsperblk;
277952a6212SJordan K. Hubbard 		if (dep->de_dirclust == MSDOSFSROOT)
2783bc482ecSTim J. Robbins 			fileid = (uint64_t)roottobn(pmp, 0) * dirsperblk;
279c0f5121cSBruce Evans 		fileid += (uoff_t)dep->de_diroffset / sizeof(struct direntry);
28027a0bc89SDoug Rabson 	}
28140373cf5SKonstantin Belousov 	vap->va_fileid = fileid;
282f458f2a5SCraig Rodrigues 
283952a6212SJordan K. Hubbard 	mode = S_IRWXU|S_IRWXG|S_IRWXO;
28460965b76SConrad Meyer 	if (dep->de_Attributes & ATTR_READONLY)
28560965b76SConrad Meyer 		mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
286c98a31caSTom Rhodes 	vap->va_mode = mode &
287c98a31caSTom Rhodes 	    (ap->a_vp->v_type == VDIR ? pmp->pm_dirmask : pmp->pm_mask);
288952a6212SJordan K. Hubbard 	vap->va_uid = pmp->pm_uid;
289952a6212SJordan K. Hubbard 	vap->va_gid = pmp->pm_gid;
29027a0bc89SDoug Rabson 	vap->va_nlink = 1;
2914c5a20e3SKonstantin Belousov 	vap->va_rdev = NODEV;
29227a0bc89SDoug Rabson 	vap->va_size = dep->de_FileSize;
2933c960d93SPoul-Henning Kamp 	fattime2timespec(dep->de_MDate, dep->de_MTime, 0, 0, &vap->va_mtime);
2941c5cf521SMaxim Konovalov 	vap->va_ctime = vap->va_mtime;
295952a6212SJordan K. Hubbard 	if (pmp->pm_flags & MSDOSFSMNT_LONGNAME) {
2963c960d93SPoul-Henning Kamp 		fattime2timespec(dep->de_ADate, 0, 0, 0, &vap->va_atime);
2973c960d93SPoul-Henning Kamp 		fattime2timespec(dep->de_CDate, dep->de_CTime, dep->de_CHun,
2981c5cf521SMaxim Konovalov 		    0, &vap->va_birthtime);
299952a6212SJordan K. Hubbard 	} else {
300952a6212SJordan K. Hubbard 		vap->va_atime = vap->va_mtime;
3011c5cf521SMaxim Konovalov 		vap->va_birthtime.tv_sec = -1;
3021c5cf521SMaxim Konovalov 		vap->va_birthtime.tv_nsec = 0;
303952a6212SJordan K. Hubbard 	}
304952a6212SJordan K. Hubbard 	vap->va_flags = 0;
3057da1a731SKenneth D. Merry 	if (dep->de_Attributes & ATTR_ARCHIVE)
3067da1a731SKenneth D. Merry 		vap->va_flags |= UF_ARCHIVE;
3077da1a731SKenneth D. Merry 	if (dep->de_Attributes & ATTR_HIDDEN)
3087da1a731SKenneth D. Merry 		vap->va_flags |= UF_HIDDEN;
3097da1a731SKenneth D. Merry 	if (dep->de_Attributes & ATTR_READONLY)
3107da1a731SKenneth D. Merry 		vap->va_flags |= UF_READONLY;
3117da1a731SKenneth D. Merry 	if (dep->de_Attributes & ATTR_SYSTEM)
3127da1a731SKenneth D. Merry 		vap->va_flags |= UF_SYSTEM;
31327a0bc89SDoug Rabson 	vap->va_gen = 0;
314952a6212SJordan K. Hubbard 	vap->va_blocksize = pmp->pm_bpcluster;
3150728695cSStefan Eßer 	if (dep->de_StartCluster != MSDOSFSROOT)
316952a6212SJordan K. Hubbard 		vap->va_bytes =
317952a6212SJordan K. Hubbard 		    (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask;
3180728695cSStefan Eßer 	else
3190728695cSStefan Eßer 		vap->va_bytes = 0; /* FAT12/FAT16 root dir in reserved area */
32027a0bc89SDoug Rabson 	vap->va_type = ap->a_vp->v_type;
32194a8606fSDoug Rabson 	vap->va_filerev = dep->de_modrev;
322952a6212SJordan K. Hubbard 	return (0);
32327a0bc89SDoug Rabson }
32427a0bc89SDoug Rabson 
325a98ca469SPoul-Henning Kamp static int
msdosfs_setattr(struct vop_setattr_args * ap)32610c9700fSEd Maste msdosfs_setattr(struct vop_setattr_args *ap)
32727a0bc89SDoug Rabson {
328d68a4190SDavid Greenman 	struct vnode *vp = ap->a_vp;
32927a0bc89SDoug Rabson 	struct denode *dep = VTODE(ap->a_vp);
330952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp = dep->de_pmp;
33127a0bc89SDoug Rabson 	struct vattr *vap = ap->a_vap;
33227a0bc89SDoug Rabson 	struct ucred *cred = ap->a_cred;
3330359a12eSAttilio Rao 	struct thread *td = curthread;
334d68a4190SDavid Greenman 	int error = 0;
33527a0bc89SDoug Rabson 
336952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG
3370359a12eSAttilio Rao 	printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
3380359a12eSAttilio Rao 	    ap->a_vp, vap, cred);
339952a6212SJordan K. Hubbard #endif
340952a6212SJordan K. Hubbard 
34127a0bc89SDoug Rabson 	/*
342d68a4190SDavid Greenman 	 * Check for unsettable attributes.
34327a0bc89SDoug Rabson 	 */
344d68a4190SDavid Greenman 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
345d68a4190SDavid Greenman 	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
346d68a4190SDavid Greenman 	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
347d68a4190SDavid Greenman 	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
348952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG
349952a6212SJordan K. Hubbard 		printf("msdosfs_setattr(): returning EINVAL\n");
350027bebe8SEd Maste 		printf("    va_type %d, va_nlink %llx, va_fsid %llx, va_fileid %llx\n",
351027bebe8SEd Maste 		    vap->va_type, (unsigned long long)vap->va_nlink,
352027bebe8SEd Maste 		    (unsigned long long)vap->va_fsid,
353027bebe8SEd Maste 		    (unsigned long long)vap->va_fileid);
354027bebe8SEd Maste 		printf("    va_blocksize %lx, va_rdev %llx, va_bytes %llx, va_gen %lx\n",
355027bebe8SEd Maste 		    vap->va_blocksize, (unsigned long long)vap->va_rdev,
356027bebe8SEd Maste 		    (unsigned long long)vap->va_bytes, vap->va_gen);
357952a6212SJordan K. Hubbard 		printf("    va_uid %x, va_gid %x\n",
358952a6212SJordan K. Hubbard 		    vap->va_uid, vap->va_gid);
359952a6212SJordan K. Hubbard #endif
360d68a4190SDavid Greenman 		return (EINVAL);
36127a0bc89SDoug Rabson 	}
3627da1a731SKenneth D. Merry 
3637da1a731SKenneth D. Merry 	/*
3647da1a731SKenneth D. Merry 	 * We don't allow setting attributes on the root directory.
3657da1a731SKenneth D. Merry 	 * The special case for the root directory is because before
3667da1a731SKenneth D. Merry 	 * FAT32, the root directory didn't have an entry for itself
3677da1a731SKenneth D. Merry 	 * (and was otherwise special).  With FAT32, the root
3687da1a731SKenneth D. Merry 	 * directory is not so special, but still doesn't have an
3697da1a731SKenneth D. Merry 	 * entry for itself.
3707da1a731SKenneth D. Merry 	 */
3717da1a731SKenneth D. Merry 	if (vp->v_vflag & VV_ROOT)
3727da1a731SKenneth D. Merry 		return (EINVAL);
3737da1a731SKenneth D. Merry 
37427a0bc89SDoug Rabson 	if (vap->va_flags != VNOVAL) {
375d68a4190SDavid Greenman 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
376d68a4190SDavid Greenman 			return (EROFS);
377acd3428bSRobert Watson 		if (cred->cr_uid != pmp->pm_uid) {
378cc426dd3SMateusz Guzik 			error = priv_check_cred(cred, PRIV_VFS_ADMIN);
379acd3428bSRobert Watson 			if (error)
380d68a4190SDavid Greenman 				return (error);
381acd3428bSRobert Watson 		}
382a176f73dSBruce Evans 		/*
383a176f73dSBruce Evans 		 * We are very inconsistent about handling unsupported
384ffbdd68eSDmitrij Tejblum 		 * attributes.  We ignored the access time and the
385a176f73dSBruce Evans 		 * read and execute bits.  We were strict for the other
386a176f73dSBruce Evans 		 * attributes.
387a176f73dSBruce Evans 		 */
3887da1a731SKenneth D. Merry 		if (vap->va_flags & ~(UF_ARCHIVE | UF_HIDDEN | UF_READONLY |
3897da1a731SKenneth D. Merry 		    UF_SYSTEM))
390c3a24f69SPeter Wemm 			return EOPNOTSUPP;
3917da1a731SKenneth D. Merry 		if (vap->va_flags & UF_ARCHIVE)
392a176f73dSBruce Evans 			dep->de_Attributes |= ATTR_ARCHIVE;
3937da1a731SKenneth D. Merry 		else
3947da1a731SKenneth D. Merry 			dep->de_Attributes &= ~ATTR_ARCHIVE;
3957da1a731SKenneth D. Merry 		if (vap->va_flags & UF_HIDDEN)
3967da1a731SKenneth D. Merry 			dep->de_Attributes |= ATTR_HIDDEN;
3977da1a731SKenneth D. Merry 		else
3987da1a731SKenneth D. Merry 			dep->de_Attributes &= ~ATTR_HIDDEN;
3997da1a731SKenneth D. Merry 		/* We don't allow changing the readonly bit on directories. */
4007da1a731SKenneth D. Merry 		if (vp->v_type != VDIR) {
4017da1a731SKenneth D. Merry 			if (vap->va_flags & UF_READONLY)
4027da1a731SKenneth D. Merry 				dep->de_Attributes |= ATTR_READONLY;
4037da1a731SKenneth D. Merry 			else
4047da1a731SKenneth D. Merry 				dep->de_Attributes &= ~ATTR_READONLY;
4057da1a731SKenneth D. Merry 		}
4067da1a731SKenneth D. Merry 		if (vap->va_flags & UF_SYSTEM)
4077da1a731SKenneth D. Merry 			dep->de_Attributes |= ATTR_SYSTEM;
4087da1a731SKenneth D. Merry 		else
4097da1a731SKenneth D. Merry 			dep->de_Attributes &= ~ATTR_SYSTEM;
41063e4f22aSBruce Evans 		dep->de_flag |= DE_MODIFIED;
41127a0bc89SDoug Rabson 	}
412d68a4190SDavid Greenman 
413952a6212SJordan K. Hubbard 	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
414952a6212SJordan K. Hubbard 		uid_t uid;
415952a6212SJordan K. Hubbard 		gid_t gid;
416952a6212SJordan K. Hubbard 
417d68a4190SDavid Greenman 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
418d68a4190SDavid Greenman 			return (EROFS);
419952a6212SJordan K. Hubbard 		uid = vap->va_uid;
420952a6212SJordan K. Hubbard 		if (uid == (uid_t)VNOVAL)
421952a6212SJordan K. Hubbard 			uid = pmp->pm_uid;
422952a6212SJordan K. Hubbard 		gid = vap->va_gid;
423952a6212SJordan K. Hubbard 		if (gid == (gid_t)VNOVAL)
424952a6212SJordan K. Hubbard 			gid = pmp->pm_gid;
425acd3428bSRobert Watson 		if (cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid ||
426acd3428bSRobert Watson 		    (gid != pmp->pm_gid && !groupmember(gid, cred))) {
427cc426dd3SMateusz Guzik 			error = priv_check_cred(cred, PRIV_VFS_CHOWN);
428acd3428bSRobert Watson 			if (error)
429acd3428bSRobert Watson 				return (error);
430acd3428bSRobert Watson 		}
431952a6212SJordan K. Hubbard 		if (uid != pmp->pm_uid || gid != pmp->pm_gid)
43240b0939aSDmitrij Tejblum 			return EINVAL;
433d68a4190SDavid Greenman 	}
434952a6212SJordan K. Hubbard 
435d68a4190SDavid Greenman 	if (vap->va_size != VNOVAL) {
436d68a4190SDavid Greenman 		switch (vp->v_type) {
437d68a4190SDavid Greenman 		case VDIR:
438d68a4190SDavid Greenman 			return (EISDIR);
439d68a4190SDavid Greenman 		case VREG:
440b7837a91SBruce Evans 			/*
441b7837a91SBruce Evans 			 * Truncation is only supported for regular files,
442b7837a91SBruce Evans 			 * Disallow it if the filesystem is read-only.
443b7837a91SBruce Evans 			 */
444d68a4190SDavid Greenman 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
445d68a4190SDavid Greenman 				return (EROFS);
446d68a4190SDavid Greenman 			break;
447831a80b0SMatthew Dillon 		default:
448b7837a91SBruce Evans 			/*
449b7837a91SBruce Evans 			 * According to POSIX, the result is unspecified
450b7837a91SBruce Evans 			 * for file types other than regular files,
451b7837a91SBruce Evans 			 * directories and shared memory objects.  We
452b7837a91SBruce Evans 			 * don't support any file types except regular
453b7837a91SBruce Evans 			 * files and directories in this file system, so
454b7837a91SBruce Evans 			 * this (default) case is unreachable and can do
455b7837a91SBruce Evans 			 * anything.  Keep falling through to detrunc()
456b7837a91SBruce Evans 			 * for now.
457b7837a91SBruce Evans 			 */
458831a80b0SMatthew Dillon 			break;
459d68a4190SDavid Greenman 		}
460701b7385SKonstantin Belousov 		error = vn_rlimit_trunc(vap->va_size, td);
461701b7385SKonstantin Belousov 		if (error != 0)
462701b7385SKonstantin Belousov 			return (error);
463c52fd858SEdward Tomasz Napierala 		error = detrunc(dep, vap->va_size, 0, cred);
464d68a4190SDavid Greenman 		if (error)
465d68a4190SDavid Greenman 			return error;
466d68a4190SDavid Greenman 	}
467952a6212SJordan K. Hubbard 	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
468d68a4190SDavid Greenman 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
469d68a4190SDavid Greenman 			return (EROFS);
4707b81a399SKonstantin Belousov 		error = vn_utimes_perm(vp, vap, cred, td);
4717b81a399SKonstantin Belousov 		if (error != 0)
4727b81a399SKonstantin Belousov 			return (error);
473952a6212SJordan K. Hubbard 		if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 &&
47432a95d83SBruce Evans 		    vap->va_atime.tv_sec != VNOVAL) {
47532a95d83SBruce Evans 			dep->de_flag &= ~DE_ACCESS;
4763c960d93SPoul-Henning Kamp 			timespec2fattime(&vap->va_atime, 0,
4773c960d93SPoul-Henning Kamp 			    &dep->de_ADate, NULL, NULL);
47832a95d83SBruce Evans 		}
47932a95d83SBruce Evans 		if (vap->va_mtime.tv_sec != VNOVAL) {
48032a95d83SBruce Evans 			dep->de_flag &= ~DE_UPDATE;
4813c960d93SPoul-Henning Kamp 			timespec2fattime(&vap->va_mtime, 0,
4823c960d93SPoul-Henning Kamp 			    &dep->de_MDate, &dep->de_MTime, NULL);
48332a95d83SBruce Evans 		}
4847da1a731SKenneth D. Merry 		/*
4857da1a731SKenneth D. Merry 		 * We don't set the archive bit when modifying the time of
4867da1a731SKenneth D. Merry 		 * a directory to emulate the Windows/DOS behavior.
4877da1a731SKenneth D. Merry 		 */
4887da1a731SKenneth D. Merry 		if (vp->v_type != VDIR)
489952a6212SJordan K. Hubbard 			dep->de_Attributes |= ATTR_ARCHIVE;
490952a6212SJordan K. Hubbard 		dep->de_flag |= DE_MODIFIED;
491d68a4190SDavid Greenman 	}
492d68a4190SDavid Greenman 	/*
493d68a4190SDavid Greenman 	 * DOS files only have the ability to have their writability
494d68a4190SDavid Greenman 	 * attribute set, so we use the owner write bit to set the readonly
495d68a4190SDavid Greenman 	 * attribute.
496d68a4190SDavid Greenman 	 */
497952a6212SJordan K. Hubbard 	if (vap->va_mode != (mode_t)VNOVAL) {
498d68a4190SDavid Greenman 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
499d68a4190SDavid Greenman 			return (EROFS);
500acd3428bSRobert Watson 		if (cred->cr_uid != pmp->pm_uid) {
501cc426dd3SMateusz Guzik 			error = priv_check_cred(cred, PRIV_VFS_ADMIN);
502acd3428bSRobert Watson 			if (error)
503952a6212SJordan K. Hubbard 				return (error);
504acd3428bSRobert Watson 		}
505952a6212SJordan K. Hubbard 		if (vp->v_type != VDIR) {
506952a6212SJordan K. Hubbard 			/* We ignore the read and execute bits. */
50760965b76SConrad Meyer 			if (vap->va_mode & S_IWUSR)
508d68a4190SDavid Greenman 				dep->de_Attributes &= ~ATTR_READONLY;
509d68a4190SDavid Greenman 			else
510d68a4190SDavid Greenman 				dep->de_Attributes |= ATTR_READONLY;
51132a95d83SBruce Evans 			dep->de_Attributes |= ATTR_ARCHIVE;
512d68a4190SDavid Greenman 			dep->de_flag |= DE_MODIFIED;
513d68a4190SDavid Greenman 		}
514952a6212SJordan K. Hubbard 	}
515cefb5582SBruce Evans 	return (deupdat(dep, 0));
51627a0bc89SDoug Rabson }
51727a0bc89SDoug Rabson 
518a98ca469SPoul-Henning Kamp static int
msdosfs_read(struct vop_read_args * ap)51910c9700fSEd Maste msdosfs_read(struct vop_read_args *ap)
52027a0bc89SDoug Rabson {
52127a0bc89SDoug Rabson 	int error = 0;
522952a6212SJordan K. Hubbard 	int blsize;
52327a0bc89SDoug Rabson 	int isadir;
524526d0bd5SKonstantin Belousov 	ssize_t orig_resid;
52570852092SBoris Popov 	u_int n;
52670852092SBoris Popov 	u_long diff;
52770852092SBoris Popov 	u_long on;
52827a0bc89SDoug Rabson 	daddr_t lbn;
5293f75478cSMike Smith 	daddr_t rablock;
53027a0bc89SDoug Rabson 	int rasize;
53167ddfcafSMatthew Dillon 	int seqcount;
53227a0bc89SDoug Rabson 	struct buf *bp;
53327a0bc89SDoug Rabson 	struct vnode *vp = ap->a_vp;
53427a0bc89SDoug Rabson 	struct denode *dep = VTODE(vp);
53527a0bc89SDoug Rabson 	struct msdosfsmount *pmp = dep->de_pmp;
53627a0bc89SDoug Rabson 	struct uio *uio = ap->a_uio;
53727a0bc89SDoug Rabson 
53827a0bc89SDoug Rabson 	/*
53927a0bc89SDoug Rabson 	 * If they didn't ask for any data, then we are done.
54027a0bc89SDoug Rabson 	 */
541a2a0b22cSBruce Evans 	orig_resid = uio->uio_resid;
542a4e6807cSBruce Evans 	if (orig_resid == 0)
543952a6212SJordan K. Hubbard 		return (0);
54427a0bc89SDoug Rabson 
545a4e6807cSBruce Evans 	/*
546a4e6807cSBruce Evans 	 * The caller is supposed to ensure that
547a4e6807cSBruce Evans 	 * uio->uio_offset >= 0 and uio->uio_resid >= 0.
548a4e6807cSBruce Evans 	 * We don't need to check for large offsets as in ffs because
54922e56aeaSEd Maste 	 * dep->de_FileSize <= MSDOSFS_FILESIZE_MAX < OFF_MAX, so large
550a4e6807cSBruce Evans 	 * offsets cannot cause overflow even in theory.
551a4e6807cSBruce Evans 	 */
552a4e6807cSBruce Evans 
5533a3d82ecSMatthew Dillon 	seqcount = ap->a_ioflag >> IO_SEQSHIFT;
55467ddfcafSMatthew Dillon 
55527a0bc89SDoug Rabson 	isadir = dep->de_Attributes & ATTR_DIRECTORY;
55627a0bc89SDoug Rabson 	do {
557dc22f85fSBoris Popov 		if (uio->uio_offset >= dep->de_FileSize)
558dc22f85fSBoris Popov 			break;
559952a6212SJordan K. Hubbard 		lbn = de_cluster(pmp, uio->uio_offset);
560d34b0a1bSBruce Evans 		rablock = lbn + 1;
561d34b0a1bSBruce Evans 		blsize = pmp->pm_bpcluster;
562d34b0a1bSBruce Evans 		on = uio->uio_offset & pmp->pm_crbomask;
56327a0bc89SDoug Rabson 		/*
56427a0bc89SDoug Rabson 		 * If we are operating on a directory file then be sure to
56527a0bc89SDoug Rabson 		 * do i/o with the vnode for the filesystem instead of the
56627a0bc89SDoug Rabson 		 * vnode for the directory.
56727a0bc89SDoug Rabson 		 */
56827a0bc89SDoug Rabson 		if (isadir) {
56970852092SBoris Popov 			/* convert cluster # to block # */
57070852092SBoris Popov 			error = pcbmap(dep, lbn, &lbn, 0, &blsize);
57137713edcSBoris Popov 			if (error == E2BIG) {
57237713edcSBoris Popov 				error = EINVAL;
57337713edcSBoris Popov 				break;
57437713edcSBoris Popov 			} else if (error)
57570852092SBoris Popov 				break;
576952a6212SJordan K. Hubbard 			error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp);
577d34b0a1bSBruce Evans 		} else if (de_cn2off(pmp, rablock) >= dep->de_FileSize) {
578d34b0a1bSBruce Evans 			error = bread(vp, lbn, blsize, NOCRED, &bp);
5796b6c5f5eSBruce Evans 		} else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) {
5806b6c5f5eSBruce Evans 			error = cluster_read(vp, dep->de_FileSize, lbn, blsize,
581c535690bSKonstantin Belousov 			    NOCRED, on + uio->uio_resid, seqcount, 0, &bp);
582d34b0a1bSBruce Evans 		} else if (seqcount > 1) {
583d34b0a1bSBruce Evans 			rasize = blsize;
584d34b0a1bSBruce Evans 			error = breadn(vp, lbn,
585d34b0a1bSBruce Evans 			    blsize, &rablock, &rasize, 1, NOCRED, &bp);
58667ddfcafSMatthew Dillon 		} else {
58770852092SBoris Popov 			error = bread(vp, lbn, blsize, NOCRED, &bp);
58867ddfcafSMatthew Dillon 		}
58927a0bc89SDoug Rabson 		if (error) {
59027a0bc89SDoug Rabson 			brelse(bp);
591a2a0b22cSBruce Evans 			break;
59227a0bc89SDoug Rabson 		}
59370852092SBoris Popov 		diff = pmp->pm_bpcluster - on;
59470852092SBoris Popov 		n = diff > uio->uio_resid ? uio->uio_resid : diff;
59570852092SBoris Popov 		diff = dep->de_FileSize - uio->uio_offset;
59670852092SBoris Popov 		if (diff < n)
59770852092SBoris Popov 			n = diff;
59870852092SBoris Popov 		diff = blsize - bp->b_resid;
59970852092SBoris Popov 		if (diff < n)
60070852092SBoris Popov 			n = diff;
6012aa39445SKonstantin Belousov 		error = vn_io_fault_uiomove(bp->b_data + on, (int) n, uio);
60227a0bc89SDoug Rabson 		brelse(bp);
60327a0bc89SDoug Rabson 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
604a2a0b22cSBruce Evans 	if (!isadir && (error == 0 || uio->uio_resid != orig_resid) &&
605df5c9c04SKonstantin Belousov 	    (vp->v_mount->mnt_flag & (MNT_NOATIME | MNT_RDONLY)) == 0)
6065c592627SDmitrij Tejblum 		dep->de_flag |= DE_ACCESS;
607952a6212SJordan K. Hubbard 	return (error);
60827a0bc89SDoug Rabson }
60927a0bc89SDoug Rabson 
61027a0bc89SDoug Rabson /*
61127a0bc89SDoug Rabson  * Write data to a file or directory.
61227a0bc89SDoug Rabson  */
613a98ca469SPoul-Henning Kamp static int
msdosfs_write(struct vop_write_args * ap)61410c9700fSEd Maste msdosfs_write(struct vop_write_args *ap)
61527a0bc89SDoug Rabson {
61627a0bc89SDoug Rabson 	int n;
61727a0bc89SDoug Rabson 	int croffset;
618a9c439baSKonstantin Belousov 	ssize_t resid, r;
619952a6212SJordan K. Hubbard 	u_long osize;
62027a0bc89SDoug Rabson 	int error = 0;
62127a0bc89SDoug Rabson 	u_long count;
6226b6c5f5eSBruce Evans 	int seqcount;
62327a0bc89SDoug Rabson 	daddr_t bn, lastcn;
62427a0bc89SDoug Rabson 	struct buf *bp;
62527a0bc89SDoug Rabson 	int ioflag = ap->a_ioflag;
62627a0bc89SDoug Rabson 	struct uio *uio = ap->a_uio;
62727a0bc89SDoug Rabson 	struct vnode *vp = ap->a_vp;
62827a0bc89SDoug Rabson 	struct vnode *thisvp;
62927a0bc89SDoug Rabson 	struct denode *dep = VTODE(vp);
63027a0bc89SDoug Rabson 	struct msdosfsmount *pmp = dep->de_pmp;
63127a0bc89SDoug Rabson 	struct ucred *cred = ap->a_cred;
63227a0bc89SDoug Rabson 
63327a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
634952a6212SJordan K. Hubbard 	printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
63527a0bc89SDoug Rabson 	    vp, uio, ioflag, cred);
636952a6212SJordan K. Hubbard 	printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
63727a0bc89SDoug Rabson 	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
63827a0bc89SDoug Rabson #endif
63927a0bc89SDoug Rabson 
64027a0bc89SDoug Rabson 	switch (vp->v_type) {
64127a0bc89SDoug Rabson 	case VREG:
64227a0bc89SDoug Rabson 		if (ioflag & IO_APPEND)
64327a0bc89SDoug Rabson 			uio->uio_offset = dep->de_FileSize;
64427a0bc89SDoug Rabson 		thisvp = vp;
64527a0bc89SDoug Rabson 		break;
64627a0bc89SDoug Rabson 	case VDIR:
647952a6212SJordan K. Hubbard 		return EISDIR;
64827a0bc89SDoug Rabson 	default:
64927a0bc89SDoug Rabson 		panic("msdosfs_write(): bad file type");
65027a0bc89SDoug Rabson 	}
65127a0bc89SDoug Rabson 
652a4e6807cSBruce Evans 	/*
653a4e6807cSBruce Evans 	 * This is needed (unlike in ffs_write()) because we extend the
654a4e6807cSBruce Evans 	 * file outside of the loop but we don't want to extend the file
655a4e6807cSBruce Evans 	 * for writes of 0 bytes.
656a4e6807cSBruce Evans 	 */
65727a0bc89SDoug Rabson 	if (uio->uio_resid == 0)
658952a6212SJordan K. Hubbard 		return (0);
65927a0bc89SDoug Rabson 
66027a0bc89SDoug Rabson 	/*
661a4e6807cSBruce Evans 	 * The caller is supposed to ensure that
662a4e6807cSBruce Evans 	 * uio->uio_offset >= 0 and uio->uio_resid >= 0.
663a9c439baSKonstantin Belousov 	 *
66427a0bc89SDoug Rabson 	 * If they've exceeded their filesize limit, tell them about it.
66527a0bc89SDoug Rabson 	 */
666a9c439baSKonstantin Belousov 	error = vn_rlimit_fsizex(vp, uio, MSDOSFS_FILESIZE_MAX, &r,
667a9c439baSKonstantin Belousov 	    uio->uio_td);
668a9c439baSKonstantin Belousov 	if (error != 0) {
669a9c439baSKonstantin Belousov 		vn_rlimit_fsizex_res(uio, r);
670cc65a412SKonstantin Belousov 		return (error);
671a9c439baSKonstantin Belousov 	}
67227a0bc89SDoug Rabson 
67327a0bc89SDoug Rabson 	/*
67427a0bc89SDoug Rabson 	 * If the offset we are starting the write at is beyond the end of
67527a0bc89SDoug Rabson 	 * the file, then they've done a seek.  Unix filesystems allow
67627a0bc89SDoug Rabson 	 * files with holes in them, DOS doesn't so we must fill the hole
67727a0bc89SDoug Rabson 	 * with zeroed blocks.
67827a0bc89SDoug Rabson 	 */
67927a0bc89SDoug Rabson 	if (uio->uio_offset > dep->de_FileSize) {
68027a0bc89SDoug Rabson 		error = deextend(dep, uio->uio_offset, cred);
681a9c439baSKonstantin Belousov 		if (error != 0) {
682a9c439baSKonstantin Belousov 			vn_rlimit_fsizex_res(uio, r);
683952a6212SJordan K. Hubbard 			return (error);
68427a0bc89SDoug Rabson 		}
685a9c439baSKonstantin Belousov 	}
68627a0bc89SDoug Rabson 
68727a0bc89SDoug Rabson 	/*
68827a0bc89SDoug Rabson 	 * Remember some values in case the write fails.
68927a0bc89SDoug Rabson 	 */
69027a0bc89SDoug Rabson 	resid = uio->uio_resid;
69127a0bc89SDoug Rabson 	osize = dep->de_FileSize;
69227a0bc89SDoug Rabson 
69327a0bc89SDoug Rabson 	/*
69427a0bc89SDoug Rabson 	 * If we write beyond the end of the file, extend it to its ultimate
69527a0bc89SDoug Rabson 	 * size ahead of the time to hopefully get a contiguous area.
69627a0bc89SDoug Rabson 	 */
69727a0bc89SDoug Rabson 	if (uio->uio_offset + resid > osize) {
698952a6212SJordan K. Hubbard 		count = de_clcount(pmp, uio->uio_offset + resid) -
699952a6212SJordan K. Hubbard 			de_clcount(pmp, osize);
700952a6212SJordan K. Hubbard 		error = extendfile(dep, count, NULL, NULL, 0);
701952a6212SJordan K. Hubbard 		if (error &&  (error != ENOSPC || (ioflag & IO_UNIT)))
70227a0bc89SDoug Rabson 			goto errexit;
70327a0bc89SDoug Rabson 		lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
70427a0bc89SDoug Rabson 	} else
70527a0bc89SDoug Rabson 		lastcn = de_clcount(pmp, osize) - 1;
70627a0bc89SDoug Rabson 
7076b6c5f5eSBruce Evans 	seqcount = ioflag >> IO_SEQSHIFT;
70827a0bc89SDoug Rabson 	do {
709952a6212SJordan K. Hubbard 		if (de_cluster(pmp, uio->uio_offset) > lastcn) {
71027a0bc89SDoug Rabson 			error = ENOSPC;
71127a0bc89SDoug Rabson 			break;
71227a0bc89SDoug Rabson 		}
71327a0bc89SDoug Rabson 
71439b1a97cSJohn Dyson 		croffset = uio->uio_offset & pmp->pm_crbomask;
71539b1a97cSJohn Dyson 		n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
71639b1a97cSJohn Dyson 		if (uio->uio_offset + n > dep->de_FileSize) {
71739b1a97cSJohn Dyson 			dep->de_FileSize = uio->uio_offset + n;
71839b1a97cSJohn Dyson 			/* The object size needs to be set before buffer is allocated */
71939b1a97cSJohn Dyson 			vnode_pager_setsize(vp, dep->de_FileSize);
72039b1a97cSJohn Dyson 		}
72139b1a97cSJohn Dyson 
7223f75478cSMike Smith 		bn = de_cluster(pmp, uio->uio_offset);
72327a0bc89SDoug Rabson 		if ((uio->uio_offset & pmp->pm_crbomask) == 0
7243f75478cSMike Smith 		    && (de_cluster(pmp, uio->uio_offset + uio->uio_resid)
7253f75478cSMike Smith 			> de_cluster(pmp, uio->uio_offset)
72627a0bc89SDoug Rabson 			|| uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
72727a0bc89SDoug Rabson 			/*
72827a0bc89SDoug Rabson 			 * If either the whole cluster gets written,
72927a0bc89SDoug Rabson 			 * or we write the cluster from its start beyond EOF,
73027a0bc89SDoug Rabson 			 * then no need to read data from disk.
73127a0bc89SDoug Rabson 			 */
7327261f5f6SJeff Roberson 			bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0, 0);
7332aa39445SKonstantin Belousov 			/*
7342aa39445SKonstantin Belousov 			 * This call to vfs_bio_clrbuf() ensures that
7352aa39445SKonstantin Belousov 			 * even if vn_io_fault_uiomove() below faults,
7362aa39445SKonstantin Belousov 			 * garbage from the newly instantiated buffer
7372aa39445SKonstantin Belousov 			 * is not exposed to the userspace via mmap().
7382aa39445SKonstantin Belousov 			 */
739d34b0a1bSBruce Evans 			vfs_bio_clrbuf(bp);
74027a0bc89SDoug Rabson 			/*
74127a0bc89SDoug Rabson 			 * Do the bmap now, since pcbmap needs buffers
7429287dbaaSEd Maste 			 * for the FAT table. (see msdosfs_strategy)
74327a0bc89SDoug Rabson 			 */
74427a0bc89SDoug Rabson 			if (bp->b_blkno == bp->b_lblkno) {
7450d2af521SKirk McKusick 				error = pcbmap(dep, bp->b_lblkno, &bn, 0, 0);
746c3c6d51eSPoul-Henning Kamp 				if (error)
74727a0bc89SDoug Rabson 					bp->b_blkno = -1;
7480d2af521SKirk McKusick 				else
7490d2af521SKirk McKusick 					bp->b_blkno = bn;
75027a0bc89SDoug Rabson 			}
75127a0bc89SDoug Rabson 			if (bp->b_blkno == -1) {
75227a0bc89SDoug Rabson 				brelse(bp);
75327a0bc89SDoug Rabson 				if (!error)
75427a0bc89SDoug Rabson 					error = EIO;		/* XXX */
75527a0bc89SDoug Rabson 				break;
75627a0bc89SDoug Rabson 			}
75727a0bc89SDoug Rabson 		} else {
75827a0bc89SDoug Rabson 			/*
75927a0bc89SDoug Rabson 			 * The block we need to write into exists, so read it in.
76027a0bc89SDoug Rabson 			 */
761c3c6d51eSPoul-Henning Kamp 			error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp);
762952a6212SJordan K. Hubbard 			if (error) {
76327a0bc89SDoug Rabson 				break;
76427a0bc89SDoug Rabson 			}
765952a6212SJordan K. Hubbard 		}
76627a0bc89SDoug Rabson 
76727a0bc89SDoug Rabson 		/*
76827a0bc89SDoug Rabson 		 * Should these vnode_pager_* functions be done on dir
76927a0bc89SDoug Rabson 		 * files?
77027a0bc89SDoug Rabson 		 */
77127a0bc89SDoug Rabson 
77227a0bc89SDoug Rabson 		/*
77327a0bc89SDoug Rabson 		 * Copy the data from user space into the buf header.
77427a0bc89SDoug Rabson 		 */
7752aa39445SKonstantin Belousov 		error = vn_io_fault_uiomove(bp->b_data + croffset, n, uio);
776687fce03SBoris Popov 		if (error) {
777687fce03SBoris Popov 			brelse(bp);
778687fce03SBoris Popov 			break;
779687fce03SBoris Popov 		}
78027a0bc89SDoug Rabson 
7816b6c5f5eSBruce Evans 		/* Prepare for clustered writes in some else clauses. */
7826b6c5f5eSBruce Evans 		if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
7836b6c5f5eSBruce Evans 			bp->b_flags |= B_CLUSTEROK;
7846b6c5f5eSBruce Evans 
78527a0bc89SDoug Rabson 		/*
786d34b0a1bSBruce Evans 		 * If IO_SYNC, then each buffer is written synchronously.
7876b6c5f5eSBruce Evans 		 * Otherwise, if we have a severe page deficiency then
7886b6c5f5eSBruce Evans 		 * write the buffer asynchronously.  Otherwise, if on a
789d34b0a1bSBruce Evans 		 * cluster boundary then write the buffer asynchronously,
7906b6c5f5eSBruce Evans 		 * combining it with contiguous clusters if permitted and
7916b6c5f5eSBruce Evans 		 * possible, since we don't expect more writes into this
792d34b0a1bSBruce Evans 		 * buffer soon.  Otherwise, do a delayed write because we
793d34b0a1bSBruce Evans 		 * expect more writes into this buffer soon.
79427a0bc89SDoug Rabson 		 */
79527a0bc89SDoug Rabson 		if (ioflag & IO_SYNC)
79627a0bc89SDoug Rabson 			(void)bwrite(bp);
7976b6c5f5eSBruce Evans 		else if (vm_page_count_severe() || buf_dirty_count_severe())
79827a0bc89SDoug Rabson 			bawrite(bp);
7996b6c5f5eSBruce Evans 		else if (n + croffset == pmp->pm_bpcluster) {
8006b6c5f5eSBruce Evans 			if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
8012bfd8992SKonstantin Belousov 				cluster_write(vp, &dep->de_clusterw, bp,
8022bfd8992SKonstantin Belousov 				    dep->de_FileSize, seqcount, 0);
803952a6212SJordan K. Hubbard 			else
8046b6c5f5eSBruce Evans 				bawrite(bp);
8056b6c5f5eSBruce Evans 		} else
80627a0bc89SDoug Rabson 			bdwrite(bp);
80727a0bc89SDoug Rabson 		dep->de_flag |= DE_UPDATE;
80827a0bc89SDoug Rabson 	} while (error == 0 && uio->uio_resid > 0);
80927a0bc89SDoug Rabson 
81027a0bc89SDoug Rabson 	/*
81127a0bc89SDoug Rabson 	 * If the write failed and they want us to, truncate the file back
81227a0bc89SDoug Rabson 	 * to the size it was before the write was attempted.
81327a0bc89SDoug Rabson 	 */
81427a0bc89SDoug Rabson errexit:
81527a0bc89SDoug Rabson 	if (error) {
81627a0bc89SDoug Rabson 		if (ioflag & IO_UNIT) {
817c52fd858SEdward Tomasz Napierala 			detrunc(dep, osize, ioflag & IO_SYNC, NOCRED);
81827a0bc89SDoug Rabson 			uio->uio_offset -= resid - uio->uio_resid;
81927a0bc89SDoug Rabson 			uio->uio_resid = resid;
82027a0bc89SDoug Rabson 		} else {
821c52fd858SEdward Tomasz Napierala 			detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED);
82227a0bc89SDoug Rabson 			if (uio->uio_resid != resid)
82327a0bc89SDoug Rabson 				error = 0;
82427a0bc89SDoug Rabson 		}
825952a6212SJordan K. Hubbard 	} else if (ioflag & IO_SYNC)
826952a6212SJordan K. Hubbard 		error = deupdat(dep, 1);
827a9c439baSKonstantin Belousov 	vn_rlimit_fsizex_res(uio, r);
828952a6212SJordan K. Hubbard 	return (error);
82927a0bc89SDoug Rabson }
83027a0bc89SDoug Rabson 
83127a0bc89SDoug Rabson /*
83227a0bc89SDoug Rabson  * Flush the blocks of a file to disk.
83327a0bc89SDoug Rabson  */
834a98ca469SPoul-Henning Kamp static int
msdosfs_fsync(struct vop_fsync_args * ap)83510c9700fSEd Maste msdosfs_fsync(struct vop_fsync_args *ap)
83627a0bc89SDoug Rabson {
837293e4eb6SKonstantin Belousov 	struct vnode *devvp;
838293e4eb6SKonstantin Belousov 	int allerror, error;
839767b9a52SJeff Roberson 
840c0f5121cSBruce Evans 	vop_stdfsync(ap);
841293e4eb6SKonstantin Belousov 
842293e4eb6SKonstantin Belousov 	/*
843293e4eb6SKonstantin Belousov 	* If the syncing request comes from fsync(2), sync the entire
844293e4eb6SKonstantin Belousov 	* FAT and any other metadata that happens to be on devvp.  We
845293e4eb6SKonstantin Belousov 	* need this mainly for the FAT.  We write the FAT sloppily, and
846293e4eb6SKonstantin Belousov 	* syncing it all now is the best we can easily do to get all
847293e4eb6SKonstantin Belousov 	* directory entries associated with the file (not just the file)
848293e4eb6SKonstantin Belousov 	* fully synced.  The other metadata includes critical metadata
849293e4eb6SKonstantin Belousov 	* for all directory entries, but only in the MNT_ASYNC case.  We
850293e4eb6SKonstantin Belousov 	* will soon sync all metadata in the file's directory entry.
851293e4eb6SKonstantin Belousov 	* Non-critical metadata for associated directory entries only
852293e4eb6SKonstantin Belousov 	* gets synced accidentally, as in most file systems.
853293e4eb6SKonstantin Belousov 	*/
8541b3cb4dcSKonstantin Belousov 	if (ap->a_waitfor != MNT_NOWAIT) {
855293e4eb6SKonstantin Belousov 		devvp = VTODE(ap->a_vp)->de_pmp->pm_devvp;
856293e4eb6SKonstantin Belousov 		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
857293e4eb6SKonstantin Belousov 		allerror = VOP_FSYNC(devvp, MNT_WAIT, ap->a_td);
858b249ce48SMateusz Guzik 		VOP_UNLOCK(devvp);
859293e4eb6SKonstantin Belousov 	} else
860293e4eb6SKonstantin Belousov 		allerror = 0;
861293e4eb6SKonstantin Belousov 
8621b3cb4dcSKonstantin Belousov 	error = deupdat(VTODE(ap->a_vp), ap->a_waitfor != MNT_NOWAIT);
863293e4eb6SKonstantin Belousov 	if (allerror == 0)
864293e4eb6SKonstantin Belousov 		allerror = error;
865293e4eb6SKonstantin Belousov 	return (allerror);
86627a0bc89SDoug Rabson }
86727a0bc89SDoug Rabson 
868a98ca469SPoul-Henning Kamp static int
msdosfs_remove(struct vop_remove_args * ap)86910c9700fSEd Maste msdosfs_remove(struct vop_remove_args *ap)
87027a0bc89SDoug Rabson {
87127a0bc89SDoug Rabson 	struct denode *dep = VTODE(ap->a_vp);
87227a0bc89SDoug Rabson 	struct denode *ddep = VTODE(ap->a_dvp);
8737be2d300SMike Smith 	int error;
87427a0bc89SDoug Rabson 
875952a6212SJordan K. Hubbard 	if (ap->a_vp->v_type == VDIR)
876952a6212SJordan K. Hubbard 		error = EPERM;
877952a6212SJordan K. Hubbard 	else
87827a0bc89SDoug Rabson 		error = removede(ddep, dep);
87927a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
880952a6212SJordan K. Hubbard 	printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep, ap->a_vp->v_usecount);
88127a0bc89SDoug Rabson #endif
882952a6212SJordan K. Hubbard 	return (error);
88327a0bc89SDoug Rabson }
88427a0bc89SDoug Rabson 
88527a0bc89SDoug Rabson /*
886b1b494a7SBoris Popov  * DOS filesystems don't know what links are.
88727a0bc89SDoug Rabson  */
888a98ca469SPoul-Henning Kamp static int
msdosfs_link(struct vop_link_args * ap)88910c9700fSEd Maste msdosfs_link(struct vop_link_args *ap)
89027a0bc89SDoug Rabson {
8917be2d300SMike Smith 	return (EOPNOTSUPP);
89227a0bc89SDoug Rabson }
89327a0bc89SDoug Rabson 
89427a0bc89SDoug Rabson /*
89527a0bc89SDoug Rabson  * Renames on files require moving the denode to a new hash queue since the
89627a0bc89SDoug Rabson  * denode's location is used to compute which hash queue to put the file
89727a0bc89SDoug Rabson  * in. Unless it is a rename in place.  For example "mv a b".
89827a0bc89SDoug Rabson  *
89927a0bc89SDoug Rabson  * What follows is the basic algorithm:
90027a0bc89SDoug Rabson  *
90127a0bc89SDoug Rabson  * if (file move) {
90227a0bc89SDoug Rabson  *	if (dest file exists) {
90327a0bc89SDoug Rabson  *		remove dest file
90427a0bc89SDoug Rabson  *	}
90527a0bc89SDoug Rabson  *	if (dest and src in same directory) {
90627a0bc89SDoug Rabson  *		rewrite name in existing directory slot
90727a0bc89SDoug Rabson  *	} else {
90827a0bc89SDoug Rabson  *		write new entry in dest directory
90927a0bc89SDoug Rabson  *		update offset and dirclust in denode
91027a0bc89SDoug Rabson  *		move denode to new hash chain
91127a0bc89SDoug Rabson  *		clear old directory entry
91227a0bc89SDoug Rabson  *	}
91327a0bc89SDoug Rabson  * } else {
91427a0bc89SDoug Rabson  *	directory move
91527a0bc89SDoug Rabson  *	if (dest directory exists) {
91627a0bc89SDoug Rabson  *		if (dest is not empty) {
91727a0bc89SDoug Rabson  *			return ENOTEMPTY
91827a0bc89SDoug Rabson  *		}
91927a0bc89SDoug Rabson  *		remove dest directory
92027a0bc89SDoug Rabson  *	}
92127a0bc89SDoug Rabson  *	if (dest and src in same directory) {
92227a0bc89SDoug Rabson  *		rewrite name in existing entry
92327a0bc89SDoug Rabson  *	} else {
92427a0bc89SDoug Rabson  *		be sure dest is not a child of src directory
92527a0bc89SDoug Rabson  *		write entry in dest directory
92627a0bc89SDoug Rabson  *		update "." and ".." in moved directory
92727a0bc89SDoug Rabson  *		clear old directory entry for moved directory
92827a0bc89SDoug Rabson  *	}
92927a0bc89SDoug Rabson  * }
93027a0bc89SDoug Rabson  *
93127a0bc89SDoug Rabson  * On entry:
93227a0bc89SDoug Rabson  *	source's parent directory is unlocked
93327a0bc89SDoug Rabson  *	source file or directory is unlocked
93427a0bc89SDoug Rabson  *	destination's parent directory is locked
93527a0bc89SDoug Rabson  *	destination file or directory is locked if it exists
93627a0bc89SDoug Rabson  *
93727a0bc89SDoug Rabson  * On exit:
93827a0bc89SDoug Rabson  *	all denodes should be released
93927a0bc89SDoug Rabson  */
940a98ca469SPoul-Henning Kamp static int
msdosfs_rename(struct vop_rename_args * ap)94110c9700fSEd Maste msdosfs_rename(struct vop_rename_args *ap)
94227a0bc89SDoug Rabson {
94395d42526SKonstantin Belousov 	struct vnode *fdvp, *fvp, *tdvp, *tvp, *vp;
94495d42526SKonstantin Belousov 	struct componentname *fcnp, *tcnp;
94595d42526SKonstantin Belousov 	struct denode *fdip, *fip, *tdip, *tip, *nip;
946813d71deSKonstantin Belousov 	u_char toname[12], oldname[11];
9472bd6d910SKonstantin Belousov 	u_long to_diroffset;
9486ae13c0fSKonstantin Belousov 	bool checkpath_locked, doingdirectory, newparent;
949952a6212SJordan K. Hubbard 	int error;
95095d42526SKonstantin Belousov 	u_long cn, pcl, blkoff;
95195d42526SKonstantin Belousov 	daddr_t bn, wait_scn, scn;
95227a0bc89SDoug Rabson 	struct msdosfsmount *pmp;
95327a0bc89SDoug Rabson 	struct direntry *dotdotp;
95427a0bc89SDoug Rabson 	struct buf *bp;
95527a0bc89SDoug Rabson 
95695d42526SKonstantin Belousov 	tdvp = ap->a_tdvp;
95795d42526SKonstantin Belousov 	fvp = ap->a_fvp;
95895d42526SKonstantin Belousov 	fdvp = ap->a_fdvp;
95995d42526SKonstantin Belousov 	tvp = ap->a_tvp;
96095d42526SKonstantin Belousov 	tcnp = ap->a_tcnp;
96195d42526SKonstantin Belousov 	fcnp = ap->a_fcnp;
962952a6212SJordan K. Hubbard 	pmp = VFSTOMSDOSFS(fdvp->v_mount);
963952a6212SJordan K. Hubbard 
964952a6212SJordan K. Hubbard 	/*
965952a6212SJordan K. Hubbard 	 * Check for cross-device rename.
966952a6212SJordan K. Hubbard 	 */
967c0f5121cSBruce Evans 	if (fvp->v_mount != tdvp->v_mount ||
96895d42526SKonstantin Belousov 	    (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
96927a0bc89SDoug Rabson 		error = EXDEV;
97095d42526SKonstantin Belousov 		goto abortit;
97127a0bc89SDoug Rabson 	}
97227a0bc89SDoug Rabson 
97327a0bc89SDoug Rabson 	/*
974952a6212SJordan K. Hubbard 	 * If source and dest are the same, do nothing.
97527a0bc89SDoug Rabson 	 */
976952a6212SJordan K. Hubbard 	if (tvp == fvp) {
977952a6212SJordan K. Hubbard 		error = 0;
978952a6212SJordan K. Hubbard 		goto abortit;
979952a6212SJordan K. Hubbard 	}
98027a0bc89SDoug Rabson 
98195d42526SKonstantin Belousov 	/*
98295d42526SKonstantin Belousov 	 * When the target exists, both the directory
98395d42526SKonstantin Belousov 	 * and target vnodes are passed locked.
98495d42526SKonstantin Belousov 	 */
98595d42526SKonstantin Belousov 	VOP_UNLOCK(tdvp);
98695d42526SKonstantin Belousov 	if (tvp != NULL && tvp != tdvp)
98795d42526SKonstantin Belousov 		VOP_UNLOCK(tvp);
98895d42526SKonstantin Belousov 
9896ae13c0fSKonstantin Belousov 	checkpath_locked = false;
9906ae13c0fSKonstantin Belousov 
99195d42526SKonstantin Belousov relock:
99295d42526SKonstantin Belousov 	doingdirectory = newparent = false;
99395d42526SKonstantin Belousov 
99495d42526SKonstantin Belousov 	error = vn_lock(fdvp, LK_EXCLUSIVE);
99595d42526SKonstantin Belousov 	if (error != 0)
99695d42526SKonstantin Belousov 		goto releout;
99795d42526SKonstantin Belousov 	if (vn_lock(tdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
99895d42526SKonstantin Belousov 		VOP_UNLOCK(fdvp);
99995d42526SKonstantin Belousov 		error = vn_lock(tdvp, LK_EXCLUSIVE);
100095d42526SKonstantin Belousov 		if (error != 0)
100195d42526SKonstantin Belousov 			goto releout;
100295d42526SKonstantin Belousov 		VOP_UNLOCK(tdvp);
100395d42526SKonstantin Belousov 		goto relock;
100495d42526SKonstantin Belousov 	}
100595d42526SKonstantin Belousov 
100695d42526SKonstantin Belousov 	error = msdosfs_lookup_ino(fdvp, NULL, fcnp, &scn, &blkoff);
100795d42526SKonstantin Belousov 	if (error != 0) {
100895d42526SKonstantin Belousov 		VOP_UNLOCK(fdvp);
100995d42526SKonstantin Belousov 		VOP_UNLOCK(tdvp);
101095d42526SKonstantin Belousov 		goto releout;
101195d42526SKonstantin Belousov 	}
101295d42526SKonstantin Belousov 	error = deget(pmp, scn, blkoff, LK_EXCLUSIVE | LK_NOWAIT, &nip);
101395d42526SKonstantin Belousov 	if (error != 0) {
101495d42526SKonstantin Belousov 		VOP_UNLOCK(fdvp);
101595d42526SKonstantin Belousov 		VOP_UNLOCK(tdvp);
101695d42526SKonstantin Belousov 		if (error != EBUSY)
101795d42526SKonstantin Belousov 			goto releout;
101895d42526SKonstantin Belousov 		error = deget(pmp, scn, blkoff, LK_EXCLUSIVE, &nip);
101995d42526SKonstantin Belousov 		if (error != 0)
102095d42526SKonstantin Belousov 			goto releout;
102195d42526SKonstantin Belousov 		vp = fvp;
102295d42526SKonstantin Belousov 		fvp = DETOV(nip);
102395d42526SKonstantin Belousov 		VOP_UNLOCK(fvp);
102495d42526SKonstantin Belousov 		vrele(vp);
102595d42526SKonstantin Belousov 		goto relock;
102695d42526SKonstantin Belousov 	}
102795d42526SKonstantin Belousov 	vrele(fvp);
102895d42526SKonstantin Belousov 	fvp = DETOV(nip);
102995d42526SKonstantin Belousov 
103095d42526SKonstantin Belousov 	error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
103195d42526SKonstantin Belousov 	if (error != 0 && error != EJUSTRETURN) {
103295d42526SKonstantin Belousov 		VOP_UNLOCK(fdvp);
103395d42526SKonstantin Belousov 		VOP_UNLOCK(tdvp);
103495d42526SKonstantin Belousov 		VOP_UNLOCK(fvp);
103595d42526SKonstantin Belousov 		goto releout;
103695d42526SKonstantin Belousov 	}
103795d42526SKonstantin Belousov 	if (error == EJUSTRETURN && tvp != NULL) {
103895d42526SKonstantin Belousov 		vrele(tvp);
103995d42526SKonstantin Belousov 		tvp = NULL;
104095d42526SKonstantin Belousov 	}
104195d42526SKonstantin Belousov 	if (error == 0) {
104295d42526SKonstantin Belousov 		nip = NULL;
104395d42526SKonstantin Belousov 		error = deget(pmp, scn, blkoff, LK_EXCLUSIVE | LK_NOWAIT,
104495d42526SKonstantin Belousov 		    &nip);
104595d42526SKonstantin Belousov 		if (tvp != NULL) {
104695d42526SKonstantin Belousov 			vrele(tvp);
104795d42526SKonstantin Belousov 			tvp = NULL;
104895d42526SKonstantin Belousov 		}
104995d42526SKonstantin Belousov 		if (error != 0) {
105095d42526SKonstantin Belousov 			VOP_UNLOCK(fdvp);
105195d42526SKonstantin Belousov 			VOP_UNLOCK(tdvp);
105295d42526SKonstantin Belousov 			VOP_UNLOCK(fvp);
105395d42526SKonstantin Belousov 			if (error != EBUSY)
105495d42526SKonstantin Belousov 				goto releout;
105595d42526SKonstantin Belousov 			error = deget(pmp, scn, blkoff, LK_EXCLUSIVE,
105695d42526SKonstantin Belousov 			    &nip);
105795d42526SKonstantin Belousov 			if (error != 0)
105895d42526SKonstantin Belousov 				goto releout;
105995d42526SKonstantin Belousov 			vput(DETOV(nip));
106095d42526SKonstantin Belousov 			goto relock;
106195d42526SKonstantin Belousov 		}
106295d42526SKonstantin Belousov 		tvp = DETOV(nip);
106395d42526SKonstantin Belousov 	}
106495d42526SKonstantin Belousov 
106595d42526SKonstantin Belousov 	fdip = VTODE(fdvp);
106695d42526SKonstantin Belousov 	fip = VTODE(fvp);
106795d42526SKonstantin Belousov 	tdip = VTODE(tdvp);
106895d42526SKonstantin Belousov 	tip = tvp != NULL ? VTODE(tvp) : NULL;
106995d42526SKonstantin Belousov 
107095d42526SKonstantin Belousov 	/*
107195d42526SKonstantin Belousov 	 * Remember direntry place to use for destination
107295d42526SKonstantin Belousov 	 */
107395d42526SKonstantin Belousov 	to_diroffset = tdip->de_fndoffset;
107427a0bc89SDoug Rabson 
107527a0bc89SDoug Rabson 	/*
107627a0bc89SDoug Rabson 	 * Be sure we are not renaming ".", "..", or an alias of ".". This
107727a0bc89SDoug Rabson 	 * leads to a crippled directory tree.  It's pretty tough to do a
107827a0bc89SDoug Rabson 	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
107927a0bc89SDoug Rabson 	 * doesn't work if the ".." entry is missing.
108027a0bc89SDoug Rabson 	 */
108195d42526SKonstantin Belousov 	if ((fip->de_Attributes & ATTR_DIRECTORY) != 0) {
1082952a6212SJordan K. Hubbard 		/*
1083952a6212SJordan K. Hubbard 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
1084952a6212SJordan K. Hubbard 		 */
1085952a6212SJordan K. Hubbard 		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
108695d42526SKonstantin Belousov 		    fdip == fip ||
108795d42526SKonstantin Belousov 		    (fcnp->cn_flags & ISDOTDOT) != 0 ||
108895d42526SKonstantin Belousov 		    (tcnp->cn_flags & ISDOTDOT) != 0) {
1089952a6212SJordan K. Hubbard 			error = EINVAL;
109095d42526SKonstantin Belousov 			goto unlock;
109127a0bc89SDoug Rabson 		}
109295d42526SKonstantin Belousov 		doingdirectory = true;
109327a0bc89SDoug Rabson 	}
109427a0bc89SDoug Rabson 
109527a0bc89SDoug Rabson 	/*
1096952a6212SJordan K. Hubbard 	 * If ".." must be changed (ie the directory gets a new
1097952a6212SJordan K. Hubbard 	 * parent) then the source directory must not be in the
10988fa03d08SUlrich Spörlein 	 * directory hierarchy above the target, as this would
1099952a6212SJordan K. Hubbard 	 * orphan everything below the source directory. Also
1100952a6212SJordan K. Hubbard 	 * the user must have write permission in the source so
1101952a6212SJordan K. Hubbard 	 * as to be able to change "..". We must repeat the call
1102952a6212SJordan K. Hubbard 	 * to namei, as the parent directory is unlocked by the
1103952a6212SJordan K. Hubbard 	 * call to doscheckpath().
1104952a6212SJordan K. Hubbard 	 */
1105b4a58fbfSMateusz Guzik 	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, curthread);
110695d42526SKonstantin Belousov 	if (fdip->de_StartCluster != tdip->de_StartCluster)
110795d42526SKonstantin Belousov 		newparent = true;
1108952a6212SJordan K. Hubbard 	if (doingdirectory && newparent) {
110995d42526SKonstantin Belousov 		if (error != 0)	/* write access check above */
111095d42526SKonstantin Belousov 			goto unlock;
11116ae13c0fSKonstantin Belousov 		lockmgr(&pmp->pm_checkpath_lock, LK_EXCLUSIVE, NULL);
11126ae13c0fSKonstantin Belousov 		checkpath_locked = true;
111395d42526SKonstantin Belousov 		error = doscheckpath(fip, tdip, &wait_scn);
111495d42526SKonstantin Belousov 		if (wait_scn != 0) {
11156ae13c0fSKonstantin Belousov 			lockmgr(&pmp->pm_checkpath_lock, LK_RELEASE, NULL);
11166ae13c0fSKonstantin Belousov 			checkpath_locked = false;
111795d42526SKonstantin Belousov 			VOP_UNLOCK(fdvp);
111895d42526SKonstantin Belousov 			VOP_UNLOCK(tdvp);
111995d42526SKonstantin Belousov 			VOP_UNLOCK(fvp);
112095d42526SKonstantin Belousov 			if (tvp != NULL && tvp != tdvp)
112195d42526SKonstantin Belousov 				VOP_UNLOCK(tvp);
112295d42526SKonstantin Belousov 			error = deget(pmp, wait_scn, 0, LK_EXCLUSIVE,
112395d42526SKonstantin Belousov 			    &nip);
112495d42526SKonstantin Belousov 			if (error == 0) {
112595d42526SKonstantin Belousov 				vput(DETOV(nip));
112695d42526SKonstantin Belousov 				goto relock;
112795d42526SKonstantin Belousov 			}
112895d42526SKonstantin Belousov 		}
112995d42526SKonstantin Belousov 		if (error != 0)
113095d42526SKonstantin Belousov 			goto unlock;
113127a0bc89SDoug Rabson 	}
113227a0bc89SDoug Rabson 
113395d42526SKonstantin Belousov 	if (tip != NULL) {
113427a0bc89SDoug Rabson 		/*
1135952a6212SJordan K. Hubbard 		 * Target must be empty if a directory and have no links
1136952a6212SJordan K. Hubbard 		 * to it. Also, ensure source and target are compatible
1137952a6212SJordan K. Hubbard 		 * (both directories, or both not directories).
113827a0bc89SDoug Rabson 		 */
113995d42526SKonstantin Belousov 		if ((tip->de_Attributes & ATTR_DIRECTORY) != 0) {
114095d42526SKonstantin Belousov 			if (!dosdirempty(tip)) {
114127a0bc89SDoug Rabson 				error = ENOTEMPTY;
114295d42526SKonstantin Belousov 				goto unlock;
114327a0bc89SDoug Rabson 			}
1144952a6212SJordan K. Hubbard 			if (!doingdirectory) {
1145952a6212SJordan K. Hubbard 				error = ENOTDIR;
114695d42526SKonstantin Belousov 				goto unlock;
1147952a6212SJordan K. Hubbard 			}
1148952a6212SJordan K. Hubbard 			cache_purge(tdvp);
1149952a6212SJordan K. Hubbard 		} else if (doingdirectory) {
115027a0bc89SDoug Rabson 			error = EISDIR;
115195d42526SKonstantin Belousov 			goto unlock;
115227a0bc89SDoug Rabson 		}
115395d42526SKonstantin Belousov 		error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
115495d42526SKonstantin Belousov 		MPASS(error == 0);
115595d42526SKonstantin Belousov 		error = removede(tdip, tip);
115695d42526SKonstantin Belousov 		if (error != 0)
115795d42526SKonstantin Belousov 			goto unlock;
1158952a6212SJordan K. Hubbard 		vput(tvp);
115995d42526SKonstantin Belousov 		tvp = NULL;
116095d42526SKonstantin Belousov 		tip = NULL;
116127a0bc89SDoug Rabson 	}
116227a0bc89SDoug Rabson 
116327a0bc89SDoug Rabson 	/*
1164952a6212SJordan K. Hubbard 	 * Convert the filename in tcnp into a dos filename. We copy this
1165952a6212SJordan K. Hubbard 	 * into the denode and directory entry for the destination
1166952a6212SJordan K. Hubbard 	 * file/directory.
116727a0bc89SDoug Rabson 	 */
116895d42526SKonstantin Belousov 	error = uniqdosname(tdip, tcnp, toname);
116995d42526SKonstantin Belousov 	if (error != 0)
117095d42526SKonstantin Belousov 		goto unlock;
117127a0bc89SDoug Rabson 
117227a0bc89SDoug Rabson 	/*
1173952a6212SJordan K. Hubbard 	 * First write a new entry in the destination
1174952a6212SJordan K. Hubbard 	 * directory and mark the entry in the source directory
1175952a6212SJordan K. Hubbard 	 * as deleted.  Then move the denode to the correct hash
117627a0bc89SDoug Rabson 	 * chain for its new location in the filesystem.  And, if
117727a0bc89SDoug Rabson 	 * we moved a directory, then update its .. entry to point
1178952a6212SJordan K. Hubbard 	 * to the new parent directory.
117927a0bc89SDoug Rabson 	 */
118095d42526SKonstantin Belousov 	memcpy(oldname, fip->de_Name, 11);
118195d42526SKonstantin Belousov 	memcpy(fip->de_Name, toname, 11);	/* update denode */
118295d42526SKonstantin Belousov 	error = msdosfs_lookup_ino(tdvp, NULL, tcnp, &scn, &blkoff);
11838b67c670SStefan Eßer 	if (error == EJUSTRETURN) {
11848b67c670SStefan Eßer 		tdip->de_fndoffset = to_diroffset;
118595d42526SKonstantin Belousov 		error = createde(fip, tdip, NULL, tcnp);
11868b67c670SStefan Eßer 	}
118795d42526SKonstantin Belousov 	if (error != 0) {
118895d42526SKonstantin Belousov 		memcpy(fip->de_Name, oldname, 11);
118995d42526SKonstantin Belousov 		goto unlock;
119027a0bc89SDoug Rabson 	}
119195d42526SKonstantin Belousov 
119208cf5cebSKonstantin Belousov 	/*
119395d42526SKonstantin Belousov 	 * If fip is for a directory, then its name should always
119408cf5cebSKonstantin Belousov 	 * be "." since it is for the directory entry in the
119508cf5cebSKonstantin Belousov 	 * directory itself (msdosfs_lookup() always translates
119608cf5cebSKonstantin Belousov 	 * to the "." entry so as to get a unique denode, except
119708cf5cebSKonstantin Belousov 	 * for the root directory there are different
119808cf5cebSKonstantin Belousov 	 * complications).  However, we just corrupted its name
119908cf5cebSKonstantin Belousov 	 * to pass the correct name to createde().  Undo this.
120008cf5cebSKonstantin Belousov 	 */
120195d42526SKonstantin Belousov 	if ((fip->de_Attributes & ATTR_DIRECTORY) != 0)
120295d42526SKonstantin Belousov 		memcpy(fip->de_Name, oldname, 11);
120395d42526SKonstantin Belousov 	fip->de_refcnt++;
120495d42526SKonstantin Belousov 	error = msdosfs_lookup_ino(fdvp, NULL, fcnp, &scn, &blkoff);
120595d42526SKonstantin Belousov 	MPASS(error == 0);
120695d42526SKonstantin Belousov 	error = removede(fdip, fip);
120795d42526SKonstantin Belousov 	if (error != 0) {
1208661db9b3SKonstantin Belousov 		printf("%s: removede %s %s err %d\n",
1209661db9b3SKonstantin Belousov 		    pmp->pm_mountp->mnt_stat.f_mntonname,
1210661db9b3SKonstantin Belousov 		    fdip->de_Name, fip->de_Name, error);
1211661db9b3SKonstantin Belousov 		msdosfs_integrity_error(pmp);
121295d42526SKonstantin Belousov 		goto unlock;
121327a0bc89SDoug Rabson 	}
1214952a6212SJordan K. Hubbard 	if (!doingdirectory) {
121595d42526SKonstantin Belousov 		error = pcbmap(tdip, de_cluster(pmp, to_diroffset), 0,
121695d42526SKonstantin Belousov 		    &fip->de_dirclust, 0);
121795d42526SKonstantin Belousov 		if (error != 0) {
121895d42526SKonstantin Belousov 			/*
121995d42526SKonstantin Belousov 			 * XXX should downgrade to ro here,
122095d42526SKonstantin Belousov 			 * fs is corrupt
122195d42526SKonstantin Belousov 			 */
122295d42526SKonstantin Belousov 			goto unlock;
122327a0bc89SDoug Rabson 		}
122495d42526SKonstantin Belousov 		if (fip->de_dirclust == MSDOSFSROOT)
122595d42526SKonstantin Belousov 			fip->de_diroffset = to_diroffset;
1226e27b047cSDmitrij Tejblum 		else
122795d42526SKonstantin Belousov 			fip->de_diroffset = to_diroffset & pmp->pm_crbomask;
1228d3628763SRodney W. Grimes 	}
122995d42526SKonstantin Belousov 	reinsert(fip);
123027a0bc89SDoug Rabson 
123127a0bc89SDoug Rabson 	/*
123227a0bc89SDoug Rabson 	 * If we moved a directory to a new parent directory, then we must
123327a0bc89SDoug Rabson 	 * fixup the ".." entry in the moved directory.
123427a0bc89SDoug Rabson 	 */
1235952a6212SJordan K. Hubbard 	if (doingdirectory && newparent) {
123695d42526SKonstantin Belousov 		cn = fip->de_StartCluster;
123727a0bc89SDoug Rabson 		if (cn == MSDOSFSROOT) {
123827a0bc89SDoug Rabson 			/* this should never happen */
1239edf8a815SDavid Greenman 			panic("msdosfs_rename(): updating .. in root directory?");
1240952a6212SJordan K. Hubbard 		} else
124127a0bc89SDoug Rabson 			bn = cntobn(pmp, cn);
124227a0bc89SDoug Rabson 		error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
124327a0bc89SDoug Rabson 		    NOCRED, &bp);
124495d42526SKonstantin Belousov 		if (error != 0) {
1245661db9b3SKonstantin Belousov 			printf("%s: block read error %d while renaming dir\n",
1246661db9b3SKonstantin Belousov 			    pmp->pm_mountp->mnt_stat.f_mntonname,
1247661db9b3SKonstantin Belousov 			    error);
1248661db9b3SKonstantin Belousov 			msdosfs_integrity_error(pmp);
124995d42526SKonstantin Belousov 			goto unlock;
125027a0bc89SDoug Rabson 		}
125127a0bc89SDoug Rabson 		dotdotp = (struct direntry *)bp->b_data + 1;
125295d42526SKonstantin Belousov 		pcl = tdip->de_StartCluster;
12539ec062ddSKonstantin Belousov 		if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
12549ec062ddSKonstantin Belousov 			pcl = MSDOSFSROOT;
12559ec062ddSKonstantin Belousov 		putushort(dotdotp->deStartCluster, pcl);
1256952a6212SJordan K. Hubbard 		if (FAT32(pmp))
12579ec062ddSKonstantin Belousov 			putushort(dotdotp->deHighClust, pcl >> 16);
12582aacee77SKonstantin Belousov 		if (DOINGASYNC(fvp))
1259cb65c1eeSBruce Evans 			bdwrite(bp);
1260cb65c1eeSBruce Evans 		else if ((error = bwrite(bp)) != 0) {
1261661db9b3SKonstantin Belousov 			printf("%s: block write error %d while renaming dir\n",
1262661db9b3SKonstantin Belousov 			    pmp->pm_mountp->mnt_stat.f_mntonname,
1263661db9b3SKonstantin Belousov 			    error);
1264661db9b3SKonstantin Belousov 			msdosfs_integrity_error(pmp);
126595d42526SKonstantin Belousov 			goto unlock;
126627a0bc89SDoug Rabson 		}
1267952a6212SJordan K. Hubbard 	}
1268952a6212SJordan K. Hubbard 
1269b0d53911SKonstantin Belousov 	/*
1270b0d53911SKonstantin Belousov 	 * The msdosfs lookup is case insensitive. Several aliases may
1271b0d53911SKonstantin Belousov 	 * be inserted for a single directory entry. As a consequnce,
1272b0d53911SKonstantin Belousov 	 * name cache purge done by lookup for fvp when DELETE op for
1273b0d53911SKonstantin Belousov 	 * namei is specified, might be not enough to expunge all
1274b0d53911SKonstantin Belousov 	 * namecache entries that were installed for this direntry.
1275b0d53911SKonstantin Belousov 	 */
12764d477d5cSKonstantin Belousov 	cache_purge(fvp);
127795d42526SKonstantin Belousov 
127895d42526SKonstantin Belousov unlock:
12796ae13c0fSKonstantin Belousov 	if (checkpath_locked)
12806ae13c0fSKonstantin Belousov 		lockmgr(&pmp->pm_checkpath_lock, LK_RELEASE, NULL);
128195d42526SKonstantin Belousov 	vput(fdvp);
128295d42526SKonstantin Belousov 	vput(fvp);
128395d42526SKonstantin Belousov 	if (tvp != NULL) {
128495d42526SKonstantin Belousov 		if (tvp != tdvp)
1285952a6212SJordan K. Hubbard 			vput(tvp);
128695d42526SKonstantin Belousov 		else
128795d42526SKonstantin Belousov 			vrele(tvp);
128895d42526SKonstantin Belousov 	}
1289952a6212SJordan K. Hubbard 	vput(tdvp);
129095d42526SKonstantin Belousov 	return (error);
129195d42526SKonstantin Belousov releout:
12926ae13c0fSKonstantin Belousov 	MPASS(!checkpath_locked);
129395d42526SKonstantin Belousov 	vrele(tdvp);
129495d42526SKonstantin Belousov 	if (tvp != NULL)
129595d42526SKonstantin Belousov 		vrele(tvp);
1296952a6212SJordan K. Hubbard 	vrele(fdvp);
1297952a6212SJordan K. Hubbard 	vrele(fvp);
1298952a6212SJordan K. Hubbard 	return (error);
129995d42526SKonstantin Belousov abortit:
130095d42526SKonstantin Belousov 	if (tdvp == tvp)
130195d42526SKonstantin Belousov 		vrele(tdvp);
130295d42526SKonstantin Belousov 	else
130395d42526SKonstantin Belousov 		vput(tdvp);
130495d42526SKonstantin Belousov 	if (tvp != NULL)
130595d42526SKonstantin Belousov 		vput(tvp);
130695d42526SKonstantin Belousov 	vrele(fdvp);
130795d42526SKonstantin Belousov 	vrele(fvp);
130895d42526SKonstantin Belousov 	return (error);
130927a0bc89SDoug Rabson }
131027a0bc89SDoug Rabson 
1311a98ca469SPoul-Henning Kamp static struct {
131227a0bc89SDoug Rabson 	struct direntry dot;
131327a0bc89SDoug Rabson 	struct direntry dotdot;
131427a0bc89SDoug Rabson } dosdirtemplate = {
131548d1bcf8SKonstantin Belousov 	{	".          ",				/* the . entry */
131627a0bc89SDoug Rabson 		ATTR_DIRECTORY,				/* file attribute */
1317952a6212SJordan K. Hubbard 		0,					/* reserved */
1318952a6212SJordan K. Hubbard 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1319952a6212SJordan K. Hubbard 		{ 0, 0 },				/* access date */
1320952a6212SJordan K. Hubbard 		{ 0, 0 },				/* high bits of start cluster */
1321952a6212SJordan K. Hubbard 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1322c3c6d51eSPoul-Henning Kamp 		{ 0, 0 },				/* startcluster */
1323952a6212SJordan K. Hubbard 		{ 0, 0, 0, 0 }				/* filesize */
1324952a6212SJordan K. Hubbard 	},
132548d1bcf8SKonstantin Belousov 	{	"..         ",				/* the .. entry */
132627a0bc89SDoug Rabson 		ATTR_DIRECTORY,				/* file attribute */
1327952a6212SJordan K. Hubbard 		0,					/* reserved */
1328952a6212SJordan K. Hubbard 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
1329952a6212SJordan K. Hubbard 		{ 0, 0 },				/* access date */
1330952a6212SJordan K. Hubbard 		{ 0, 0 },				/* high bits of start cluster */
1331952a6212SJordan K. Hubbard 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
1332c3c6d51eSPoul-Henning Kamp 		{ 0, 0 },				/* startcluster */
1333952a6212SJordan K. Hubbard 		{ 0, 0, 0, 0 }				/* filesize */
1334c3c6d51eSPoul-Henning Kamp 	}
133527a0bc89SDoug Rabson };
133627a0bc89SDoug Rabson 
1337a98ca469SPoul-Henning Kamp static int
msdosfs_mkdir(struct vop_mkdir_args * ap)133810c9700fSEd Maste msdosfs_mkdir(struct vop_mkdir_args *ap)
133927a0bc89SDoug Rabson {
1340952a6212SJordan K. Hubbard 	struct componentname *cnp = ap->a_cnp;
1341952a6212SJordan K. Hubbard 	struct denode *dep;
1342952a6212SJordan K. Hubbard 	struct denode *pdep = VTODE(ap->a_dvp);
1343952a6212SJordan K. Hubbard 	struct direntry *denp;
1344952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp = pdep->de_pmp;
134527a0bc89SDoug Rabson 	struct buf *bp;
13467be2d300SMike Smith 	u_long newcluster, pcl;
13477be2d300SMike Smith 	int bn;
13487be2d300SMike Smith 	int error;
13497be2d300SMike Smith 	struct denode ndirent;
135027a0bc89SDoug Rabson 	struct timespec ts;
135127a0bc89SDoug Rabson 
135227a0bc89SDoug Rabson 	/*
135327a0bc89SDoug Rabson 	 * If this is the root directory and there is no space left we
135427a0bc89SDoug Rabson 	 * can't do anything.  This is because the root directory can not
135527a0bc89SDoug Rabson 	 * change size.
135627a0bc89SDoug Rabson 	 */
1357952a6212SJordan K. Hubbard 	if (pdep->de_StartCluster == MSDOSFSROOT
1358952a6212SJordan K. Hubbard 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
1359952a6212SJordan K. Hubbard 		error = ENOSPC;
1360952a6212SJordan K. Hubbard 		goto bad2;
136127a0bc89SDoug Rabson 	}
136227a0bc89SDoug Rabson 
136327a0bc89SDoug Rabson 	/*
136427a0bc89SDoug Rabson 	 * Allocate a cluster to hold the about to be created directory.
136527a0bc89SDoug Rabson 	 */
1366c3c6d51eSPoul-Henning Kamp 	error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL);
1367952a6212SJordan K. Hubbard 	if (error)
1368952a6212SJordan K. Hubbard 		goto bad2;
1369952a6212SJordan K. Hubbard 
13706a1c2e1fSEd Maste 	memset(&ndirent, 0, sizeof(ndirent));
1371952a6212SJordan K. Hubbard 	ndirent.de_pmp = pmp;
1372952a6212SJordan K. Hubbard 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
1373b732ceb6SPedro F. Giffuni 	vfs_timestamp(&ts);
1374952a6212SJordan K. Hubbard 	DETIMES(&ndirent, &ts, &ts, &ts);
137527a0bc89SDoug Rabson 
137627a0bc89SDoug Rabson 	/*
137727a0bc89SDoug Rabson 	 * Now fill the cluster with the "." and ".." entries. And write
137827a0bc89SDoug Rabson 	 * the cluster to disk.  This way it is there for the parent
137927a0bc89SDoug Rabson 	 * directory to be pointing at if there were a crash.
138027a0bc89SDoug Rabson 	 */
138127a0bc89SDoug Rabson 	bn = cntobn(pmp, newcluster);
138227a0bc89SDoug Rabson 	/* always succeeds */
13837261f5f6SJeff Roberson 	bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0);
13846a1c2e1fSEd Maste 	memset(bp->b_data, 0, pmp->pm_bpcluster);
13856a1c2e1fSEd Maste 	memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
138627a0bc89SDoug Rabson 	denp = (struct direntry *)bp->b_data;
1387952a6212SJordan K. Hubbard 	putushort(denp[0].deStartCluster, newcluster);
1388952a6212SJordan K. Hubbard 	putushort(denp[0].deCDate, ndirent.de_CDate);
1389952a6212SJordan K. Hubbard 	putushort(denp[0].deCTime, ndirent.de_CTime);
1390952a6212SJordan K. Hubbard 	denp[0].deCHundredth = ndirent.de_CHun;
1391952a6212SJordan K. Hubbard 	putushort(denp[0].deADate, ndirent.de_ADate);
1392952a6212SJordan K. Hubbard 	putushort(denp[0].deMDate, ndirent.de_MDate);
1393952a6212SJordan K. Hubbard 	putushort(denp[0].deMTime, ndirent.de_MTime);
1394952a6212SJordan K. Hubbard 	pcl = pdep->de_StartCluster;
13959ec062ddSKonstantin Belousov 	/*
13969ec062ddSKonstantin Belousov 	 * Although the root directory has a non-magic starting cluster
13979ec062ddSKonstantin Belousov 	 * number for FAT32, chkdsk and fsck_msdosfs still require
13989ec062ddSKonstantin Belousov 	 * references to it in dotdot entries to be magic.
13999ec062ddSKonstantin Belousov 	 */
1400952a6212SJordan K. Hubbard 	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
14019ec062ddSKonstantin Belousov 		pcl = MSDOSFSROOT;
1402952a6212SJordan K. Hubbard 	putushort(denp[1].deStartCluster, pcl);
1403952a6212SJordan K. Hubbard 	putushort(denp[1].deCDate, ndirent.de_CDate);
1404952a6212SJordan K. Hubbard 	putushort(denp[1].deCTime, ndirent.de_CTime);
1405952a6212SJordan K. Hubbard 	denp[1].deCHundredth = ndirent.de_CHun;
1406952a6212SJordan K. Hubbard 	putushort(denp[1].deADate, ndirent.de_ADate);
1407952a6212SJordan K. Hubbard 	putushort(denp[1].deMDate, ndirent.de_MDate);
1408952a6212SJordan K. Hubbard 	putushort(denp[1].deMTime, ndirent.de_MTime);
1409952a6212SJordan K. Hubbard 	if (FAT32(pmp)) {
1410952a6212SJordan K. Hubbard 		putushort(denp[0].deHighClust, newcluster >> 16);
14119ec062ddSKonstantin Belousov 		putushort(denp[1].deHighClust, pcl >> 16);
141227a0bc89SDoug Rabson 	}
141327a0bc89SDoug Rabson 
14142aacee77SKonstantin Belousov 	if (DOINGASYNC(ap->a_dvp))
1415cb65c1eeSBruce Evans 		bdwrite(bp);
1416cb65c1eeSBruce Evans 	else if ((error = bwrite(bp)) != 0)
1417952a6212SJordan K. Hubbard 		goto bad;
1418952a6212SJordan K. Hubbard 
141927a0bc89SDoug Rabson 	/*
142027a0bc89SDoug Rabson 	 * Now build up a directory entry pointing to the newly allocated
142127a0bc89SDoug Rabson 	 * cluster.  This will be written to an empty slot in the parent
142227a0bc89SDoug Rabson 	 * directory.
142327a0bc89SDoug Rabson 	 */
1424952a6212SJordan K. Hubbard 	error = uniqdosname(pdep, cnp, ndirent.de_Name);
1425952a6212SJordan K. Hubbard 	if (error)
1426952a6212SJordan K. Hubbard 		goto bad;
1427952a6212SJordan K. Hubbard 
1428952a6212SJordan K. Hubbard 	ndirent.de_Attributes = ATTR_DIRECTORY;
1429bad3d41dSDmitrij Tejblum 	ndirent.de_LowerCase = 0;
1430952a6212SJordan K. Hubbard 	ndirent.de_StartCluster = newcluster;
1431952a6212SJordan K. Hubbard 	ndirent.de_FileSize = 0;
1432952a6212SJordan K. Hubbard 	error = createde(&ndirent, pdep, &dep, cnp);
1433952a6212SJordan K. Hubbard 	if (error)
1434952a6212SJordan K. Hubbard 		goto bad;
1435952a6212SJordan K. Hubbard 	*ap->a_vpp = DETOV(dep);
1436952a6212SJordan K. Hubbard 	return (0);
1437952a6212SJordan K. Hubbard 
1438952a6212SJordan K. Hubbard bad:
143965990b68SKonstantin Belousov 	clusterfree(pmp, newcluster);
1440952a6212SJordan K. Hubbard bad2:
1441952a6212SJordan K. Hubbard 	return (error);
144227a0bc89SDoug Rabson }
144327a0bc89SDoug Rabson 
1444a98ca469SPoul-Henning Kamp static int
msdosfs_rmdir(struct vop_rmdir_args * ap)144510c9700fSEd Maste msdosfs_rmdir(struct vop_rmdir_args *ap)
144627a0bc89SDoug Rabson {
14478994a245SDag-Erling Smørgrav 	struct vnode *vp = ap->a_vp;
14488994a245SDag-Erling Smørgrav 	struct vnode *dvp = ap->a_dvp;
14498994a245SDag-Erling Smørgrav 	struct componentname *cnp = ap->a_cnp;
14508994a245SDag-Erling Smørgrav 	struct denode *ip, *dp;
1451952a6212SJordan K. Hubbard 	int error;
145227a0bc89SDoug Rabson 
1453952a6212SJordan K. Hubbard 	ip = VTODE(vp);
1454952a6212SJordan K. Hubbard 	dp = VTODE(dvp);
145527a0bc89SDoug Rabson 
145627a0bc89SDoug Rabson 	/*
1457952a6212SJordan K. Hubbard 	 * Verify the directory is empty (and valid).
1458952a6212SJordan K. Hubbard 	 * (Rmdir ".." won't be valid since
1459952a6212SJordan K. Hubbard 	 *  ".." will contain a reference to
1460952a6212SJordan K. Hubbard 	 *  the current directory and thus be
1461952a6212SJordan K. Hubbard 	 *  non-empty.)
146227a0bc89SDoug Rabson 	 */
1463952a6212SJordan K. Hubbard 	error = 0;
146495d42526SKonstantin Belousov 	if (!dosdirempty(ip)) {
146527a0bc89SDoug Rabson 		error = ENOTEMPTY;
146627a0bc89SDoug Rabson 		goto out;
146727a0bc89SDoug Rabson 	}
146827a0bc89SDoug Rabson 	/*
146927a0bc89SDoug Rabson 	 * Delete the entry from the directory.  For dos filesystems this
147027a0bc89SDoug Rabson 	 * gets rid of the directory entry on disk, the in memory copy
147127a0bc89SDoug Rabson 	 * still exists but the de_refcnt is <= 0.  This prevents it from
147227a0bc89SDoug Rabson 	 * being found by deget().  When the vput() on dep is done we give
147327a0bc89SDoug Rabson 	 * up access and eventually msdosfs_reclaim() will be called which
147427a0bc89SDoug Rabson 	 * will remove it from the denode cache.
147527a0bc89SDoug Rabson 	 */
1476952a6212SJordan K. Hubbard 	error = removede(dp, ip);
1477c3c6d51eSPoul-Henning Kamp 	if (error)
147827a0bc89SDoug Rabson 		goto out;
147927a0bc89SDoug Rabson 	/*
148027a0bc89SDoug Rabson 	 * This is where we decrement the link count in the parent
148127a0bc89SDoug Rabson 	 * directory.  Since dos filesystems don't do this we just purge
14827be2d300SMike Smith 	 * the name cache.
148327a0bc89SDoug Rabson 	 */
1484952a6212SJordan K. Hubbard 	cache_purge(dvp);
148527a0bc89SDoug Rabson 	/*
148627a0bc89SDoug Rabson 	 * Truncate the directory that is being deleted.
148727a0bc89SDoug Rabson 	 */
1488c52fd858SEdward Tomasz Napierala 	error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred);
1489952a6212SJordan K. Hubbard 	cache_purge(vp);
14907be2d300SMike Smith 
1491952a6212SJordan K. Hubbard out:
1492952a6212SJordan K. Hubbard 	return (error);
149327a0bc89SDoug Rabson }
149427a0bc89SDoug Rabson 
149527a0bc89SDoug Rabson /*
149627a0bc89SDoug Rabson  * DOS filesystems don't know what symlinks are.
149727a0bc89SDoug Rabson  */
1498a98ca469SPoul-Henning Kamp static int
msdosfs_symlink(struct vop_symlink_args * ap)149910c9700fSEd Maste msdosfs_symlink(struct vop_symlink_args *ap)
150027a0bc89SDoug Rabson {
1501952a6212SJordan K. Hubbard 	return (EOPNOTSUPP);
150227a0bc89SDoug Rabson }
150327a0bc89SDoug Rabson 
1504a98ca469SPoul-Henning Kamp static int
msdosfs_readdir(struct vop_readdir_args * ap)150510c9700fSEd Maste msdosfs_readdir(struct vop_readdir_args *ap)
150627a0bc89SDoug Rabson {
1507c2819440SBruce Evans 	struct mbnambuf nb;
150827a0bc89SDoug Rabson 	int error = 0;
150927a0bc89SDoug Rabson 	int diff;
151027a0bc89SDoug Rabson 	long n;
1511952a6212SJordan K. Hubbard 	int blsize;
151227a0bc89SDoug Rabson 	long on;
151327a0bc89SDoug Rabson 	u_long cn;
1514952a6212SJordan K. Hubbard 	u_long dirsperblk;
151527a0bc89SDoug Rabson 	long bias = 0;
1516952a6212SJordan K. Hubbard 	daddr_t bn, lbn;
151727a0bc89SDoug Rabson 	struct buf *bp;
151827a0bc89SDoug Rabson 	struct denode *dep = VTODE(ap->a_vp);
151927a0bc89SDoug Rabson 	struct msdosfsmount *pmp = dep->de_pmp;
152027a0bc89SDoug Rabson 	struct direntry *dentp;
1521952a6212SJordan K. Hubbard 	struct dirent dirbuf;
152227a0bc89SDoug Rabson 	struct uio *uio = ap->a_uio;
1523b214fcceSAlan Somers 	uint64_t *cookies = NULL;
15249abf4d6eSDoug Rabson 	int ncookies = 0;
1525952a6212SJordan K. Hubbard 	off_t offset, off;
1526952a6212SJordan K. Hubbard 	int chksum = -1;
152727a0bc89SDoug Rabson 
152827a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
1529952a6212SJordan K. Hubbard 	printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
153027a0bc89SDoug Rabson 	    ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
153127a0bc89SDoug Rabson #endif
153227a0bc89SDoug Rabson 
153327a0bc89SDoug Rabson 	/*
153427a0bc89SDoug Rabson 	 * msdosfs_readdir() won't operate properly on regular files since
15356bccea7cSRebecca Cran 	 * it does i/o only with the filesystem vnode, and hence can
153627a0bc89SDoug Rabson 	 * retrieve the wrong block from the buffer cache for a plain file.
153727a0bc89SDoug Rabson 	 * So, fail attempts to readdir() on a plain file.
153827a0bc89SDoug Rabson 	 */
153927a0bc89SDoug Rabson 	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
1540952a6212SJordan K. Hubbard 		return (ENOTDIR);
1541952a6212SJordan K. Hubbard 
1542952a6212SJordan K. Hubbard 	/*
1543952a6212SJordan K. Hubbard 	 * To be safe, initialize dirbuf
1544952a6212SJordan K. Hubbard 	 */
15456a1c2e1fSEd Maste 	memset(dirbuf.d_name, 0, sizeof(dirbuf.d_name));
154627a0bc89SDoug Rabson 
154727a0bc89SDoug Rabson 	/*
154827a0bc89SDoug Rabson 	 * If the user buffer is smaller than the size of one dos directory
154927a0bc89SDoug Rabson 	 * entry or the file offset is not a multiple of the size of a
155027a0bc89SDoug Rabson 	 * directory entry, then we fail the read.
155127a0bc89SDoug Rabson 	 */
155269a36f24SMike Smith 	off = offset = uio->uio_offset;
155369a36f24SMike Smith 	if (uio->uio_resid < sizeof(struct direntry) ||
1554952a6212SJordan K. Hubbard 	    (offset & (sizeof(struct direntry) - 1)))
1555952a6212SJordan K. Hubbard 		return (EINVAL);
1556952a6212SJordan K. Hubbard 
1557952a6212SJordan K. Hubbard 	if (ap->a_ncookies) {
1558952a6212SJordan K. Hubbard 		ncookies = uio->uio_resid / 16;
1559b214fcceSAlan Somers 		cookies = malloc(ncookies * sizeof(*cookies), M_TEMP,
1560a163d034SWarner Losh 		       M_WAITOK);
1561952a6212SJordan K. Hubbard 		*ap->a_cookies = cookies;
1562952a6212SJordan K. Hubbard 		*ap->a_ncookies = ncookies;
1563952a6212SJordan K. Hubbard 	}
1564952a6212SJordan K. Hubbard 
1565952a6212SJordan K. Hubbard 	dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
156627a0bc89SDoug Rabson 
156727a0bc89SDoug Rabson 	/*
156827a0bc89SDoug Rabson 	 * If they are reading from the root directory then, we simulate
156927a0bc89SDoug Rabson 	 * the . and .. entries since these don't exist in the root
157027a0bc89SDoug Rabson 	 * directory.  We also set the offset bias to make up for having to
157127a0bc89SDoug Rabson 	 * simulate these entries. By this I mean that at file offset 64 we
157227a0bc89SDoug Rabson 	 * read the first entry in the root directory that lives on disk.
157327a0bc89SDoug Rabson 	 */
1574952a6212SJordan K. Hubbard 	if (dep->de_StartCluster == MSDOSFSROOT
1575952a6212SJordan K. Hubbard 	    || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) {
1576952a6212SJordan K. Hubbard #if 0
1577952a6212SJordan K. Hubbard 		printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
1578952a6212SJordan K. Hubbard 		    offset);
1579952a6212SJordan K. Hubbard #endif
158027a0bc89SDoug Rabson 		bias = 2 * sizeof(struct direntry);
1581952a6212SJordan K. Hubbard 		if (offset < bias) {
1582952a6212SJordan K. Hubbard 			for (n = (int)offset / sizeof(struct direntry);
1583952a6212SJordan K. Hubbard 			     n < 2; n++) {
158440373cf5SKonstantin Belousov 				dirbuf.d_fileno = FAT32(pmp) ?
158540373cf5SKonstantin Belousov 				    (uint64_t)cntobn(pmp, pmp->pm_rootdirblk) *
158640373cf5SKonstantin Belousov 				    dirsperblk : 1;
1587952a6212SJordan K. Hubbard 				dirbuf.d_type = DT_DIR;
1588952a6212SJordan K. Hubbard 				switch (n) {
1589952a6212SJordan K. Hubbard 				case 0:
1590952a6212SJordan K. Hubbard 					dirbuf.d_namlen = 1;
15916d2e2df7SMark Johnston 					dirbuf.d_name[0] = '.';
1592952a6212SJordan K. Hubbard 					break;
1593952a6212SJordan K. Hubbard 				case 1:
1594952a6212SJordan K. Hubbard 					dirbuf.d_namlen = 2;
15956d2e2df7SMark Johnston 					dirbuf.d_name[0] = '.';
15966d2e2df7SMark Johnston 					dirbuf.d_name[1] = '.';
1597952a6212SJordan K. Hubbard 					break;
1598952a6212SJordan K. Hubbard 				}
1599952a6212SJordan K. Hubbard 				dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
16001c4ca778SKonstantin Belousov 				/* NOTE: d_off is the offset of the *next* entry. */
16011c4ca778SKonstantin Belousov 				dirbuf.d_off = offset + sizeof(struct direntry);
16026d2e2df7SMark Johnston 				dirent_terminate(&dirbuf);
1603952a6212SJordan K. Hubbard 				if (uio->uio_resid < dirbuf.d_reclen)
1604952a6212SJordan K. Hubbard 					goto out;
1605c9524588SDag-Erling Smørgrav 				error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1606952a6212SJordan K. Hubbard 				if (error)
1607952a6212SJordan K. Hubbard 					goto out;
160869a36f24SMike Smith 				offset += sizeof(struct direntry);
160969a36f24SMike Smith 				off = offset;
1610952a6212SJordan K. Hubbard 				if (cookies) {
1611952a6212SJordan K. Hubbard 					*cookies++ = offset;
1612952a6212SJordan K. Hubbard 					if (--ncookies <= 0)
161327a0bc89SDoug Rabson 						goto out;
161427a0bc89SDoug Rabson 				}
161527a0bc89SDoug Rabson 			}
161627a0bc89SDoug Rabson 		}
1617952a6212SJordan K. Hubbard 	}
1618952a6212SJordan K. Hubbard 
1619c2819440SBruce Evans 	mbnambuf_init(&nb);
1620952a6212SJordan K. Hubbard 	off = offset;
1621952a6212SJordan K. Hubbard 	while (uio->uio_resid > 0) {
1622952a6212SJordan K. Hubbard 		lbn = de_cluster(pmp, offset - bias);
1623952a6212SJordan K. Hubbard 		on = (offset - bias) & pmp->pm_crbomask;
1624952a6212SJordan K. Hubbard 		n = min(pmp->pm_bpcluster - on, uio->uio_resid);
1625952a6212SJordan K. Hubbard 		diff = dep->de_FileSize - (offset - bias);
1626ebbcda3eSBruce Evans 		if (diff <= 0)
1627ebbcda3eSBruce Evans 			break;
1628952a6212SJordan K. Hubbard 		n = min(n, diff);
1629952a6212SJordan K. Hubbard 		error = pcbmap(dep, lbn, &bn, &cn, &blsize);
163027a0bc89SDoug Rabson 		if (error)
163127a0bc89SDoug Rabson 			break;
1632952a6212SJordan K. Hubbard 		error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
163327a0bc89SDoug Rabson 		if (error) {
1634952a6212SJordan K. Hubbard 			return (error);
1635952a6212SJordan K. Hubbard 		}
1636952a6212SJordan K. Hubbard 		n = min(n, blsize - bp->b_resid);
16373c01bab8STom Rhodes 		if (n == 0) {
16383c01bab8STom Rhodes 			brelse(bp);
16393c01bab8STom Rhodes 			return (EIO);
16403c01bab8STom Rhodes 		}
1641952a6212SJordan K. Hubbard 
1642952a6212SJordan K. Hubbard 		/*
1643952a6212SJordan K. Hubbard 		 * Convert from dos directory entries to fs-independent
1644952a6212SJordan K. Hubbard 		 * directory entries.
1645952a6212SJordan K. Hubbard 		 */
1646952a6212SJordan K. Hubbard 		for (dentp = (struct direntry *)(bp->b_data + on);
1647952a6212SJordan K. Hubbard 		     (char *)dentp < bp->b_data + on + n;
1648952a6212SJordan K. Hubbard 		     dentp++, offset += sizeof(struct direntry)) {
1649952a6212SJordan K. Hubbard #if 0
1650952a6212SJordan K. Hubbard 			printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
1651952a6212SJordan K. Hubbard 			    dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
1652952a6212SJordan K. Hubbard #endif
1653952a6212SJordan K. Hubbard 			/*
1654952a6212SJordan K. Hubbard 			 * If this is an unused entry, we can stop.
1655952a6212SJordan K. Hubbard 			 */
1656952a6212SJordan K. Hubbard 			if (dentp->deName[0] == SLOT_EMPTY) {
1657952a6212SJordan K. Hubbard 				brelse(bp);
1658952a6212SJordan K. Hubbard 				goto out;
1659952a6212SJordan K. Hubbard 			}
1660952a6212SJordan K. Hubbard 			/*
1661952a6212SJordan K. Hubbard 			 * Skip deleted entries.
1662952a6212SJordan K. Hubbard 			 */
1663952a6212SJordan K. Hubbard 			if (dentp->deName[0] == SLOT_DELETED) {
1664952a6212SJordan K. Hubbard 				chksum = -1;
1665c2819440SBruce Evans 				mbnambuf_init(&nb);
1666952a6212SJordan K. Hubbard 				continue;
166727a0bc89SDoug Rabson 			}
166827a0bc89SDoug Rabson 
166927a0bc89SDoug Rabson 			/*
1670952a6212SJordan K. Hubbard 			 * Handle Win95 long directory entries
167127a0bc89SDoug Rabson 			 */
1672952a6212SJordan K. Hubbard 			if (dentp->deAttributes == ATTR_WIN95) {
1673952a6212SJordan K. Hubbard 				if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
1674952a6212SJordan K. Hubbard 					continue;
1675c2819440SBruce Evans 				chksum = win2unixfn(&nb,
1676c2819440SBruce Evans 				    (struct winentry *)dentp, chksum, pmp);
1677952a6212SJordan K. Hubbard 				continue;
167827a0bc89SDoug Rabson 			}
1679952a6212SJordan K. Hubbard 
168027a0bc89SDoug Rabson 			/*
1681952a6212SJordan K. Hubbard 			 * Skip volume labels
1682952a6212SJordan K. Hubbard 			 */
1683952a6212SJordan K. Hubbard 			if (dentp->deAttributes & ATTR_VOLUME) {
1684952a6212SJordan K. Hubbard 				chksum = -1;
1685c2819440SBruce Evans 				mbnambuf_init(&nb);
1686952a6212SJordan K. Hubbard 				continue;
1687952a6212SJordan K. Hubbard 			}
1688952a6212SJordan K. Hubbard 			/*
1689952a6212SJordan K. Hubbard 			 * This computation of d_fileno must match
169027a0bc89SDoug Rabson 			 * the computation of va_fileid in
1691952a6212SJordan K. Hubbard 			 * msdosfs_getattr.
169227a0bc89SDoug Rabson 			 */
169327a0bc89SDoug Rabson 			if (dentp->deAttributes & ATTR_DIRECTORY) {
169440373cf5SKonstantin Belousov 				cn = getushort(dentp->deStartCluster);
169540373cf5SKonstantin Belousov 				if (FAT32(pmp)) {
169640373cf5SKonstantin Belousov 					cn |= getushort(dentp->deHighClust) <<
169740373cf5SKonstantin Belousov 					    16;
169840373cf5SKonstantin Belousov 					if (cn == MSDOSFSROOT)
169940373cf5SKonstantin Belousov 						cn = pmp->pm_rootdirblk;
170040373cf5SKonstantin Belousov 				}
170140373cf5SKonstantin Belousov 				if (cn == MSDOSFSROOT && !FAT32(pmp))
170240373cf5SKonstantin Belousov 					dirbuf.d_fileno = 1;
1703952a6212SJordan K. Hubbard 				else
170440373cf5SKonstantin Belousov 					dirbuf.d_fileno = cntobn(pmp, cn) *
17053bc482ecSTim J. Robbins 					    dirsperblk;
1706952a6212SJordan K. Hubbard 				dirbuf.d_type = DT_DIR;
170727a0bc89SDoug Rabson 			} else {
170840373cf5SKonstantin Belousov 				dirbuf.d_fileno = (uoff_t)offset /
1709c0f5121cSBruce Evans 				    sizeof(struct direntry);
1710952a6212SJordan K. Hubbard 				dirbuf.d_type = DT_REG;
171127a0bc89SDoug Rabson 			}
1712f458f2a5SCraig Rodrigues 
171348d1bcf8SKonstantin Belousov 			if (chksum != winChksum(dentp->deName)) {
1714952a6212SJordan K. Hubbard 				dirbuf.d_namlen = dos2unixfn(dentp->deName,
1715952a6212SJordan K. Hubbard 				    (u_char *)dirbuf.d_name,
1716bad3d41dSDmitrij Tejblum 				    dentp->deLowerCase |
1717bad3d41dSDmitrij Tejblum 					((pmp->pm_flags & MSDOSFSMNT_SHORTNAME) ?
1718bad3d41dSDmitrij Tejblum 					(LCASE_BASE | LCASE_EXT) : 0),
1719c4f02a89SMax Khon 				    pmp);
1720c2819440SBruce Evans 				mbnambuf_init(&nb);
1721c4f02a89SMax Khon 			} else
1722c2819440SBruce Evans 				mbnambuf_flush(&nb, &dirbuf);
1723952a6212SJordan K. Hubbard 			chksum = -1;
1724952a6212SJordan K. Hubbard 			dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
17251c4ca778SKonstantin Belousov 			/* NOTE: d_off is the offset of the *next* entry. */
17261c4ca778SKonstantin Belousov 			dirbuf.d_off = offset + sizeof(struct direntry);
1727599f9044SMark Johnston 			dirent_terminate(&dirbuf);
1728952a6212SJordan K. Hubbard 			if (uio->uio_resid < dirbuf.d_reclen) {
172927a0bc89SDoug Rabson 				brelse(bp);
1730952a6212SJordan K. Hubbard 				goto out;
173127a0bc89SDoug Rabson 			}
1732c9524588SDag-Erling Smørgrav 			error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
1733952a6212SJordan K. Hubbard 			if (error) {
1734952a6212SJordan K. Hubbard 				brelse(bp);
1735952a6212SJordan K. Hubbard 				goto out;
1736952a6212SJordan K. Hubbard 			}
1737952a6212SJordan K. Hubbard 			if (cookies) {
173869a36f24SMike Smith 				*cookies++ = offset + sizeof(struct direntry);
1739952a6212SJordan K. Hubbard 				if (--ncookies <= 0) {
1740952a6212SJordan K. Hubbard 					brelse(bp);
1741952a6212SJordan K. Hubbard 					goto out;
1742952a6212SJordan K. Hubbard 				}
1743952a6212SJordan K. Hubbard 			}
174469a36f24SMike Smith 			off = offset + sizeof(struct direntry);
1745952a6212SJordan K. Hubbard 		}
1746952a6212SJordan K. Hubbard 		brelse(bp);
1747952a6212SJordan K. Hubbard 	}
1748952a6212SJordan K. Hubbard out:
1749952a6212SJordan K. Hubbard 	/* Subtract unused cookies */
1750952a6212SJordan K. Hubbard 	if (ap->a_ncookies)
1751952a6212SJordan K. Hubbard 		*ap->a_ncookies -= ncookies;
175227a0bc89SDoug Rabson 
175369a36f24SMike Smith 	uio->uio_offset = off;
17549abf4d6eSDoug Rabson 
175527a0bc89SDoug Rabson 	/*
17566213f6fcSPaul Traina 	 * Set the eofflag (NFS uses it)
175727a0bc89SDoug Rabson 	 */
1758dfd5dee1SPeter Wemm 	if (ap->a_eofflag) {
1759952a6212SJordan K. Hubbard 		if (dep->de_FileSize - (offset - bias) <= 0)
176027a0bc89SDoug Rabson 			*ap->a_eofflag = 1;
176127a0bc89SDoug Rabson 		else
176227a0bc89SDoug Rabson 			*ap->a_eofflag = 0;
1763dfd5dee1SPeter Wemm 	}
1764952a6212SJordan K. Hubbard 	return (error);
176527a0bc89SDoug Rabson }
176627a0bc89SDoug Rabson 
1767d34b0a1bSBruce Evans /*-
1768d34b0a1bSBruce Evans  * a_vp   - pointer to the file's vnode
1769d34b0a1bSBruce Evans  * a_bn   - logical block number within the file (cluster number for us)
1770d34b0a1bSBruce Evans  * a_bop  - where to return the bufobj of the special file containing the fs
1771d34b0a1bSBruce Evans  * a_bnp  - where to return the "physical" block number corresponding to a_bn
1772d34b0a1bSBruce Evans  *          (relative to the special file; units are blocks of size DEV_BSIZE)
1773d34b0a1bSBruce Evans  * a_runp - where to return the "run past" a_bn.  This is the count of logical
1774d34b0a1bSBruce Evans  *          blocks whose physical blocks (together with a_bn's physical block)
1775d34b0a1bSBruce Evans  *          are contiguous.
1776d34b0a1bSBruce Evans  * a_runb - where to return the "run before" a_bn.
177727a0bc89SDoug Rabson  */
1778a98ca469SPoul-Henning Kamp static int
msdosfs_bmap(struct vop_bmap_args * ap)177910c9700fSEd Maste msdosfs_bmap(struct vop_bmap_args *ap)
178027a0bc89SDoug Rabson {
1781416e232cSBruce Evans 	struct fatcache savefc;
1782d34b0a1bSBruce Evans 	struct denode *dep;
17836b6c5f5eSBruce Evans 	struct mount *mp;
1784d34b0a1bSBruce Evans 	struct msdosfsmount *pmp;
1785d34b0a1bSBruce Evans 	struct vnode *vp;
17866b6c5f5eSBruce Evans 	daddr_t runbn;
1787d34b0a1bSBruce Evans 	u_long cn;
17886b6c5f5eSBruce Evans 	int bnpercn, error, maxio, maxrun, run;
178927a0bc89SDoug Rabson 
1790d34b0a1bSBruce Evans 	vp = ap->a_vp;
1791d34b0a1bSBruce Evans 	dep = VTODE(vp);
1792d34b0a1bSBruce Evans 	pmp = dep->de_pmp;
17939c83534dSPoul-Henning Kamp 	if (ap->a_bop != NULL)
1794d34b0a1bSBruce Evans 		*ap->a_bop = &pmp->pm_devvp->v_bufobj;
179527a0bc89SDoug Rabson 	if (ap->a_bnp == NULL)
1796952a6212SJordan K. Hubbard 		return (0);
1797d34b0a1bSBruce Evans 	if (ap->a_runp != NULL)
179827a0bc89SDoug Rabson 		*ap->a_runp = 0;
1799d34b0a1bSBruce Evans 	if (ap->a_runb != NULL)
1800c83ebe77SJohn Dyson 		*ap->a_runb = 0;
1801d34b0a1bSBruce Evans 	cn = ap->a_bn;
1802d34b0a1bSBruce Evans 	if (cn != ap->a_bn)
1803d34b0a1bSBruce Evans 		return (EFBIG);
1804d34b0a1bSBruce Evans 	error = pcbmap(dep, cn, ap->a_bnp, NULL, NULL);
18056b6c5f5eSBruce Evans 	if (error != 0 || (ap->a_runp == NULL && ap->a_runb == NULL))
18060d2af521SKirk McKusick 		return (error);
18076b6c5f5eSBruce Evans 
1808416e232cSBruce Evans 	/*
1809416e232cSBruce Evans 	 * Prepare to back out updates of the fatchain cache after the one
1810416e232cSBruce Evans 	 * for the first block done by pcbmap() above.  Without the backout,
1811416e232cSBruce Evans 	 * then whenever the caller doesn't do i/o to all of the blocks that
1812416e232cSBruce Evans 	 * we find, the single useful cache entry would be too far in advance
1813416e232cSBruce Evans 	 * of the actual i/o to work for the next sequential i/o.  Then the
1814416e232cSBruce Evans 	 * FAT would be searched from the beginning.  With the backout, the
1815416e232cSBruce Evans 	 * FAT is searched starting at most a few blocks early.  This wastes
1816416e232cSBruce Evans 	 * much less time.  Time is also wasted finding more blocks than the
1817416e232cSBruce Evans 	 * caller will do i/o to.  This is necessary because the runlength
1818416e232cSBruce Evans 	 * parameters are output-only.
1819416e232cSBruce Evans 	 */
1820416e232cSBruce Evans 	savefc = dep->de_fc[FC_LASTMAP];
1821416e232cSBruce Evans 
18226b6c5f5eSBruce Evans 	mp = vp->v_mount;
18236b6c5f5eSBruce Evans 	maxio = mp->mnt_iosize_max / mp->mnt_stat.f_iosize;
18246b6c5f5eSBruce Evans 	bnpercn = de_cn2bn(pmp, 1);
18256b6c5f5eSBruce Evans 	if (ap->a_runp != NULL) {
18266b6c5f5eSBruce Evans 		maxrun = ulmin(maxio - 1, pmp->pm_maxcluster - cn);
18276b6c5f5eSBruce Evans 		for (run = 1; run <= maxrun; run++) {
18286b6c5f5eSBruce Evans 			if (pcbmap(dep, cn + run, &runbn, NULL, NULL) != 0 ||
18296b6c5f5eSBruce Evans 			    runbn != *ap->a_bnp + run * bnpercn)
18306b6c5f5eSBruce Evans 				break;
18316b6c5f5eSBruce Evans 		}
18326b6c5f5eSBruce Evans 		*ap->a_runp = run - 1;
18336b6c5f5eSBruce Evans 	}
18346b6c5f5eSBruce Evans 	if (ap->a_runb != NULL) {
18356b6c5f5eSBruce Evans 		maxrun = ulmin(maxio - 1, cn);
18366b6c5f5eSBruce Evans 		for (run = 1; run < maxrun; run++) {
18376b6c5f5eSBruce Evans 			if (pcbmap(dep, cn - run, &runbn, NULL, NULL) != 0 ||
18386b6c5f5eSBruce Evans 			    runbn != *ap->a_bnp - run * bnpercn)
18396b6c5f5eSBruce Evans 				break;
18406b6c5f5eSBruce Evans 		}
18416b6c5f5eSBruce Evans 		*ap->a_runb = run - 1;
18426b6c5f5eSBruce Evans 	}
1843416e232cSBruce Evans 	dep->de_fc[FC_LASTMAP] = savefc;
18446b6c5f5eSBruce Evans 	return (0);
184527a0bc89SDoug Rabson }
184627a0bc89SDoug Rabson 
18477029da5cSPawel Biernacki SYSCTL_NODE(_vfs, OID_AUTO, msdosfs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
18487029da5cSPawel Biernacki     "msdos filesystem");
184906965e96SKonstantin Belousov static int use_buf_pager = 1;
185006965e96SKonstantin Belousov SYSCTL_INT(_vfs_msdosfs, OID_AUTO, use_buf_pager, CTLFLAG_RWTUN,
185106965e96SKonstantin Belousov     &use_buf_pager, 0,
185206965e96SKonstantin Belousov     "Use buffer pager instead of bmap");
185306965e96SKonstantin Belousov 
185406965e96SKonstantin Belousov static daddr_t
msdosfs_gbp_getblkno(struct vnode * vp,vm_ooffset_t off)185506965e96SKonstantin Belousov msdosfs_gbp_getblkno(struct vnode *vp, vm_ooffset_t off)
185606965e96SKonstantin Belousov {
185706965e96SKonstantin Belousov 
185806965e96SKonstantin Belousov 	return (de_cluster(VTODE(vp)->de_pmp, off));
185906965e96SKonstantin Belousov }
186006965e96SKonstantin Belousov 
186106965e96SKonstantin Belousov static int
msdosfs_gbp_getblksz(struct vnode * vp,daddr_t lbn,long * sz)1862197a4f29SKonstantin Belousov msdosfs_gbp_getblksz(struct vnode *vp, daddr_t lbn, long *sz)
186306965e96SKonstantin Belousov {
186406965e96SKonstantin Belousov 
1865197a4f29SKonstantin Belousov 	*sz = VTODE(vp)->de_pmp->pm_bpcluster;
1866197a4f29SKonstantin Belousov 	return (0);
186706965e96SKonstantin Belousov }
186806965e96SKonstantin Belousov 
186906965e96SKonstantin Belousov static int
msdosfs_getpages(struct vop_getpages_args * ap)187006965e96SKonstantin Belousov msdosfs_getpages(struct vop_getpages_args *ap)
187106965e96SKonstantin Belousov {
187206965e96SKonstantin Belousov 
187306965e96SKonstantin Belousov 	if (use_buf_pager)
187406965e96SKonstantin Belousov 		return (vfs_bio_getpages(ap->a_vp, ap->a_m, ap->a_count,
187506965e96SKonstantin Belousov 		    ap->a_rbehind, ap->a_rahead, msdosfs_gbp_getblkno,
187606965e96SKonstantin Belousov 		    msdosfs_gbp_getblksz));
187706965e96SKonstantin Belousov 	return (vnode_pager_generic_getpages(ap->a_vp, ap->a_m, ap->a_count,
187806965e96SKonstantin Belousov 	    ap->a_rbehind, ap->a_rahead, NULL, NULL));
187906965e96SKonstantin Belousov }
188006965e96SKonstantin Belousov 
1881a98ca469SPoul-Henning Kamp static int
msdosfs_strategy(struct vop_strategy_args * ap)188210c9700fSEd Maste msdosfs_strategy(struct vop_strategy_args *ap)
188327a0bc89SDoug Rabson {
188427a0bc89SDoug Rabson 	struct buf *bp = ap->a_bp;
1885d83b7498SPoul-Henning Kamp 	struct denode *dep = VTODE(ap->a_vp);
18869a135592SPoul-Henning Kamp 	struct bufobj *bo;
188727a0bc89SDoug Rabson 	int error = 0;
18880d2af521SKirk McKusick 	daddr_t blkno;
188927a0bc89SDoug Rabson 
189027a0bc89SDoug Rabson 	/*
189127a0bc89SDoug Rabson 	 * If we don't already know the filesystem relative block number
189227a0bc89SDoug Rabson 	 * then get it using pcbmap().  If pcbmap() returns the block
189327a0bc89SDoug Rabson 	 * number as -1 then we've got a hole in the file.  DOS filesystems
189427a0bc89SDoug Rabson 	 * don't allow files with holes, so we shouldn't ever see this.
189527a0bc89SDoug Rabson 	 */
189627a0bc89SDoug Rabson 	if (bp->b_blkno == bp->b_lblkno) {
18970d2af521SKirk McKusick 		error = pcbmap(dep, bp->b_lblkno, &blkno, 0, 0);
18980d2af521SKirk McKusick 		bp->b_blkno = blkno;
1899952a6212SJordan K. Hubbard 		if (error) {
1900952a6212SJordan K. Hubbard 			bp->b_error = error;
1901c244d2deSPoul-Henning Kamp 			bp->b_ioflags |= BIO_ERROR;
19028177437dSPoul-Henning Kamp 			bufdone(bp);
19030da50f6eSEdward Tomasz Napierala 			return (0);
1904952a6212SJordan K. Hubbard 		}
1905952a6212SJordan K. Hubbard 		if ((long)bp->b_blkno == -1)
1906952a6212SJordan K. Hubbard 			vfs_bio_clrbuf(bp);
190727a0bc89SDoug Rabson 	}
190827a0bc89SDoug Rabson 	if (bp->b_blkno == -1) {
19098177437dSPoul-Henning Kamp 		bufdone(bp);
1910952a6212SJordan K. Hubbard 		return (0);
191127a0bc89SDoug Rabson 	}
191227a0bc89SDoug Rabson 	/*
191327a0bc89SDoug Rabson 	 * Read/write the block from/to the disk that contains the desired
191427a0bc89SDoug Rabson 	 * file block.
191527a0bc89SDoug Rabson 	 */
19162c18019fSPoul-Henning Kamp 	bp->b_iooffset = dbtob(bp->b_blkno);
19179a135592SPoul-Henning Kamp 	bo = dep->de_pmp->pm_bo;
19180391e5a1SPoul-Henning Kamp 	BO_STRATEGY(bo, bp);
1919952a6212SJordan K. Hubbard 	return (0);
192027a0bc89SDoug Rabson }
192127a0bc89SDoug Rabson 
1922a98ca469SPoul-Henning Kamp static int
msdosfs_print(struct vop_print_args * ap)192310c9700fSEd Maste msdosfs_print(struct vop_print_args *ap)
192427a0bc89SDoug Rabson {
192527a0bc89SDoug Rabson 	struct denode *dep = VTODE(ap->a_vp);
192627a0bc89SDoug Rabson 
192799648386SNate Lawson 	printf("\tstartcluster %lu, dircluster %lu, diroffset %lu, ",
192886ed6d45SNate Lawson 	       dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
1929c72ae142SJohn Baldwin 	printf("on dev %s\n", devtoname(dep->de_pmp->pm_dev));
1930952a6212SJordan K. Hubbard 	return (0);
193127a0bc89SDoug Rabson }
193227a0bc89SDoug Rabson 
1933a98ca469SPoul-Henning Kamp static int
msdosfs_pathconf(struct vop_pathconf_args * ap)193410c9700fSEd Maste msdosfs_pathconf(struct vop_pathconf_args *ap)
193527a0bc89SDoug Rabson {
1936952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp;
1937952a6212SJordan K. Hubbard 
193827a0bc89SDoug Rabson 	switch (ap->a_name) {
1939853b3a8aSJohn Baldwin 	case _PC_FILESIZEBITS:
1940853b3a8aSJohn Baldwin 		*ap->a_retval = 32;
1941853b3a8aSJohn Baldwin 		return (0);
194227a0bc89SDoug Rabson 	case _PC_LINK_MAX:
194327a0bc89SDoug Rabson 		*ap->a_retval = 1;
1944952a6212SJordan K. Hubbard 		return (0);
194527a0bc89SDoug Rabson 	case _PC_NAME_MAX:
1946952a6212SJordan K. Hubbard 		*ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12;
1947952a6212SJordan K. Hubbard 		return (0);
1948599afe53SJohn Baldwin 	case _PC_CHOWN_RESTRICTED:
1949599afe53SJohn Baldwin 		*ap->a_retval = 1;
1950599afe53SJohn Baldwin 		return (0);
195127a0bc89SDoug Rabson 	case _PC_NO_TRUNC:
195227a0bc89SDoug Rabson 		*ap->a_retval = 0;
1953952a6212SJordan K. Hubbard 		return (0);
195427a0bc89SDoug Rabson 	default:
195515a88f81SJohn Baldwin 		return (vop_stdpathconf(ap));
195627a0bc89SDoug Rabson 	}
1957952a6212SJordan K. Hubbard 	/* NOTREACHED */
195827a0bc89SDoug Rabson }
195927a0bc89SDoug Rabson 
1960cede1f56STom Rhodes static int
msdosfs_vptofh(struct vop_vptofh_args * ap)196110c9700fSEd Maste msdosfs_vptofh(struct vop_vptofh_args *ap)
196210bcafe9SPawel Jakub Dawidek {
196310bcafe9SPawel Jakub Dawidek 	struct denode *dep;
196410bcafe9SPawel Jakub Dawidek 	struct defid *defhp;
1965*91b5592aSRick Macklem 	_Static_assert(sizeof(struct defid) <= sizeof(struct fid),
1966*91b5592aSRick Macklem 	    "struct defid cannot be larger than struct fid");
196710bcafe9SPawel Jakub Dawidek 
196810bcafe9SPawel Jakub Dawidek 	dep = VTODE(ap->a_vp);
196910bcafe9SPawel Jakub Dawidek 	defhp = (struct defid *)ap->a_fhp;
197010bcafe9SPawel Jakub Dawidek 	defhp->defid_len = sizeof(struct defid);
197110bcafe9SPawel Jakub Dawidek 	defhp->defid_dirclust = dep->de_dirclust;
197210bcafe9SPawel Jakub Dawidek 	defhp->defid_dirofs = dep->de_diroffset;
197310bcafe9SPawel Jakub Dawidek 	/* defhp->defid_gen = dep->de_gen; */
197410bcafe9SPawel Jakub Dawidek 	return (0);
197510bcafe9SPawel Jakub Dawidek }
197610bcafe9SPawel Jakub Dawidek 
197727a0bc89SDoug Rabson /* Global vfs data structures for msdosfs */
1978aec0fb7bSPoul-Henning Kamp struct vop_vector msdosfs_vnodeops = {
1979aec0fb7bSPoul-Henning Kamp 	.vop_default =		&default_vnodeops,
198083c64397SPoul-Henning Kamp 
1981aec0fb7bSPoul-Henning Kamp 	.vop_access =		msdosfs_access,
1982aec0fb7bSPoul-Henning Kamp 	.vop_bmap =		msdosfs_bmap,
198306965e96SKonstantin Belousov 	.vop_getpages =		msdosfs_getpages,
1984aec0fb7bSPoul-Henning Kamp 	.vop_cachedlookup =	msdosfs_lookup,
198572b3e305SPeter Edwards 	.vop_open =		msdosfs_open,
1986aec0fb7bSPoul-Henning Kamp 	.vop_close =		msdosfs_close,
1987aec0fb7bSPoul-Henning Kamp 	.vop_create =		msdosfs_create,
1988aec0fb7bSPoul-Henning Kamp 	.vop_fsync =		msdosfs_fsync,
198947e61f6cSKonstantin Belousov 	.vop_fdatasync =	vop_stdfdatasync_buf,
1990aec0fb7bSPoul-Henning Kamp 	.vop_getattr =		msdosfs_getattr,
1991aec0fb7bSPoul-Henning Kamp 	.vop_inactive =		msdosfs_inactive,
1992aec0fb7bSPoul-Henning Kamp 	.vop_link =		msdosfs_link,
1993aec0fb7bSPoul-Henning Kamp 	.vop_lookup =		vfs_cache_lookup,
1994aec0fb7bSPoul-Henning Kamp 	.vop_mkdir =		msdosfs_mkdir,
1995aec0fb7bSPoul-Henning Kamp 	.vop_mknod =		msdosfs_mknod,
1996aec0fb7bSPoul-Henning Kamp 	.vop_pathconf =		msdosfs_pathconf,
1997aec0fb7bSPoul-Henning Kamp 	.vop_print =		msdosfs_print,
1998aec0fb7bSPoul-Henning Kamp 	.vop_read =		msdosfs_read,
1999aec0fb7bSPoul-Henning Kamp 	.vop_readdir =		msdosfs_readdir,
2000aec0fb7bSPoul-Henning Kamp 	.vop_reclaim =		msdosfs_reclaim,
2001aec0fb7bSPoul-Henning Kamp 	.vop_remove =		msdosfs_remove,
2002aec0fb7bSPoul-Henning Kamp 	.vop_rename =		msdosfs_rename,
2003aec0fb7bSPoul-Henning Kamp 	.vop_rmdir =		msdosfs_rmdir,
2004aec0fb7bSPoul-Henning Kamp 	.vop_setattr =		msdosfs_setattr,
2005aec0fb7bSPoul-Henning Kamp 	.vop_strategy =		msdosfs_strategy,
2006aec0fb7bSPoul-Henning Kamp 	.vop_symlink =		msdosfs_symlink,
2007aec0fb7bSPoul-Henning Kamp 	.vop_write =		msdosfs_write,
200810bcafe9SPawel Jakub Dawidek 	.vop_vptofh =		msdosfs_vptofh,
200927a0bc89SDoug Rabson };
20106fa079fcSMateusz Guzik VFS_VOP_VECTOR_REGISTER(msdosfs_vnodeops);
2011