1*4874b543Sderaadt /* $OpenBSD: msdosfs_denode.c,v 1.7 2021/10/06 00:40:41 deraadt Exp $ */
26163fc9cSnatano /* $NetBSD: msdosfs_denode.c,v 1.7 2015/03/29 05:52:59 agc Exp $ */
36163fc9cSnatano
46163fc9cSnatano /*-
56163fc9cSnatano * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
66163fc9cSnatano * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
76163fc9cSnatano * All rights reserved.
86163fc9cSnatano * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
96163fc9cSnatano *
106163fc9cSnatano * Redistribution and use in source and binary forms, with or without
116163fc9cSnatano * modification, are permitted provided that the following conditions
126163fc9cSnatano * are met:
136163fc9cSnatano * 1. Redistributions of source code must retain the above copyright
146163fc9cSnatano * notice, this list of conditions and the following disclaimer.
156163fc9cSnatano * 2. Redistributions in binary form must reproduce the above copyright
166163fc9cSnatano * notice, this list of conditions and the following disclaimer in the
176163fc9cSnatano * documentation and/or other materials provided with the distribution.
186163fc9cSnatano * 3. All advertising materials mentioning features or use of this software
196163fc9cSnatano * must display the following acknowledgement:
206163fc9cSnatano * This product includes software developed by TooLs GmbH.
216163fc9cSnatano * 4. The name of TooLs GmbH may not be used to endorse or promote products
226163fc9cSnatano * derived from this software without specific prior written permission.
236163fc9cSnatano *
246163fc9cSnatano * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
256163fc9cSnatano * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
266163fc9cSnatano * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
276163fc9cSnatano * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
286163fc9cSnatano * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
296163fc9cSnatano * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
306163fc9cSnatano * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
316163fc9cSnatano * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
326163fc9cSnatano * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
336163fc9cSnatano * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
346163fc9cSnatano */
356163fc9cSnatano /*
366163fc9cSnatano * Written by Paul Popelka (paulp@uts.amdahl.com)
376163fc9cSnatano *
386163fc9cSnatano * You can do anything you want with this software, just don't say you wrote
396163fc9cSnatano * it, and don't remove this notice.
406163fc9cSnatano *
416163fc9cSnatano * This software is provided "as is".
426163fc9cSnatano *
436163fc9cSnatano * The author supplies this software to be publicly redistributed on the
446163fc9cSnatano * understanding that the author is not responsible for the correct
456163fc9cSnatano * functioning of this software in any circumstances and is not liable for
466163fc9cSnatano * any damages caused by this software.
476163fc9cSnatano *
486163fc9cSnatano * October 1992
496163fc9cSnatano */
506163fc9cSnatano
516163fc9cSnatano
5272290da1Snatano #include "ffs/buf.h"
536163fc9cSnatano
54bb19b730Snatano #include <msdosfs/bpb.h>
5572290da1Snatano #include "msdos/msdosfsmount.h"
5672290da1Snatano #include "msdos/direntry.h"
5772290da1Snatano #include "msdos/denode.h"
5872290da1Snatano #include "msdos/fat.h"
596163fc9cSnatano
606163fc9cSnatano #include <util.h>
616163fc9cSnatano
626163fc9cSnatano #include "makefs.h"
636163fc9cSnatano
646163fc9cSnatano /*
656163fc9cSnatano * If deget() succeeds it returns with the gotten denode locked().
666163fc9cSnatano *
676163fc9cSnatano * pmp - address of msdosfsmount structure of the filesystem containing
686163fc9cSnatano * the denode of interest. The pm_dev field and the address of
696163fc9cSnatano * the msdosfsmount structure are used.
706163fc9cSnatano * dirclust - which cluster bp contains, if dirclust is 0 (root directory)
716163fc9cSnatano * diroffset is relative to the beginning of the root directory,
726163fc9cSnatano * otherwise it is cluster relative.
736163fc9cSnatano * diroffset - offset past begin of cluster of denode we want
746163fc9cSnatano * depp - returns the address of the gotten denode.
756163fc9cSnatano */
766163fc9cSnatano int
deget(struct msdosfsmount * pmp,u_long dirclust,u_long diroffset,struct denode ** depp)776163fc9cSnatano deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
786163fc9cSnatano struct denode **depp)
796163fc9cSnatano /* pmp: so we know the maj/min number */
806163fc9cSnatano /* dirclust: cluster this dir entry came from */
816163fc9cSnatano /* diroffset: index of entry within the cluster */
826163fc9cSnatano /* depp: returns the addr of the gotten denode */
836163fc9cSnatano {
846163fc9cSnatano int error;
856163fc9cSnatano struct direntry *direntptr;
866163fc9cSnatano struct denode *ldep;
874af638d7Stedu struct mkfsbuf *bp;
886163fc9cSnatano
896163fc9cSnatano #ifdef MSDOSFS_DEBUG
906163fc9cSnatano printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n",
916163fc9cSnatano pmp, dirclust, diroffset, depp);
926163fc9cSnatano #endif
936163fc9cSnatano
946163fc9cSnatano /*
956163fc9cSnatano * On FAT32 filesystems, root is a (more or less) normal
966163fc9cSnatano * directory
976163fc9cSnatano */
986163fc9cSnatano if (FAT32(pmp) && dirclust == MSDOSFSROOT)
996163fc9cSnatano dirclust = pmp->pm_rootdirblk;
1006163fc9cSnatano
1016163fc9cSnatano ldep = ecalloc(1, sizeof(*ldep));
1024af638d7Stedu ldep->de_mkfsvnode = NULL;
1036163fc9cSnatano ldep->de_flag = 0;
1046163fc9cSnatano ldep->de_devvp = 0;
1056163fc9cSnatano ldep->de_lockf = 0;
1066163fc9cSnatano ldep->de_dev = pmp->pm_dev;
1076163fc9cSnatano ldep->de_dirclust = dirclust;
1086163fc9cSnatano ldep->de_diroffset = diroffset;
1096163fc9cSnatano ldep->de_pmp = pmp;
1106163fc9cSnatano ldep->de_devvp = pmp->pm_devvp;
1116163fc9cSnatano ldep->de_refcnt = 1;
1126163fc9cSnatano fc_purge(ldep, 0);
1136163fc9cSnatano /*
1144af638d7Stedu * Copy the directory entry into the denode area of the mkfsvnode.
1156163fc9cSnatano */
1166163fc9cSnatano if ((dirclust == MSDOSFSROOT
1176163fc9cSnatano || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk))
1186163fc9cSnatano && diroffset == MSDOSFSROOT_OFS) {
1196163fc9cSnatano /*
1206163fc9cSnatano * Directory entry for the root directory. There isn't one,
1216163fc9cSnatano * so we manufacture one. We should probably rummage
1226163fc9cSnatano * through the root directory and find a label entry (if it
1236163fc9cSnatano * exists), and then use the time and date from that entry
1246163fc9cSnatano * as the time and date for the root denode.
1256163fc9cSnatano */
1264af638d7Stedu ldep->de_mkfsvnode = (struct mkfsvnode *)-1;
1276163fc9cSnatano
1286163fc9cSnatano ldep->de_Attributes = ATTR_DIRECTORY;
1296163fc9cSnatano if (FAT32(pmp))
1306163fc9cSnatano ldep->de_StartCluster = pmp->pm_rootdirblk;
1316163fc9cSnatano /* de_FileSize will be filled in further down */
1326163fc9cSnatano else {
1336163fc9cSnatano ldep->de_StartCluster = MSDOSFSROOT;
1346163fc9cSnatano ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec;
1356163fc9cSnatano }
1366163fc9cSnatano /*
1376163fc9cSnatano * fill in time and date so that dos2unixtime() doesn't
1386163fc9cSnatano * spit up when called from msdosfs_getattr() with root
1396163fc9cSnatano * denode
1406163fc9cSnatano */
1416163fc9cSnatano ldep->de_CHun = 0;
1426163fc9cSnatano ldep->de_CTime = 0x0000; /* 00:00:00 */
1436163fc9cSnatano ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
1446163fc9cSnatano | (1 << DD_DAY_SHIFT);
1456163fc9cSnatano /* Jan 1, 1980 */
1466163fc9cSnatano ldep->de_ADate = ldep->de_CDate;
1476163fc9cSnatano ldep->de_MTime = ldep->de_CTime;
1486163fc9cSnatano ldep->de_MDate = ldep->de_CDate;
1496163fc9cSnatano /* leave the other fields as garbage */
1506163fc9cSnatano } else {
1516163fc9cSnatano error = readep(pmp, dirclust, diroffset, &bp, &direntptr);
1526163fc9cSnatano if (error) {
1536163fc9cSnatano ldep->de_devvp = NULL;
1546163fc9cSnatano ldep->de_Name[0] = SLOT_DELETED;
1556163fc9cSnatano return (error);
1566163fc9cSnatano }
1576163fc9cSnatano DE_INTERNALIZE(ldep, direntptr);
1586163fc9cSnatano brelse(bp, 0);
1596163fc9cSnatano }
1606163fc9cSnatano
1616163fc9cSnatano /*
1624af638d7Stedu * Fill in a few fields of the mkfsvnode and finish filling in the
1636163fc9cSnatano * denode. Then return the address of the found denode.
1646163fc9cSnatano */
1656163fc9cSnatano if (ldep->de_Attributes & ATTR_DIRECTORY) {
1666163fc9cSnatano /*
1676163fc9cSnatano * Since DOS directory entries that describe directories
1686163fc9cSnatano * have 0 in the filesize field, we take this opportunity
1696163fc9cSnatano * to find out the length of the directory and plug it into
1706163fc9cSnatano * the denode structure.
1716163fc9cSnatano */
1726163fc9cSnatano u_long size;
1736163fc9cSnatano
1746163fc9cSnatano if (ldep->de_StartCluster != MSDOSFSROOT) {
1756163fc9cSnatano error = pcbmap(ldep, CLUST_END, 0, &size, 0);
1766163fc9cSnatano if (error == E2BIG) {
1776163fc9cSnatano ldep->de_FileSize = de_cn2off(pmp, size);
1786163fc9cSnatano error = 0;
1796163fc9cSnatano } else
1806163fc9cSnatano printf("deget(): pcbmap returned %d\n", error);
1816163fc9cSnatano }
1826163fc9cSnatano }
1836163fc9cSnatano *depp = ldep;
1846163fc9cSnatano return (0);
1856163fc9cSnatano }
1866163fc9cSnatano
1876163fc9cSnatano /*
1886163fc9cSnatano * Truncate the file described by dep to the length specified by length.
1896163fc9cSnatano */
1906163fc9cSnatano int
detrunc(struct denode * dep,u_long length,int flags)191bee0c138Stedu detrunc(struct denode *dep, u_long length, int flags)
1926163fc9cSnatano {
1936163fc9cSnatano int error;
1946163fc9cSnatano int allerror = 0;
1956163fc9cSnatano u_long eofentry;
1966163fc9cSnatano u_long chaintofree = 0;
1976163fc9cSnatano daddr_t bn, lastblock;
1986163fc9cSnatano int boff;
1996163fc9cSnatano int isadir = dep->de_Attributes & ATTR_DIRECTORY;
2004af638d7Stedu struct mkfsbuf *bp;
2016163fc9cSnatano struct msdosfsmount *pmp = dep->de_pmp;
2026163fc9cSnatano
2036163fc9cSnatano #ifdef MSDOSFS_DEBUG
2046163fc9cSnatano printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags);
2056163fc9cSnatano #endif
2066163fc9cSnatano
2076163fc9cSnatano /*
2086163fc9cSnatano * Disallow attempts to truncate the root directory since it is of
2096163fc9cSnatano * fixed size. That's just the way dos filesystems are. We use
2104af638d7Stedu * the VROOT bit in the mkfsvnode because checking for the directory
2116163fc9cSnatano * bit and a startcluster of 0 in the denode is not adequate to
2126163fc9cSnatano * recognize the root directory at this point in a file or
2136163fc9cSnatano * directory's life.
2146163fc9cSnatano */
2154af638d7Stedu if (dep->de_mkfsvnode != NULL && !FAT32(pmp)) {
2166163fc9cSnatano printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
2176163fc9cSnatano dep->de_dirclust, dep->de_diroffset);
2186163fc9cSnatano return (EINVAL);
2196163fc9cSnatano }
2206163fc9cSnatano
2216163fc9cSnatano if (dep->de_FileSize < length)
222bee0c138Stedu return (deextend(dep, length));
2236163fc9cSnatano lastblock = de_clcount(pmp, length) - 1;
2246163fc9cSnatano
2256163fc9cSnatano /*
2266163fc9cSnatano * If the desired length is 0 then remember the starting cluster of
2276163fc9cSnatano * the file and set the StartCluster field in the directory entry
2286163fc9cSnatano * to 0. If the desired length is not zero, then get the number of
2296163fc9cSnatano * the last cluster in the shortened file. Then get the number of
2306163fc9cSnatano * the first cluster in the part of the file that is to be freed.
2316163fc9cSnatano * Then set the next cluster pointer in the last cluster of the
2326163fc9cSnatano * file to CLUST_EOFE.
2336163fc9cSnatano */
2346163fc9cSnatano if (length == 0) {
2356163fc9cSnatano chaintofree = dep->de_StartCluster;
2366163fc9cSnatano dep->de_StartCluster = 0;
2376163fc9cSnatano eofentry = ~0;
2386163fc9cSnatano } else {
2396163fc9cSnatano error = pcbmap(dep, lastblock, 0, &eofentry, 0);
2406163fc9cSnatano if (error) {
2416163fc9cSnatano #ifdef MSDOSFS_DEBUG
2426163fc9cSnatano printf("detrunc(): pcbmap fails %d\n", error);
2436163fc9cSnatano #endif
2446163fc9cSnatano return (error);
2456163fc9cSnatano }
2466163fc9cSnatano }
2476163fc9cSnatano
2486163fc9cSnatano /*
2496163fc9cSnatano * If the new length is not a multiple of the cluster size then we
2506163fc9cSnatano * must zero the tail end of the new last cluster in case it
2516163fc9cSnatano * becomes part of the file again because of a seek.
2526163fc9cSnatano */
2536163fc9cSnatano if ((boff = length & pmp->pm_crbomask) != 0) {
2546163fc9cSnatano if (isadir) {
2556163fc9cSnatano bn = cntobn(pmp, eofentry);
2566163fc9cSnatano error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
2576163fc9cSnatano pmp->pm_bpcluster, B_MODIFY, &bp);
2586163fc9cSnatano if (error) {
2596163fc9cSnatano #ifdef MSDOSFS_DEBUG
2606163fc9cSnatano printf("detrunc(): bread fails %d\n", error);
2616163fc9cSnatano #endif
2626163fc9cSnatano return (error);
2636163fc9cSnatano }
2646163fc9cSnatano memset((char *)bp->b_data + boff, 0,
2656163fc9cSnatano pmp->pm_bpcluster - boff);
2666163fc9cSnatano if (flags & IO_SYNC)
2676163fc9cSnatano bwrite(bp);
2686163fc9cSnatano else
2696163fc9cSnatano bdwrite(bp);
2706163fc9cSnatano }
2716163fc9cSnatano }
2726163fc9cSnatano
2736163fc9cSnatano /*
2746163fc9cSnatano * Write out the updated directory entry. Even if the update fails
2756163fc9cSnatano * we free the trailing clusters.
2766163fc9cSnatano */
2776163fc9cSnatano dep->de_FileSize = length;
2786163fc9cSnatano if (!isadir)
2796163fc9cSnatano dep->de_flag |= DE_UPDATE|DE_MODIFIED;
2806163fc9cSnatano #ifdef MSDOSFS_DEBUG
2816163fc9cSnatano printf("detrunc(): allerror %d, eofentry %lu\n",
2826163fc9cSnatano allerror, eofentry);
2836163fc9cSnatano #endif
2846163fc9cSnatano
2856163fc9cSnatano /*
2866163fc9cSnatano * If we need to break the cluster chain for the file then do it
2876163fc9cSnatano * now.
2886163fc9cSnatano */
2896163fc9cSnatano if (eofentry != (u_long)~0) {
2906163fc9cSnatano error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
2916163fc9cSnatano &chaintofree, CLUST_EOFE);
2926163fc9cSnatano if (error) {
2936163fc9cSnatano #ifdef MSDOSFS_DEBUG
2946163fc9cSnatano printf("detrunc(): fatentry errors %d\n", error);
2956163fc9cSnatano #endif
2966163fc9cSnatano return (error);
2976163fc9cSnatano }
2986163fc9cSnatano }
2996163fc9cSnatano
3006163fc9cSnatano /*
3016163fc9cSnatano * Now free the clusters removed from the file because of the
3026163fc9cSnatano * truncation.
3036163fc9cSnatano */
3046163fc9cSnatano if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask))
3056163fc9cSnatano freeclusterchain(pmp, chaintofree);
3066163fc9cSnatano
3076163fc9cSnatano return (allerror);
3086163fc9cSnatano }
3096163fc9cSnatano
3106163fc9cSnatano /*
3116163fc9cSnatano * Extend the file described by dep to length specified by length.
3126163fc9cSnatano */
3136163fc9cSnatano int
deextend(struct denode * dep,u_long length)314bee0c138Stedu deextend(struct denode *dep, u_long length)
3156163fc9cSnatano {
3166163fc9cSnatano struct msdosfsmount *pmp = dep->de_pmp;
3176163fc9cSnatano u_long count;
3186163fc9cSnatano int error;
3196163fc9cSnatano
3206163fc9cSnatano /*
3216163fc9cSnatano * The root of a DOS filesystem cannot be extended.
3226163fc9cSnatano */
3234af638d7Stedu if (dep->de_mkfsvnode != NULL && !FAT32(pmp))
3246163fc9cSnatano return EINVAL;
3256163fc9cSnatano
3266163fc9cSnatano /*
3276163fc9cSnatano * Directories cannot be extended.
3286163fc9cSnatano */
3296163fc9cSnatano if (dep->de_Attributes & ATTR_DIRECTORY)
3306163fc9cSnatano return EISDIR;
3316163fc9cSnatano
3326163fc9cSnatano if (length <= dep->de_FileSize)
3336163fc9cSnatano return E2BIG;
3346163fc9cSnatano
3356163fc9cSnatano /*
3366163fc9cSnatano * Compute the number of clusters to allocate.
3376163fc9cSnatano */
3386163fc9cSnatano count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize);
3396163fc9cSnatano if (count > 0) {
3406163fc9cSnatano if (count > pmp->pm_freeclustercount)
3416163fc9cSnatano return (ENOSPC);
3426163fc9cSnatano error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
3436163fc9cSnatano if (error) {
3446163fc9cSnatano /* truncate the added clusters away again */
345bee0c138Stedu (void) detrunc(dep, dep->de_FileSize, 0);
3466163fc9cSnatano return (error);
3476163fc9cSnatano }
3486163fc9cSnatano }
3496163fc9cSnatano
3506163fc9cSnatano /*
3516163fc9cSnatano * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a
3526163fc9cSnatano * memset(); we set the write size so ubc won't read in file data that
3536163fc9cSnatano * is zero'd later.
3546163fc9cSnatano */
3556163fc9cSnatano dep->de_FileSize = length;
3566163fc9cSnatano dep->de_flag |= DE_UPDATE|DE_MODIFIED;
3576163fc9cSnatano return 0;
3586163fc9cSnatano }
359