1952a6212SJordan K. Hubbard /* $NetBSD: msdosfs_denode.c,v 1.28 1998/02/10 14:10:00 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>
5427a0bc89SDoug Rabson #include <sys/buf.h>
553c960d93SPoul-Henning Kamp #include <sys/clock.h>
56a878a31cSBruce Evans #include <sys/kernel.h>
57a878a31cSBruce Evans #include <sys/malloc.h>
58a878a31cSBruce Evans #include <sys/mount.h>
599ed01c32SGleb Smirnoff #include <sys/vmmeter.h>
6027a0bc89SDoug Rabson #include <sys/vnode.h>
6127a0bc89SDoug Rabson
62c3c6d51eSPoul-Henning Kamp #include <vm/vm.h>
63efeaf95aSDavid Greenman #include <vm/vm_extern.h>
64c3c6d51eSPoul-Henning Kamp
651166fb51SRuslan Ermilov #include <fs/msdosfs/bpb.h>
661166fb51SRuslan Ermilov #include <fs/msdosfs/direntry.h>
671166fb51SRuslan Ermilov #include <fs/msdosfs/denode.h>
681166fb51SRuslan Ermilov #include <fs/msdosfs/fat.h>
69a878a31cSBruce Evans #include <fs/msdosfs/msdosfsmount.h>
7027a0bc89SDoug Rabson
715bb84bc8SRobert Watson static MALLOC_DEFINE(M_MSDOSFSNODE, "msdosfs_node", "MSDOSFS vnode private part");
7255166637SPoul-Henning Kamp
73f4b423aeSPoul-Henning Kamp static int
de_vncmpf(struct vnode * vp,void * arg)74f4b423aeSPoul-Henning Kamp de_vncmpf(struct vnode *vp, void *arg)
75f4b423aeSPoul-Henning Kamp {
76f4b423aeSPoul-Henning Kamp struct denode *de;
77f4b423aeSPoul-Henning Kamp uint64_t *a;
78f4b423aeSPoul-Henning Kamp
79f4b423aeSPoul-Henning Kamp a = arg;
80f4b423aeSPoul-Henning Kamp de = VTODE(vp);
815fdac752SKyle Evans return (de->de_inode != *a) || (de->de_refcnt <= 0);
82f4b423aeSPoul-Henning Kamp }
8327a0bc89SDoug Rabson
8427a0bc89SDoug Rabson /*
8527a0bc89SDoug Rabson * If deget() succeeds it returns with the gotten denode locked().
8627a0bc89SDoug Rabson *
8727a0bc89SDoug Rabson * pmp - address of msdosfsmount structure of the filesystem containing
883b97f388SPoul-Henning Kamp * the denode of interest. The address of
8927a0bc89SDoug Rabson * the msdosfsmount structure are used.
9027a0bc89SDoug Rabson * dirclust - which cluster bp contains, if dirclust is 0 (root directory)
9127a0bc89SDoug Rabson * diroffset is relative to the beginning of the root directory,
9227a0bc89SDoug Rabson * otherwise it is cluster relative.
9327a0bc89SDoug Rabson * diroffset - offset past begin of cluster of denode we want
94ae7e8a02SKonstantin Belousov * lkflags - locking flags (LK_NOWAIT)
9527a0bc89SDoug Rabson * depp - returns the address of the gotten denode.
9627a0bc89SDoug Rabson */
9727a0bc89SDoug Rabson int
deget(struct msdosfsmount * pmp,u_long dirclust,u_long diroffset,int lkflags,struct denode ** depp)9810c9700fSEd Maste deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
99ae7e8a02SKonstantin Belousov int lkflags, struct denode **depp)
10027a0bc89SDoug Rabson {
10127a0bc89SDoug Rabson int error;
102f4b423aeSPoul-Henning Kamp uint64_t inode;
10327a0bc89SDoug Rabson struct mount *mntp = pmp->pm_mountp;
104952a6212SJordan K. Hubbard struct direntry *direntptr;
10527a0bc89SDoug Rabson struct denode *ldep;
106a30fc63bSPoul-Henning Kamp struct vnode *nvp, *xvp;
10727a0bc89SDoug Rabson struct buf *bp;
10827a0bc89SDoug Rabson
10927a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
110ae7e8a02SKonstantin Belousov printf("deget(pmp %p, dirclust %lu, diroffset %lx, flags %#x, "
111ae7e8a02SKonstantin Belousov "depp %p)\n",
1122d8cf575SStefan Eßer pmp, dirclust, diroffset, lkflags, depp);
11327a0bc89SDoug Rabson #endif
114ae7e8a02SKonstantin Belousov MPASS((lkflags & LK_TYPE_MASK) == LK_EXCLUSIVE);
11527a0bc89SDoug Rabson
11627a0bc89SDoug Rabson /*
117952a6212SJordan K. Hubbard * On FAT32 filesystems, root is a (more or less) normal
118952a6212SJordan K. Hubbard * directory
11927a0bc89SDoug Rabson */
120952a6212SJordan K. Hubbard if (FAT32(pmp) && dirclust == MSDOSFSROOT)
121952a6212SJordan K. Hubbard dirclust = pmp->pm_rootdirblk;
12227a0bc89SDoug Rabson
12327a0bc89SDoug Rabson /*
12427a0bc89SDoug Rabson * See if the denode is in the denode cache. Use the location of
12527a0bc89SDoug Rabson * the directory entry to compute the hash value. For subdir use
126952a6212SJordan K. Hubbard * address of "." entry. For root dir (if not FAT32) use cluster
127952a6212SJordan K. Hubbard * MSDOSFSROOT, offset MSDOSFSROOT_OFS
12827a0bc89SDoug Rabson *
1295fdac752SKyle Evans * NOTE: de_vncmpf will explicitly skip any denodes that do not have
1300721306cSGordon Bergling * a de_refcnt > 0. This insures that we do not attempt to use
1315fdac752SKyle Evans * a denode that represents an unlinked but still open file.
13227a0bc89SDoug Rabson * These files are not to be accessible even when the directory
13327a0bc89SDoug Rabson * entry that represented the file happens to be reused while the
13427a0bc89SDoug Rabson * deleted file is still open.
13527a0bc89SDoug Rabson */
136*445d3d22SStefan Eßer inode = DETOI(pmp, dirclust, diroffset);
137f4b423aeSPoul-Henning Kamp
138ae7e8a02SKonstantin Belousov error = vfs_hash_get(mntp, inode, lkflags, curthread, &nvp,
139f4b423aeSPoul-Henning Kamp de_vncmpf, &inode);
140*445d3d22SStefan Eßer #ifdef MSDOSFS_DEBUG
141*445d3d22SStefan Eßer printf("vfs_hash_get(inode %lu) error %d\n", inode, error);
142*445d3d22SStefan Eßer #endif
143a30fc63bSPoul-Henning Kamp if (error)
144a30fc63bSPoul-Henning Kamp return (error);
145a30fc63bSPoul-Henning Kamp if (nvp != NULL) {
146a30fc63bSPoul-Henning Kamp *depp = VTODE(nvp);
147595ed4d7SKonstantin Belousov if ((*depp)->de_dirclust != dirclust) {
148595ed4d7SKonstantin Belousov printf("%s: wrong dir cluster %lu %lu\n",
149595ed4d7SKonstantin Belousov pmp->pm_mountp->mnt_stat.f_mntonname,
150595ed4d7SKonstantin Belousov (*depp)->de_dirclust, dirclust);
151595ed4d7SKonstantin Belousov goto badoff;
152595ed4d7SKonstantin Belousov }
153595ed4d7SKonstantin Belousov if ((*depp)->de_diroffset != diroffset) {
154595ed4d7SKonstantin Belousov printf("%s: wrong dir offset %lu %lu\n",
155595ed4d7SKonstantin Belousov pmp->pm_mountp->mnt_stat.f_mntonname,
156595ed4d7SKonstantin Belousov (*depp)->de_diroffset, diroffset);
157595ed4d7SKonstantin Belousov goto badoff;
158595ed4d7SKonstantin Belousov }
159952a6212SJordan K. Hubbard return (0);
160595ed4d7SKonstantin Belousov badoff:
161595ed4d7SKonstantin Belousov vgone(nvp);
162595ed4d7SKonstantin Belousov vput(nvp);
16341e85eeaSKonstantin Belousov msdosfs_integrity_error(pmp);
164595ed4d7SKonstantin Belousov return (EBADF);
16527a0bc89SDoug Rabson }
1668b36e813SKonstantin Belousov ldep = malloc(sizeof(struct denode), M_MSDOSFSNODE, M_WAITOK | M_ZERO);
16727a0bc89SDoug Rabson
16827a0bc89SDoug Rabson /*
16927a0bc89SDoug Rabson * Directory entry was not in cache, have to create a vnode and
17027a0bc89SDoug Rabson * copy it from the passed disk buffer.
17127a0bc89SDoug Rabson */
17227a0bc89SDoug Rabson /* getnewvnode() does a VREF() on the vnode */
173aec0fb7bSPoul-Henning Kamp error = getnewvnode("msdosfs", mntp, &msdosfs_vnodeops, &nvp);
174c3c6d51eSPoul-Henning Kamp if (error) {
1752f9bae59SDavid Greenman *depp = NULL;
1761ede983cSDag-Erling Smørgrav free(ldep, M_MSDOSFSNODE);
17727a0bc89SDoug Rabson return error;
17827a0bc89SDoug Rabson }
17927a0bc89SDoug Rabson nvp->v_data = ldep;
18027a0bc89SDoug Rabson ldep->de_vnode = nvp;
18127a0bc89SDoug Rabson ldep->de_flag = 0;
18227a0bc89SDoug Rabson ldep->de_dirclust = dirclust;
18327a0bc89SDoug Rabson ldep->de_diroffset = diroffset;
184f4b423aeSPoul-Henning Kamp ldep->de_inode = inode;
1852bfd8992SKonstantin Belousov cluster_init_vn(&ldep->de_clusterw);
186303d3ae7SKonstantin Belousov lockmgr(nvp->v_vnlock, LK_EXCLUSIVE | LK_NOWITNESS, NULL);
18795d42526SKonstantin Belousov VN_LOCK_AREC(nvp); /* for doscheckpath */
1889287dbaaSEd Maste fc_purge(ldep, 0); /* init the FAT cache for this denode */
18961b9d89fSTor Egge error = insmntque(nvp, mntp);
19061b9d89fSTor Egge if (error != 0) {
1911ede983cSDag-Erling Smørgrav free(ldep, M_MSDOSFSNODE);
19261b9d89fSTor Egge *depp = NULL;
19361b9d89fSTor Egge return (error);
19461b9d89fSTor Egge }
195ae7e8a02SKonstantin Belousov error = vfs_hash_insert(nvp, inode, lkflags, curthread, &xvp,
196f4b423aeSPoul-Henning Kamp de_vncmpf, &inode);
197*445d3d22SStefan Eßer #ifdef MSDOSFS_DEBUG
198*445d3d22SStefan Eßer printf("vfs_hash_insert(inode %lu) error %d\n", inode, error);
199*445d3d22SStefan Eßer #endif
200a30fc63bSPoul-Henning Kamp if (error) {
201a30fc63bSPoul-Henning Kamp *depp = NULL;
202a30fc63bSPoul-Henning Kamp return (error);
203a30fc63bSPoul-Henning Kamp }
204a30fc63bSPoul-Henning Kamp if (xvp != NULL) {
2050bdbd627SKonstantin Belousov *depp = xvp->v_data;
2060bdbd627SKonstantin Belousov return (0);
207a30fc63bSPoul-Henning Kamp }
20827a0bc89SDoug Rabson
209952a6212SJordan K. Hubbard ldep->de_pmp = pmp;
210952a6212SJordan K. Hubbard ldep->de_refcnt = 1;
21127a0bc89SDoug Rabson /*
21227a0bc89SDoug Rabson * Copy the directory entry into the denode area of the vnode.
21327a0bc89SDoug Rabson */
214d51b0786SKonstantin Belousov if ((dirclust == MSDOSFSROOT ||
215d51b0786SKonstantin Belousov (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)) &&
216d51b0786SKonstantin Belousov diroffset == MSDOSFSROOT_OFS) {
21727a0bc89SDoug Rabson /*
21827a0bc89SDoug Rabson * Directory entry for the root directory. There isn't one,
21927a0bc89SDoug Rabson * so we manufacture one. We should probably rummage
22027a0bc89SDoug Rabson * through the root directory and find a label entry (if it
22127a0bc89SDoug Rabson * exists), and then use the time and date from that entry
22227a0bc89SDoug Rabson * as the time and date for the root denode.
22327a0bc89SDoug Rabson */
224e6e370a7SJeff Roberson nvp->v_vflag |= VV_ROOT; /* should be further down XXX */
225952a6212SJordan K. Hubbard
22627a0bc89SDoug Rabson ldep->de_Attributes = ATTR_DIRECTORY;
227bad3d41dSDmitrij Tejblum ldep->de_LowerCase = 0;
228952a6212SJordan K. Hubbard if (FAT32(pmp))
229952a6212SJordan K. Hubbard ldep->de_StartCluster = pmp->pm_rootdirblk;
230952a6212SJordan K. Hubbard /* de_FileSize will be filled in further down */
231952a6212SJordan K. Hubbard else {
23227a0bc89SDoug Rabson ldep->de_StartCluster = MSDOSFSROOT;
23301f6cfbaSYoshihiro Takahashi ldep->de_FileSize = pmp->pm_rootdirsize * DEV_BSIZE;
234952a6212SJordan K. Hubbard }
23527a0bc89SDoug Rabson /*
2363c960d93SPoul-Henning Kamp * fill in time and date so that fattime2timespec() doesn't
23727a0bc89SDoug Rabson * spit up when called from msdosfs_getattr() with root
23827a0bc89SDoug Rabson * denode
23927a0bc89SDoug Rabson */
240952a6212SJordan K. Hubbard ldep->de_CHun = 0;
241952a6212SJordan K. Hubbard ldep->de_CTime = 0x0000; /* 00:00:00 */
242952a6212SJordan K. Hubbard ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
24327a0bc89SDoug Rabson | (1 << DD_DAY_SHIFT);
24427a0bc89SDoug Rabson /* Jan 1, 1980 */
245952a6212SJordan K. Hubbard ldep->de_ADate = ldep->de_CDate;
246952a6212SJordan K. Hubbard ldep->de_MTime = ldep->de_CTime;
247952a6212SJordan K. Hubbard ldep->de_MDate = ldep->de_CDate;
24827a0bc89SDoug Rabson /* leave the other fields as garbage */
24927a0bc89SDoug Rabson } else {
250952a6212SJordan K. Hubbard error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
2515097a20dSBruce Evans if (error) {
2525097a20dSBruce Evans /*
2535097a20dSBruce Evans * The denode does not contain anything useful, so
2545097a20dSBruce Evans * it would be wrong to leave it on its hash chain.
2555097a20dSBruce Evans * Arrange for vput() to just forget about it.
2565097a20dSBruce Evans */
2575097a20dSBruce Evans ldep->de_Name[0] = SLOT_DELETED;
258d2203b48SMateusz Guzik vgone(nvp);
2595097a20dSBruce Evans vput(nvp);
2605097a20dSBruce Evans *depp = NULL;
261952a6212SJordan K. Hubbard return (error);
2625097a20dSBruce Evans }
2630b53cc9fSRui Paulo (void)DE_INTERNALIZE(ldep, direntptr);
26427a0bc89SDoug Rabson brelse(bp);
26527a0bc89SDoug Rabson }
26627a0bc89SDoug Rabson
26727a0bc89SDoug Rabson /*
26827a0bc89SDoug Rabson * Fill in a few fields of the vnode and finish filling in the
26927a0bc89SDoug Rabson * denode. Then return the address of the found denode.
27027a0bc89SDoug Rabson */
27127a0bc89SDoug Rabson if (ldep->de_Attributes & ATTR_DIRECTORY) {
27227a0bc89SDoug Rabson /*
27327a0bc89SDoug Rabson * Since DOS directory entries that describe directories
27427a0bc89SDoug Rabson * have 0 in the filesize field, we take this opportunity
27527a0bc89SDoug Rabson * to find out the length of the directory and plug it into
27627a0bc89SDoug Rabson * the denode structure.
27727a0bc89SDoug Rabson */
27827a0bc89SDoug Rabson u_long size;
27927a0bc89SDoug Rabson
280b5cdbc6dSTom Rhodes /*
281e3117f85SBruce Evans * XXX it sometimes happens that the "." entry has cluster
282e3117f85SBruce Evans * number 0 when it shouldn't. Use the actual cluster number
283b5cdbc6dSTom Rhodes * instead of what is written in directory entry.
284b5cdbc6dSTom Rhodes */
285e3117f85SBruce Evans if (diroffset == 0 && ldep->de_StartCluster != dirclust) {
28654cf9198SKonstantin Belousov #ifdef MSDOSFS_DEBUG
287e3117f85SBruce Evans printf("deget(): \".\" entry at clust %lu != %lu\n",
288b5cdbc6dSTom Rhodes dirclust, ldep->de_StartCluster);
28954cf9198SKonstantin Belousov #endif
290b5cdbc6dSTom Rhodes ldep->de_StartCluster = dirclust;
291b5cdbc6dSTom Rhodes }
292b5cdbc6dSTom Rhodes
29327a0bc89SDoug Rabson nvp->v_type = VDIR;
294952a6212SJordan K. Hubbard if (ldep->de_StartCluster != MSDOSFSROOT) {
295952a6212SJordan K. Hubbard error = pcbmap(ldep, 0xffff, 0, &size, 0);
29627a0bc89SDoug Rabson if (error == E2BIG) {
297952a6212SJordan K. Hubbard ldep->de_FileSize = de_cn2off(pmp, size);
29827a0bc89SDoug Rabson error = 0;
29954cf9198SKonstantin Belousov } else {
30054cf9198SKonstantin Belousov #ifdef MSDOSFS_DEBUG
30127a0bc89SDoug Rabson printf("deget(): pcbmap returned %d\n", error);
30254cf9198SKonstantin Belousov #endif
30354cf9198SKonstantin Belousov }
30427a0bc89SDoug Rabson }
30527a0bc89SDoug Rabson } else
30627a0bc89SDoug Rabson nvp->v_type = VREG;
307829f0bcbSMateusz Guzik vn_set_state(nvp, VSTATE_CONSTRUCTED);
3081affa3adSPoul-Henning Kamp ldep->de_modrev = init_va_filerev();
30927a0bc89SDoug Rabson *depp = ldep;
310952a6212SJordan K. Hubbard return (0);
31127a0bc89SDoug Rabson }
31227a0bc89SDoug Rabson
31327a0bc89SDoug Rabson int
deupdat(struct denode * dep,int waitfor)31410c9700fSEd Maste deupdat(struct denode *dep, int waitfor)
31527a0bc89SDoug Rabson {
316293e4eb6SKonstantin Belousov struct direntry dir;
317293e4eb6SKonstantin Belousov struct timespec ts;
31827a0bc89SDoug Rabson struct buf *bp;
31927a0bc89SDoug Rabson struct direntry *dirp;
320293e4eb6SKonstantin Belousov int error;
32127a0bc89SDoug Rabson
322293e4eb6SKonstantin Belousov if (DETOV(dep)->v_mount->mnt_flag & MNT_RDONLY) {
323293e4eb6SKonstantin Belousov dep->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS |
324293e4eb6SKonstantin Belousov DE_MODIFIED);
325952a6212SJordan K. Hubbard return (0);
326293e4eb6SKonstantin Belousov }
327b732ceb6SPedro F. Giffuni vfs_timestamp(&ts);
328952a6212SJordan K. Hubbard DETIMES(dep, &ts, &ts, &ts);
329293e4eb6SKonstantin Belousov if ((dep->de_flag & DE_MODIFIED) == 0 && waitfor == 0)
330952a6212SJordan K. Hubbard return (0);
331952a6212SJordan K. Hubbard dep->de_flag &= ~DE_MODIFIED;
3327da1a731SKenneth D. Merry if (DETOV(dep)->v_vflag & VV_ROOT)
3337da1a731SKenneth D. Merry return (EINVAL);
334952a6212SJordan K. Hubbard if (dep->de_refcnt <= 0)
335952a6212SJordan K. Hubbard return (0);
336c3c6d51eSPoul-Henning Kamp error = readde(dep, &bp, &dirp);
337c3c6d51eSPoul-Henning Kamp if (error)
338952a6212SJordan K. Hubbard return (error);
339293e4eb6SKonstantin Belousov DE_EXTERNALIZE(&dir, dep);
340293e4eb6SKonstantin Belousov if (bcmp(dirp, &dir, sizeof(dir)) == 0) {
341293e4eb6SKonstantin Belousov if (waitfor == 0 || (bp->b_flags & B_DELWRI) == 0) {
342293e4eb6SKonstantin Belousov brelse(bp);
343952a6212SJordan K. Hubbard return (0);
344952a6212SJordan K. Hubbard }
345293e4eb6SKonstantin Belousov } else
346293e4eb6SKonstantin Belousov *dirp = dir;
347293e4eb6SKonstantin Belousov if ((DETOV(dep)->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0)
348293e4eb6SKonstantin Belousov bp->b_flags |= B_CLUSTEROK;
349293e4eb6SKonstantin Belousov if (waitfor)
350293e4eb6SKonstantin Belousov error = bwrite(bp);
351293e4eb6SKonstantin Belousov else if (vm_page_count_severe() || buf_dirty_count_severe())
352293e4eb6SKonstantin Belousov bawrite(bp);
353293e4eb6SKonstantin Belousov else
354293e4eb6SKonstantin Belousov bdwrite(bp);
355293e4eb6SKonstantin Belousov return (error);
35627a0bc89SDoug Rabson }
35727a0bc89SDoug Rabson
35827a0bc89SDoug Rabson /*
35927a0bc89SDoug Rabson * Truncate the file described by dep to the length specified by length.
36027a0bc89SDoug Rabson */
36127a0bc89SDoug Rabson int
detrunc(struct denode * dep,u_long length,int flags,struct ucred * cred)36210c9700fSEd Maste detrunc(struct denode *dep, u_long length, int flags, struct ucred *cred)
36327a0bc89SDoug Rabson {
36427a0bc89SDoug Rabson int error;
36527a0bc89SDoug Rabson int allerror;
36627a0bc89SDoug Rabson u_long eofentry;
36727a0bc89SDoug Rabson u_long chaintofree;
36827a0bc89SDoug Rabson daddr_t bn;
36927a0bc89SDoug Rabson int boff;
37027a0bc89SDoug Rabson int isadir = dep->de_Attributes & ATTR_DIRECTORY;
37127a0bc89SDoug Rabson struct buf *bp;
37227a0bc89SDoug Rabson struct msdosfsmount *pmp = dep->de_pmp;
37327a0bc89SDoug Rabson
37427a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
375952a6212SJordan K. Hubbard printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags);
37627a0bc89SDoug Rabson #endif
37727a0bc89SDoug Rabson
37827a0bc89SDoug Rabson /*
37927a0bc89SDoug Rabson * Disallow attempts to truncate the root directory since it is of
38027a0bc89SDoug Rabson * fixed size. That's just the way dos filesystems are. We use
38127a0bc89SDoug Rabson * the VROOT bit in the vnode because checking for the directory
38227a0bc89SDoug Rabson * bit and a startcluster of 0 in the denode is not adequate to
38327a0bc89SDoug Rabson * recognize the root directory at this point in a file or
38427a0bc89SDoug Rabson * directory's life.
38527a0bc89SDoug Rabson */
386e6e370a7SJeff Roberson if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) {
38754cf9198SKonstantin Belousov #ifdef MSDOSFS_DEBUG
388952a6212SJordan K. Hubbard printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
38927a0bc89SDoug Rabson dep->de_dirclust, dep->de_diroffset);
39054cf9198SKonstantin Belousov #endif
391952a6212SJordan K. Hubbard return (EINVAL);
39227a0bc89SDoug Rabson }
39327a0bc89SDoug Rabson
394e59180eaSKonstantin Belousov if (dep->de_FileSize < length)
395d51b0786SKonstantin Belousov return (deextend(dep, length, cred));
39627a0bc89SDoug Rabson
39727a0bc89SDoug Rabson /*
39827a0bc89SDoug Rabson * If the desired length is 0 then remember the starting cluster of
39927a0bc89SDoug Rabson * the file and set the StartCluster field in the directory entry
40027a0bc89SDoug Rabson * to 0. If the desired length is not zero, then get the number of
40127a0bc89SDoug Rabson * the last cluster in the shortened file. Then get the number of
40227a0bc89SDoug Rabson * the first cluster in the part of the file that is to be freed.
40327a0bc89SDoug Rabson * Then set the next cluster pointer in the last cluster of the
40427a0bc89SDoug Rabson * file to CLUST_EOFE.
40527a0bc89SDoug Rabson */
40627a0bc89SDoug Rabson if (length == 0) {
40727a0bc89SDoug Rabson chaintofree = dep->de_StartCluster;
40827a0bc89SDoug Rabson dep->de_StartCluster = 0;
40927a0bc89SDoug Rabson eofentry = ~0;
41027a0bc89SDoug Rabson } else {
411952a6212SJordan K. Hubbard error = pcbmap(dep, de_clcount(pmp, length) - 1, 0,
412952a6212SJordan K. Hubbard &eofentry, 0);
413c3c6d51eSPoul-Henning Kamp if (error) {
41427a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
41527a0bc89SDoug Rabson printf("detrunc(): pcbmap fails %d\n", error);
41627a0bc89SDoug Rabson #endif
417952a6212SJordan K. Hubbard return (error);
41827a0bc89SDoug Rabson }
41927a0bc89SDoug Rabson }
42027a0bc89SDoug Rabson
421952a6212SJordan K. Hubbard fc_purge(dep, de_clcount(pmp, length));
42227a0bc89SDoug Rabson
42327a0bc89SDoug Rabson /*
42427a0bc89SDoug Rabson * If the new length is not a multiple of the cluster size then we
42527a0bc89SDoug Rabson * must zero the tail end of the new last cluster in case it
42627a0bc89SDoug Rabson * becomes part of the file again because of a seek.
42727a0bc89SDoug Rabson */
42827a0bc89SDoug Rabson if ((boff = length & pmp->pm_crbomask) != 0) {
42927a0bc89SDoug Rabson if (isadir) {
43027a0bc89SDoug Rabson bn = cntobn(pmp, eofentry);
43127a0bc89SDoug Rabson error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
43227a0bc89SDoug Rabson NOCRED, &bp);
433c973ee9eSKonstantin Belousov } else {
434c973ee9eSKonstantin Belousov error = bread(DETOV(dep), de_cluster(pmp, length),
435c973ee9eSKonstantin Belousov pmp->pm_bpcluster, cred, &bp);
436c973ee9eSKonstantin Belousov }
43727a0bc89SDoug Rabson if (error) {
43827a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
43927a0bc89SDoug Rabson printf("detrunc(): bread fails %d\n", error);
44027a0bc89SDoug Rabson #endif
441952a6212SJordan K. Hubbard return (error);
44227a0bc89SDoug Rabson }
4436a1c2e1fSEd Maste memset(bp->b_data + boff, 0, pmp->pm_bpcluster - boff);
444c973ee9eSKonstantin Belousov if ((flags & IO_SYNC) != 0)
44527a0bc89SDoug Rabson bwrite(bp);
44627a0bc89SDoug Rabson else
44727a0bc89SDoug Rabson bdwrite(bp);
44827a0bc89SDoug Rabson }
44927a0bc89SDoug Rabson
45027a0bc89SDoug Rabson /*
45127a0bc89SDoug Rabson * Write out the updated directory entry. Even if the update fails
45227a0bc89SDoug Rabson * we free the trailing clusters.
45327a0bc89SDoug Rabson */
45427a0bc89SDoug Rabson dep->de_FileSize = length;
455952a6212SJordan K. Hubbard if (!isadir)
456952a6212SJordan K. Hubbard dep->de_flag |= DE_UPDATE | DE_MODIFIED;
45765417f5eSAlan Somers allerror = vtruncbuf(DETOV(dep), length, pmp->pm_bpcluster);
45844fdad99SPeter Wemm #ifdef MSDOSFS_DEBUG
45944fdad99SPeter Wemm if (allerror)
46044fdad99SPeter Wemm printf("detrunc(): vtruncbuf error %d\n", allerror);
46144fdad99SPeter Wemm #endif
4622aacee77SKonstantin Belousov error = deupdat(dep, !DOINGASYNC((DETOV(dep))));
463e3117f85SBruce Evans if (error != 0 && allerror == 0)
46444fdad99SPeter Wemm allerror = error;
46527a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
466952a6212SJordan K. Hubbard printf("detrunc(): allerror %d, eofentry %lu\n",
46727a0bc89SDoug Rabson allerror, eofentry);
46827a0bc89SDoug Rabson #endif
46927a0bc89SDoug Rabson
47027a0bc89SDoug Rabson /*
47127a0bc89SDoug Rabson * If we need to break the cluster chain for the file then do it
47227a0bc89SDoug Rabson * now.
47327a0bc89SDoug Rabson */
47427a0bc89SDoug Rabson if (eofentry != ~0) {
47527a0bc89SDoug Rabson error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
47627a0bc89SDoug Rabson &chaintofree, CLUST_EOFE);
47727a0bc89SDoug Rabson if (error) {
47827a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
47927a0bc89SDoug Rabson printf("detrunc(): fatentry errors %d\n", error);
48027a0bc89SDoug Rabson #endif
481952a6212SJordan K. Hubbard return (error);
48227a0bc89SDoug Rabson }
483952a6212SJordan K. Hubbard fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1),
48427a0bc89SDoug Rabson eofentry);
48527a0bc89SDoug Rabson }
48627a0bc89SDoug Rabson
48727a0bc89SDoug Rabson /*
48827a0bc89SDoug Rabson * Now free the clusters removed from the file because of the
48927a0bc89SDoug Rabson * truncation.
49027a0bc89SDoug Rabson */
491952a6212SJordan K. Hubbard if (chaintofree != 0 && !MSDOSFSEOF(pmp, chaintofree))
49227a0bc89SDoug Rabson freeclusterchain(pmp, chaintofree);
49327a0bc89SDoug Rabson
494952a6212SJordan K. Hubbard return (allerror);
49527a0bc89SDoug Rabson }
49627a0bc89SDoug Rabson
49727a0bc89SDoug Rabson /*
49827a0bc89SDoug Rabson * Extend the file described by dep to length specified by length.
49927a0bc89SDoug Rabson */
50027a0bc89SDoug Rabson int
deextend(struct denode * dep,u_long length,struct ucred * cred)50110c9700fSEd Maste deextend(struct denode *dep, u_long length, struct ucred *cred)
50227a0bc89SDoug Rabson {
50327a0bc89SDoug Rabson struct msdosfsmount *pmp = dep->de_pmp;
50467dc1e7bSKonstantin Belousov struct vnode *vp = DETOV(dep);
5050152d453SKonstantin Belousov struct buf *bp;
5067e4c6b21SKonstantin Belousov off_t eof_clusteroff;
50727a0bc89SDoug Rabson u_long count;
50827a0bc89SDoug Rabson int error;
50927a0bc89SDoug Rabson
51027a0bc89SDoug Rabson /*
51127a0bc89SDoug Rabson * The root of a DOS filesystem cannot be extended.
51227a0bc89SDoug Rabson */
51367dc1e7bSKonstantin Belousov if ((vp->v_vflag & VV_ROOT) != 0 && !FAT32(pmp))
514952a6212SJordan K. Hubbard return (EINVAL);
51527a0bc89SDoug Rabson
51627a0bc89SDoug Rabson /*
517952a6212SJordan K. Hubbard * Directories cannot be extended.
51827a0bc89SDoug Rabson */
519952a6212SJordan K. Hubbard if (dep->de_Attributes & ATTR_DIRECTORY)
520952a6212SJordan K. Hubbard return (EISDIR);
52127a0bc89SDoug Rabson
52227a0bc89SDoug Rabson if (length <= dep->de_FileSize)
52327a0bc89SDoug Rabson panic("deextend: file too large");
52427a0bc89SDoug Rabson
52527a0bc89SDoug Rabson /*
52627a0bc89SDoug Rabson * Compute the number of clusters to allocate.
52727a0bc89SDoug Rabson */
52827a0bc89SDoug Rabson count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize);
52927a0bc89SDoug Rabson if (count > 0) {
53027a0bc89SDoug Rabson if (count > pmp->pm_freeclustercount)
531952a6212SJordan K. Hubbard return (ENOSPC);
532c3c6d51eSPoul-Henning Kamp error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
5330152d453SKonstantin Belousov if (error != 0)
5340152d453SKonstantin Belousov goto rewind;
53527a0bc89SDoug Rabson }
5360152d453SKonstantin Belousov
5370152d453SKonstantin Belousov /*
5380152d453SKonstantin Belousov * For the case of cluster size larger than the page size, we
5390152d453SKonstantin Belousov * need to ensure that the possibly dirty partial buffer at
5400152d453SKonstantin Belousov * the old end of file is not filled with invalid pages by
5410152d453SKonstantin Belousov * extension. Otherwise it has a contradictory state of
5420152d453SKonstantin Belousov * B_CACHE | B_DELWRI but with invalid pages, and cannot be
5430152d453SKonstantin Belousov * neither written out nor validated.
5440152d453SKonstantin Belousov *
5457e4c6b21SKonstantin Belousov * Fix it by proactively clearing extended pages. Need to do
5467e4c6b21SKonstantin Belousov * both vfs_bio_clrbuf() to mark pages valid, and to zero
5477e4c6b21SKonstantin Belousov * actual buffer content which might exist in the tail of the
5487e4c6b21SKonstantin Belousov * already valid cluster.
5490152d453SKonstantin Belousov */
5500152d453SKonstantin Belousov error = bread(vp, de_cluster(pmp, dep->de_FileSize), pmp->pm_bpcluster,
5510152d453SKonstantin Belousov NOCRED, &bp);
5520152d453SKonstantin Belousov if (error != 0)
5530152d453SKonstantin Belousov goto rewind;
5540152d453SKonstantin Belousov vfs_bio_clrbuf(bp);
5557e4c6b21SKonstantin Belousov eof_clusteroff = de_cn2off(pmp, de_cluster(pmp, dep->de_FileSize));
5567e4c6b21SKonstantin Belousov vfs_bio_bzero_buf(bp, dep->de_FileSize - eof_clusteroff,
5577e4c6b21SKonstantin Belousov pmp->pm_bpcluster - dep->de_FileSize + eof_clusteroff);
5580152d453SKonstantin Belousov if (!DOINGASYNC(vp))
5590152d453SKonstantin Belousov (void)bwrite(bp);
5600152d453SKonstantin Belousov else if (vm_page_count_severe() || buf_dirty_count_severe())
5610152d453SKonstantin Belousov bawrite(bp);
5620152d453SKonstantin Belousov else
5630152d453SKonstantin Belousov bdwrite(bp);
5640152d453SKonstantin Belousov
56567dc1e7bSKonstantin Belousov vnode_pager_setsize(vp, length);
56627a0bc89SDoug Rabson dep->de_FileSize = length;
567952a6212SJordan K. Hubbard dep->de_flag |= DE_UPDATE | DE_MODIFIED;
56867dc1e7bSKonstantin Belousov return (deupdat(dep, !DOINGASYNC(vp)));
5690152d453SKonstantin Belousov
5700152d453SKonstantin Belousov rewind:
5710152d453SKonstantin Belousov /* truncate the added clusters away again */
5720152d453SKonstantin Belousov (void)detrunc(dep, dep->de_FileSize, 0, cred);
5730152d453SKonstantin Belousov return (error);
57427a0bc89SDoug Rabson }
57527a0bc89SDoug Rabson
57627a0bc89SDoug Rabson /*
57727a0bc89SDoug Rabson * Move a denode to its correct hash queue after the file it represents has
57827a0bc89SDoug Rabson * been moved to a new directory.
57927a0bc89SDoug Rabson */
580952a6212SJordan K. Hubbard void
reinsert(struct denode * dep)58110c9700fSEd Maste reinsert(struct denode *dep)
58227a0bc89SDoug Rabson {
583a30fc63bSPoul-Henning Kamp struct vnode *vp;
584a30fc63bSPoul-Henning Kamp
58527a0bc89SDoug Rabson /*
58627a0bc89SDoug Rabson * Fix up the denode cache. If the denode is for a directory,
58727a0bc89SDoug Rabson * there is nothing to do since the hash is based on the starting
58827a0bc89SDoug Rabson * cluster of the directory file and that hasn't changed. If for a
58927a0bc89SDoug Rabson * file the hash is based on the location of the directory entry,
59027a0bc89SDoug Rabson * so we must remove it from the cache and re-enter it with the
59127a0bc89SDoug Rabson * hash based on the new location of the directory entry.
59227a0bc89SDoug Rabson */
593f4b423aeSPoul-Henning Kamp #if 0
594952a6212SJordan K. Hubbard if (dep->de_Attributes & ATTR_DIRECTORY)
595952a6212SJordan K. Hubbard return;
596f4b423aeSPoul-Henning Kamp #endif
597a30fc63bSPoul-Henning Kamp vp = DETOV(dep);
598*445d3d22SStefan Eßer dep->de_inode = DETOI(dep->de_pmp, dep->de_dirclust, dep->de_diroffset);
599*445d3d22SStefan Eßer #ifdef MSDOSFS_DEBUG
600*445d3d22SStefan Eßer printf("vfs_hash_rehash(inode %lu, refcnt %lu, vp %p)\n",
601*445d3d22SStefan Eßer dep->de_inode, dep->de_refcnt, vp);
602*445d3d22SStefan Eßer #endif
603f4b423aeSPoul-Henning Kamp vfs_hash_rehash(vp, dep->de_inode);
60427a0bc89SDoug Rabson }
60527a0bc89SDoug Rabson
60627a0bc89SDoug Rabson int
msdosfs_reclaim(struct vop_reclaim_args * ap)60710c9700fSEd Maste msdosfs_reclaim(struct vop_reclaim_args *ap)
60827a0bc89SDoug Rabson {
60927a0bc89SDoug Rabson struct vnode *vp = ap->a_vp;
61027a0bc89SDoug Rabson struct denode *dep = VTODE(vp);
61127a0bc89SDoug Rabson
61227a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
613f0707215SPoul-Henning Kamp printf("msdosfs_reclaim(): dep %p, file %s, refcnt %ld\n",
61427a0bc89SDoug Rabson dep, dep->de_Name, dep->de_refcnt);
61527a0bc89SDoug Rabson #endif
61627a0bc89SDoug Rabson
61727a0bc89SDoug Rabson /*
618952a6212SJordan K. Hubbard * Remove the denode from its hash chain.
61927a0bc89SDoug Rabson */
620*445d3d22SStefan Eßer #ifdef MSDOSFS_DEBUG
621*445d3d22SStefan Eßer printf("vfs_hash_remove(inode %lu, refcnt %lu, vp %p)\n",
622*445d3d22SStefan Eßer dep->de_inode, dep->de_refcnt, vp);
623*445d3d22SStefan Eßer #endif
624a30fc63bSPoul-Henning Kamp vfs_hash_remove(vp);
62527a0bc89SDoug Rabson /*
626952a6212SJordan K. Hubbard * Purge old data structures associated with the denode.
62727a0bc89SDoug Rabson */
628952a6212SJordan K. Hubbard #if 0 /* XXX */
62927a0bc89SDoug Rabson dep->de_flag = 0;
630952a6212SJordan K. Hubbard #endif
6311ede983cSDag-Erling Smørgrav free(dep, M_MSDOSFSNODE);
63227a0bc89SDoug Rabson vp->v_data = NULL;
63327a0bc89SDoug Rabson
634952a6212SJordan K. Hubbard return (0);
63527a0bc89SDoug Rabson }
63627a0bc89SDoug Rabson
63727a0bc89SDoug Rabson int
msdosfs_inactive(struct vop_inactive_args * ap)63810c9700fSEd Maste msdosfs_inactive(struct vop_inactive_args *ap)
63927a0bc89SDoug Rabson {
64027a0bc89SDoug Rabson struct vnode *vp = ap->a_vp;
64127a0bc89SDoug Rabson struct denode *dep = VTODE(vp);
64227a0bc89SDoug Rabson int error = 0;
64327a0bc89SDoug Rabson
64427a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
645f0707215SPoul-Henning Kamp printf("msdosfs_inactive(): dep %p, de_Name[0] %x\n", dep, dep->de_Name[0]);
64627a0bc89SDoug Rabson #endif
64727a0bc89SDoug Rabson
64827a0bc89SDoug Rabson /*
649952a6212SJordan K. Hubbard * Ignore denodes related to stale file handles.
65027a0bc89SDoug Rabson */
651740a7201SKonstantin Belousov if (dep->de_Name[0] == SLOT_DELETED || dep->de_Name[0] == SLOT_EMPTY)
652af3f60d5SBruce Evans goto out;
65327a0bc89SDoug Rabson
65427a0bc89SDoug Rabson /*
65527a0bc89SDoug Rabson * If the file has been deleted and it is on a read/write
65627a0bc89SDoug Rabson * filesystem, then truncate the file, and mark the directory slot
65727a0bc89SDoug Rabson * as empty. (This may not be necessary for the dos filesystem.)
65827a0bc89SDoug Rabson */
65927a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
660027bebe8SEd Maste printf("msdosfs_inactive(): dep %p, refcnt %ld, mntflag %llx, MNT_RDONLY %llx\n",
661027bebe8SEd Maste dep, dep->de_refcnt, (unsigned long long)vp->v_mount->mnt_flag,
662027bebe8SEd Maste (unsigned long long)MNT_RDONLY);
66327a0bc89SDoug Rabson #endif
66427a0bc89SDoug Rabson if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
665c52fd858SEdward Tomasz Napierala error = detrunc(dep, (u_long) 0, 0, NOCRED);
66627a0bc89SDoug Rabson dep->de_flag |= DE_UPDATE;
66727a0bc89SDoug Rabson dep->de_Name[0] = SLOT_DELETED;
66827a0bc89SDoug Rabson }
669952a6212SJordan K. Hubbard deupdat(dep, 0);
670952a6212SJordan K. Hubbard
671af3f60d5SBruce Evans out:
67227a0bc89SDoug Rabson /*
673952a6212SJordan K. Hubbard * If we are done with the denode, reclaim it
674952a6212SJordan K. Hubbard * so that it can be reused immediately.
67527a0bc89SDoug Rabson */
67627a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
6774d93c0beSJeff Roberson printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n",
6784d93c0beSJeff Roberson vrefcnt(vp), dep->de_Name[0]);
67927a0bc89SDoug Rabson #endif
680740a7201SKonstantin Belousov if (dep->de_Name[0] == SLOT_DELETED || dep->de_Name[0] == SLOT_EMPTY)
681af6e6b87SEdward Tomasz Napierala vrecycle(vp);
682952a6212SJordan K. Hubbard return (error);
68327a0bc89SDoug Rabson }
684