xref: /openbsd-src/usr.sbin/makefs/msdos/msdosfs_denode.c (revision 4874b543168b8168ff9788e98a56e3398af44a49)
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