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