xref: /openbsd-src/usr.sbin/makefs/msdos/msdosfs_vnops.c (revision 502e0f15eadf9cbe35241a9373fce8ed25c95f51)
1*502e0f15Sguenther /*	$OpenBSD: msdosfs_vnops.c,v 1.10 2023/08/11 04:51:36 guenther Exp $	*/
26163fc9cSnatano /*	$NetBSD: msdosfs_vnops.c,v 1.17 2016/01/30 09:59:27 mlelstv 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 
514874b543Sderaadt #include <sys/types.h>
526163fc9cSnatano #include <sys/mman.h>
536163fc9cSnatano #include <fcntl.h>
546163fc9cSnatano #include <unistd.h>
556163fc9cSnatano 
5672290da1Snatano #include "ffs/buf.h"
576163fc9cSnatano 
58bb19b730Snatano #include <msdosfs/bpb.h>
5972290da1Snatano #include "msdos/direntry.h"
6072290da1Snatano #include "msdos/denode.h"
6172290da1Snatano #include "msdos/msdosfsmount.h"
6272290da1Snatano #include "msdos/fat.h"
636163fc9cSnatano 
646163fc9cSnatano #include "makefs.h"
656163fc9cSnatano #include "msdos.h"
666163fc9cSnatano 
676163fc9cSnatano #ifdef MSDOSFS_DEBUG
686163fc9cSnatano #define DPRINTF(a) printf a
696163fc9cSnatano #else
706163fc9cSnatano #define DPRINTF(a)
716163fc9cSnatano #endif
726163fc9cSnatano /*
736163fc9cSnatano  * Some general notes:
746163fc9cSnatano  *
756163fc9cSnatano  * In the ufs filesystem the inodes, superblocks, and indirect blocks are
764af638d7Stedu  * read/written using the mkfsvnode for the filesystem. Blocks that represent
774af638d7Stedu  * the contents of a file are read/written using the mkfsvnode for the file
786163fc9cSnatano  * (including directories when they are read/written as files). This
796163fc9cSnatano  * presents problems for the dos filesystem because data that should be in
806163fc9cSnatano  * an inode (if dos had them) resides in the directory itself.	Since we
814af638d7Stedu  * must update directory entries without the benefit of having the mkfsvnode
824af638d7Stedu  * for the directory we must use the mkfsvnode for the filesystem.	This means
836163fc9cSnatano  * that when a directory is actually read/written (via read, write, or
844af638d7Stedu  * readdir, or seek) we must use the mkfsvnode for the filesystem instead of
854af638d7Stedu  * the mkfsvnode for the directory as would happen in ufs. This is to insure we
866163fc9cSnatano  * retrieve the correct block from the buffer cache since the hash value is
874af638d7Stedu  * based upon the mkfsvnode address and the desired block number.
886163fc9cSnatano  */
896163fc9cSnatano 
906163fc9cSnatano static int msdosfs_wfile(const char *, struct denode *, fsnode *);
916163fc9cSnatano 
926163fc9cSnatano static void
msdosfs_times(struct msdosfsmount * pmp,struct denode * dep,const struct stat * st)936163fc9cSnatano msdosfs_times(struct msdosfsmount *pmp, struct denode *dep,
946163fc9cSnatano     const struct stat *st)
956163fc9cSnatano {
96*502e0f15Sguenther 	unix2dostime(&st->st_atim, pmp->pm_minuteswest, &dep->de_ADate,
97*502e0f15Sguenther 	    NULL, NULL);
98*502e0f15Sguenther 	unix2dostime(&st->st_mtim, pmp->pm_minuteswest, &dep->de_MDate,
99*502e0f15Sguenther 	    &dep->de_MTime, NULL);
1006163fc9cSnatano }
1016163fc9cSnatano 
1026163fc9cSnatano /*
1036163fc9cSnatano  * When we search a directory the blocks containing directory entries are
1046163fc9cSnatano  * read and examined.  The directory entries contain information that would
1056163fc9cSnatano  * normally be in the inode of a unix filesystem.  This means that some of
1066163fc9cSnatano  * a directory's contents may also be in memory resident denodes (sort of
1076163fc9cSnatano  * an inode).  This can cause problems if we are searching while some other
1086163fc9cSnatano  * process is modifying a directory.  To prevent one process from accessing
1096163fc9cSnatano  * incompletely modified directory information we depend upon being the
1106163fc9cSnatano  * sole owner of a directory block.  bread/brelse provide this service.
1116163fc9cSnatano  * This being the case, when a process modifies a directory it must first
1126163fc9cSnatano  * acquire the disk block that contains the directory entry to be modified.
1136163fc9cSnatano  * Then update the disk block and the denode, and then write the disk block
1146163fc9cSnatano  * out to disk.	 This way disk blocks containing directory entries and in
1156163fc9cSnatano  * memory denode's will be in synch.
1166163fc9cSnatano  */
1176163fc9cSnatano static int
msdosfs_findslot(struct denode * dp,struct componentname * cnp)1186163fc9cSnatano msdosfs_findslot(struct denode *dp, struct componentname *cnp)
1196163fc9cSnatano {
1206163fc9cSnatano 	daddr_t bn;
1216163fc9cSnatano 	int error;
1226163fc9cSnatano 	int slotcount;
1236163fc9cSnatano 	int slotoffset = 0;
1246163fc9cSnatano 	int frcn;
1256163fc9cSnatano 	u_long cluster;
1266163fc9cSnatano 	int blkoff;
1276163fc9cSnatano 	u_int diroff;
1286163fc9cSnatano 	int blsize;
1296163fc9cSnatano 	struct msdosfsmount *pmp;
1304af638d7Stedu 	struct mkfsbuf *bp = 0;
1316163fc9cSnatano 	struct direntry *dep;
1326163fc9cSnatano 	u_char dosfilename[12];
1336163fc9cSnatano 	int wincnt = 1;
1346163fc9cSnatano 	int chksum = -1, chksum_ok;
1356163fc9cSnatano 	int olddos = 1;
1366163fc9cSnatano 
1376163fc9cSnatano 	pmp = dp->de_pmp;
1386163fc9cSnatano 
139800675fdSnatano 	switch (unix2dosfn(cnp->cn_nameptr, dosfilename, cnp->cn_namelen, 0)) {
1406163fc9cSnatano 	case 0:
1416163fc9cSnatano 		return (EINVAL);
1426163fc9cSnatano 	case 1:
1436163fc9cSnatano 		break;
1446163fc9cSnatano 	case 2:
145800675fdSnatano 		wincnt = winSlotCnt(cnp->cn_nameptr, cnp->cn_namelen) + 1;
1466163fc9cSnatano 		break;
1476163fc9cSnatano 	case 3:
1486163fc9cSnatano 		olddos = 0;
149800675fdSnatano 		wincnt = winSlotCnt(cnp->cn_nameptr, cnp->cn_namelen) + 1;
1506163fc9cSnatano 		break;
1516163fc9cSnatano 	}
1526163fc9cSnatano 
1536163fc9cSnatano 	if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
1546163fc9cSnatano 		wincnt = 1;
1556163fc9cSnatano 
1566163fc9cSnatano 	/*
1576163fc9cSnatano 	 * Suppress search for slots unless creating
1586163fc9cSnatano 	 * file and at end of pathname, in which case
1596163fc9cSnatano 	 * we watch for a place to put the new file in
1606163fc9cSnatano 	 * case it doesn't already exist.
1616163fc9cSnatano 	 */
1626163fc9cSnatano 	slotcount = 0;
1636163fc9cSnatano 	DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename));
1646163fc9cSnatano 	/*
1656163fc9cSnatano 	 * Search the directory pointed at by vdp for the name pointed at
1666163fc9cSnatano 	 * by cnp->cn_nameptr.
1676163fc9cSnatano 	 */
1686163fc9cSnatano 	/*
1696163fc9cSnatano 	 * The outer loop ranges over the clusters that make up the
1706163fc9cSnatano 	 * directory.  Note that the root directory is different from all
1716163fc9cSnatano 	 * other directories.  It has a fixed number of blocks that are not
1726163fc9cSnatano 	 * part of the pool of allocatable clusters.  So, we treat it a
1736163fc9cSnatano 	 * little differently. The root directory starts at "cluster" 0.
1746163fc9cSnatano 	 */
1756163fc9cSnatano 	diroff = 0;
1766163fc9cSnatano 	for (frcn = 0; diroff < dp->de_FileSize; frcn++) {
1776163fc9cSnatano 		if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) {
1786163fc9cSnatano 			if (error == E2BIG)
1796163fc9cSnatano 				break;
1806163fc9cSnatano 			return (error);
1816163fc9cSnatano 		}
1826163fc9cSnatano 		error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
1836163fc9cSnatano 		    0, &bp);
1846163fc9cSnatano 		if (error) {
1856163fc9cSnatano 			return (error);
1866163fc9cSnatano 		}
1876163fc9cSnatano 		for (blkoff = 0; blkoff < blsize;
1886163fc9cSnatano 		     blkoff += sizeof(struct direntry),
1896163fc9cSnatano 		     diroff += sizeof(struct direntry)) {
1906163fc9cSnatano 			dep = (struct direntry *)((char *)bp->b_data + blkoff);
1916163fc9cSnatano 			/*
1926163fc9cSnatano 			 * If the slot is empty and we are still looking
1936163fc9cSnatano 			 * for an empty then remember this one.	 If the
1946163fc9cSnatano 			 * slot is not empty then check to see if it
1956163fc9cSnatano 			 * matches what we are looking for.  If the slot
1966163fc9cSnatano 			 * has never been filled with anything, then the
1976163fc9cSnatano 			 * remainder of the directory has never been used,
1986163fc9cSnatano 			 * so there is no point in searching it.
1996163fc9cSnatano 			 */
2006163fc9cSnatano 			if (dep->deName[0] == SLOT_EMPTY ||
2016163fc9cSnatano 			    dep->deName[0] == SLOT_DELETED) {
2026163fc9cSnatano 				/*
2036163fc9cSnatano 				 * Drop memory of previous long matches
2046163fc9cSnatano 				 */
2056163fc9cSnatano 				chksum = -1;
2066163fc9cSnatano 
2076163fc9cSnatano 				if (slotcount < wincnt) {
2086163fc9cSnatano 					slotcount++;
2096163fc9cSnatano 					slotoffset = diroff;
2106163fc9cSnatano 				}
2116163fc9cSnatano 				if (dep->deName[0] == SLOT_EMPTY) {
2126163fc9cSnatano 					brelse(bp, 0);
2136163fc9cSnatano 					goto notfound;
2146163fc9cSnatano 				}
2156163fc9cSnatano 			} else {
2166163fc9cSnatano 				/*
2176163fc9cSnatano 				 * If there wasn't enough space for our
2186163fc9cSnatano 				 * winentries, forget about the empty space
2196163fc9cSnatano 				 */
2206163fc9cSnatano 				if (slotcount < wincnt)
2216163fc9cSnatano 					slotcount = 0;
2226163fc9cSnatano 
2236163fc9cSnatano 				/*
2246163fc9cSnatano 				 * Check for Win95 long filename entry
2256163fc9cSnatano 				 */
2266163fc9cSnatano 				if (dep->deAttributes == ATTR_WIN95) {
2276163fc9cSnatano 					if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
2286163fc9cSnatano 						continue;
2296163fc9cSnatano 
230800675fdSnatano 					chksum = winChkName(cnp->cn_nameptr,
2316163fc9cSnatano 							    cnp->cn_namelen,
2326163fc9cSnatano 							    (struct winentry *)dep,
233800675fdSnatano 							    chksum);
2346163fc9cSnatano 					continue;
2356163fc9cSnatano 				}
2366163fc9cSnatano 
2376163fc9cSnatano 				/*
2386163fc9cSnatano 				 * Ignore volume labels (anywhere, not just
2396163fc9cSnatano 				 * the root directory).
2406163fc9cSnatano 				 */
2416163fc9cSnatano 				if (dep->deAttributes & ATTR_VOLUME) {
2426163fc9cSnatano 					chksum = -1;
2436163fc9cSnatano 					continue;
2446163fc9cSnatano 				}
2456163fc9cSnatano 
2466163fc9cSnatano 				/*
2476163fc9cSnatano 				 * Check for a checksum or name match
2486163fc9cSnatano 				 */
2496163fc9cSnatano 				chksum_ok = (chksum == winChksum(dep->deName));
2506163fc9cSnatano 				if (!chksum_ok
2516163fc9cSnatano 				    && (!olddos || memcmp(dosfilename, dep->deName, 11))) {
2526163fc9cSnatano 					chksum = -1;
2536163fc9cSnatano 					continue;
2546163fc9cSnatano 				}
2556163fc9cSnatano 				DPRINTF(("%s(): match blkoff %d, diroff %d\n",
2566163fc9cSnatano 				    __func__, blkoff, diroff));
2576163fc9cSnatano 				/*
2586163fc9cSnatano 				 * Remember where this directory
2596163fc9cSnatano 				 * entry came from for whoever did
2606163fc9cSnatano 				 * this lookup.
2616163fc9cSnatano 				 */
2626163fc9cSnatano 				dp->de_fndoffset = diroff;
2636163fc9cSnatano 				dp->de_fndcnt = 0;
2646163fc9cSnatano 
2656163fc9cSnatano 				return EEXIST;
2666163fc9cSnatano 			}
2676163fc9cSnatano 		}	/* for (blkoff = 0; .... */
2686163fc9cSnatano 		/*
2696163fc9cSnatano 		 * Release the buffer holding the directory cluster just
2706163fc9cSnatano 		 * searched.
2716163fc9cSnatano 		 */
2726163fc9cSnatano 		brelse(bp, 0);
2736163fc9cSnatano 	}	/* for (frcn = 0; ; frcn++) */
2746163fc9cSnatano 
2756163fc9cSnatano notfound:
2766163fc9cSnatano 	/*
2776163fc9cSnatano 	 * We hold no disk buffers at this point.
2786163fc9cSnatano 	 */
2796163fc9cSnatano 
2806163fc9cSnatano 	/*
2816163fc9cSnatano 	 * If we get here we didn't find the entry we were looking for. But
2826163fc9cSnatano 	 * that's ok if we are creating or renaming and are at the end of
2836163fc9cSnatano 	 * the pathname and the directory hasn't been removed.
2846163fc9cSnatano 	 */
2856163fc9cSnatano 	DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n",
2866163fc9cSnatano 	    __func__, dp->de_refcnt, slotcount, slotoffset));
2876163fc9cSnatano 	/*
2886163fc9cSnatano 	 * Fixup the slot description to point to the place where
2896163fc9cSnatano 	 * we might put the new DOS direntry (putting the Win95
2906163fc9cSnatano 	 * long name entries before that)
2916163fc9cSnatano 	 */
2926163fc9cSnatano 	if (!slotcount) {
2936163fc9cSnatano 		slotcount = 1;
2946163fc9cSnatano 		slotoffset = diroff;
2956163fc9cSnatano 	}
2966163fc9cSnatano 	if (wincnt > slotcount) {
2976163fc9cSnatano 		slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
2986163fc9cSnatano 	}
2996163fc9cSnatano 
3006163fc9cSnatano 	/*
3016163fc9cSnatano 	 * Return an indication of where the new directory
3026163fc9cSnatano 	 * entry should be put.
3036163fc9cSnatano 	 */
3046163fc9cSnatano 	dp->de_fndoffset = slotoffset;
3056163fc9cSnatano 	dp->de_fndcnt = wincnt - 1;
3066163fc9cSnatano 
3076163fc9cSnatano 	/*
3086163fc9cSnatano 	 * We return with the directory locked, so that
3096163fc9cSnatano 	 * the parameters we set up above will still be
3106163fc9cSnatano 	 * valid if we actually decide to do a direnter().
3116163fc9cSnatano 	 * We return ni_vp == NULL to indicate that the entry
3126163fc9cSnatano 	 * does not currently exist; we leave a pointer to
3136163fc9cSnatano 	 * the (locked) directory inode in ndp->ni_dvp.
3146163fc9cSnatano 	 *
3156163fc9cSnatano 	 * NB - if the directory is unlocked, then this
3166163fc9cSnatano 	 * information cannot be used.
3176163fc9cSnatano 	 */
3186163fc9cSnatano 	return 0;
3196163fc9cSnatano }
3206163fc9cSnatano 
3216163fc9cSnatano /*
3226163fc9cSnatano  * Create a regular file. On entry the directory to contain the file being
3236163fc9cSnatano  * created is locked.  We must release before we return.
3246163fc9cSnatano  */
3256163fc9cSnatano struct denode *
msdosfs_mkfile(const char * path,struct denode * pdep,fsnode * node)3266163fc9cSnatano msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node)
3276163fc9cSnatano {
3286163fc9cSnatano 	struct componentname cn;
3296163fc9cSnatano 	struct denode ndirent;
3306163fc9cSnatano 	struct denode *dep;
3316163fc9cSnatano 	int error;
3326163fc9cSnatano 	struct stat *st = &node->inode->st;
3336163fc9cSnatano 	struct msdosfsmount *pmp = pdep->de_pmp;
3346163fc9cSnatano 
3356163fc9cSnatano 	cn.cn_nameptr = node->name;
3366163fc9cSnatano 	cn.cn_namelen = strlen(node->name);
3376163fc9cSnatano 
3386163fc9cSnatano 	DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name,
3396163fc9cSnatano 	    st->st_mode, (size_t)st->st_size));
3406163fc9cSnatano 
3416163fc9cSnatano 	/*
3426163fc9cSnatano 	 * If this is the root directory and there is no space left we
3436163fc9cSnatano 	 * can't do anything.  This is because the root directory can not
3446163fc9cSnatano 	 * change size.
3456163fc9cSnatano 	 */
3466163fc9cSnatano 	if (pdep->de_StartCluster == MSDOSFSROOT
3476163fc9cSnatano 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
3486163fc9cSnatano 		error = ENOSPC;
3496163fc9cSnatano 		goto bad;
3506163fc9cSnatano 	}
3516163fc9cSnatano 
3526163fc9cSnatano 	/*
3536163fc9cSnatano 	 * Create a directory entry for the file, then call createde() to
3546163fc9cSnatano 	 * have it installed. NOTE: DOS files are always executable.  We
3556163fc9cSnatano 	 * use the absence of the owner write bit to make the file
3566163fc9cSnatano 	 * readonly.
3576163fc9cSnatano 	 */
3586163fc9cSnatano 	memset(&ndirent, 0, sizeof(ndirent));
3596163fc9cSnatano 	if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
3606163fc9cSnatano 		goto bad;
3616163fc9cSnatano 
3626163fc9cSnatano 	ndirent.de_Attributes = (st->st_mode & S_IWUSR) ?
3636163fc9cSnatano 				ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
3646163fc9cSnatano 	ndirent.de_StartCluster = 0;
3656163fc9cSnatano 	ndirent.de_FileSize = 0;
3666163fc9cSnatano 	ndirent.de_dev = pdep->de_dev;
3676163fc9cSnatano 	ndirent.de_devvp = pdep->de_devvp;
3686163fc9cSnatano 	ndirent.de_pmp = pdep->de_pmp;
3696163fc9cSnatano 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
3706163fc9cSnatano 	msdosfs_times(pmp, &ndirent, st);
3716163fc9cSnatano 	if ((error = msdosfs_findslot(pdep, &cn)) != 0)
3726163fc9cSnatano 		goto bad;
3736163fc9cSnatano 	if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
3746163fc9cSnatano 		goto bad;
3756163fc9cSnatano 	if ((error = msdosfs_wfile(path, dep, node)) != 0)
3766163fc9cSnatano 		goto bad;
3776163fc9cSnatano 	return dep;
3786163fc9cSnatano 
3796163fc9cSnatano bad:
3806163fc9cSnatano 	errno = error;
3816163fc9cSnatano 	return NULL;
3826163fc9cSnatano }
3836163fc9cSnatano static int
msdosfs_updatede(struct denode * dep)3846163fc9cSnatano msdosfs_updatede(struct denode *dep)
3856163fc9cSnatano {
3864af638d7Stedu 	struct mkfsbuf *bp;
3876163fc9cSnatano 	struct direntry *dirp;
3886163fc9cSnatano 	int error;
3896163fc9cSnatano 
3906163fc9cSnatano 	dep->de_flag &= ~DE_MODIFIED;
3916163fc9cSnatano 	error = readde(dep, &bp, &dirp);
3926163fc9cSnatano 	if (error)
3936163fc9cSnatano 		return error;
3946163fc9cSnatano 	DE_EXTERNALIZE(dirp, dep);
3956163fc9cSnatano 	error = bwrite(bp);
3966163fc9cSnatano 	return error;
3976163fc9cSnatano }
3986163fc9cSnatano 
3996163fc9cSnatano /*
4006163fc9cSnatano  * Write data to a file or directory.
4016163fc9cSnatano  */
4026163fc9cSnatano static int
msdosfs_wfile(const char * path,struct denode * dep,fsnode * node)4036163fc9cSnatano msdosfs_wfile(const char *path, struct denode *dep, fsnode *node)
4046163fc9cSnatano {
4056163fc9cSnatano 	int error, fd;
4066163fc9cSnatano 	size_t osize = dep->de_FileSize;
4076163fc9cSnatano 	struct stat *st = &node->inode->st;
4086163fc9cSnatano 	size_t nsize, offs;
4096163fc9cSnatano 	struct msdosfsmount *pmp = dep->de_pmp;
4104af638d7Stedu 	struct mkfsbuf *bp;
4116163fc9cSnatano 	char *dat;
4126163fc9cSnatano 	u_long cn = 0;
4136163fc9cSnatano 
4146163fc9cSnatano 	error = 0;	/* XXX: gcc/vax */
4156163fc9cSnatano 	DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__,
4166163fc9cSnatano 	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster));
4176163fc9cSnatano 	if (st->st_size == 0)
4186163fc9cSnatano 		return 0;
4196163fc9cSnatano 
4206163fc9cSnatano 	/* Don't bother to try to write files larger than the fs limit */
4216163fc9cSnatano 	if (st->st_size > MSDOSFS_FILESIZE_MAX) {
4226163fc9cSnatano 		errno = EFBIG;
4236163fc9cSnatano 		return -1;
4246163fc9cSnatano 	}
4256163fc9cSnatano 
4266163fc9cSnatano 	nsize = st->st_size;
4276163fc9cSnatano 	DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize));
4286163fc9cSnatano 	if (nsize > osize) {
429bee0c138Stedu 		if ((error = deextend(dep, nsize)) != 0) {
4306163fc9cSnatano 			errno = error;
4316163fc9cSnatano 			return -1;
4326163fc9cSnatano 		}
4336163fc9cSnatano 		if ((error = msdosfs_updatede(dep)) != 0) {
4346163fc9cSnatano 			errno = error;
4356163fc9cSnatano 			return -1;
4366163fc9cSnatano 		}
4376163fc9cSnatano 	}
4386163fc9cSnatano 
4396163fc9cSnatano 	if ((fd = open(path, O_RDONLY)) == -1)
4406163fc9cSnatano 		err(1, "open %s", path);
4416163fc9cSnatano 
4426163fc9cSnatano 	if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0))
4436163fc9cSnatano 	    == MAP_FAILED) {
4446163fc9cSnatano 		DPRINTF(("%s: mmap %s %s", __func__, node->name,
4456163fc9cSnatano 		    strerror(errno)));
4466163fc9cSnatano 		close(fd);
4476163fc9cSnatano 		goto out;
4486163fc9cSnatano 	}
4496163fc9cSnatano 	close(fd);
4506163fc9cSnatano 
4516163fc9cSnatano 	for (offs = 0; offs < nsize;) {
4526163fc9cSnatano 		int blsize, cpsize;
4536163fc9cSnatano 		daddr_t bn;
4546163fc9cSnatano 		u_long on = offs & pmp->pm_crbomask;
4556163fc9cSnatano #ifdef HACK
4566163fc9cSnatano 		cn = dep->de_StartCluster;
4576163fc9cSnatano 		if (cn == MSDOSFSROOT) {
4586163fc9cSnatano 			DPRINTF(("%s: bad lbn %lu", __func__, cn));
4596163fc9cSnatano 			goto out;
4606163fc9cSnatano 		}
4616163fc9cSnatano 		bn = cntobn(pmp, cn);
4626163fc9cSnatano 		blsize = pmp->pm_bpcluster;
4636163fc9cSnatano #else
4646163fc9cSnatano 		if ((error = pcbmap(dep, cn++, &bn, NULL, &blsize)) != 0) {
4656163fc9cSnatano 			DPRINTF(("%s: pcbmap %lu", __func__, bn));
4666163fc9cSnatano 			goto out;
4676163fc9cSnatano 		}
4686163fc9cSnatano #endif
4696163fc9cSnatano 		DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__,
4706163fc9cSnatano 		    cn, (unsigned long long)bn,
4716163fc9cSnatano 		    (unsigned long long)de_bn2kb(pmp, bn), blsize));
4726163fc9cSnatano 		if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
4736163fc9cSnatano 		    0, &bp)) != 0) {
4746163fc9cSnatano 			DPRINTF(("bread %d\n", error));
4756163fc9cSnatano 			goto out;
4766163fc9cSnatano 		}
4774874b543Sderaadt 		cpsize = MINIMUM((nsize - offs), blsize - on);
4786163fc9cSnatano 		memcpy((char *)bp->b_data + on, dat + offs, cpsize);
4796163fc9cSnatano 		bwrite(bp);
4806163fc9cSnatano 		offs += cpsize;
4816163fc9cSnatano 	}
4826163fc9cSnatano 
4836163fc9cSnatano 	munmap(dat, nsize);
4846163fc9cSnatano 	return 0;
4856163fc9cSnatano out:
4866163fc9cSnatano 	munmap(dat, nsize);
4876163fc9cSnatano 	return error;
4886163fc9cSnatano }
4896163fc9cSnatano 
4906163fc9cSnatano 
4916163fc9cSnatano static const struct {
4926163fc9cSnatano 	struct direntry dot;
4936163fc9cSnatano 	struct direntry dotdot;
4946163fc9cSnatano } dosdirtemplate = {
4956163fc9cSnatano 	{	".       ", "   ",			/* the . entry */
4966163fc9cSnatano 		ATTR_DIRECTORY,				/* file attribute */
4976163fc9cSnatano 		0,					/* reserved */
4986163fc9cSnatano 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
4996163fc9cSnatano 		{ 0, 0 },				/* access date */
5006163fc9cSnatano 		{ 0, 0 },				/* high bits of start cluster */
5016163fc9cSnatano 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
5026163fc9cSnatano 		{ 0, 0 },				/* startcluster */
5036163fc9cSnatano 		{ 0, 0, 0, 0 }				/* filesize */
5046163fc9cSnatano 	},
5056163fc9cSnatano 	{	"..      ", "   ",			/* the .. entry */
5066163fc9cSnatano 		ATTR_DIRECTORY,				/* file attribute */
5076163fc9cSnatano 		0,					/* reserved */
5086163fc9cSnatano 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
5096163fc9cSnatano 		{ 0, 0 },				/* access date */
5106163fc9cSnatano 		{ 0, 0 },				/* high bits of start cluster */
5116163fc9cSnatano 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
5126163fc9cSnatano 		{ 0, 0 },				/* startcluster */
5136163fc9cSnatano 		{ 0, 0, 0, 0 }				/* filesize */
5146163fc9cSnatano 	}
5156163fc9cSnatano };
5166163fc9cSnatano 
5176163fc9cSnatano struct denode *
msdosfs_mkdire(const char * path,struct denode * pdep,fsnode * node)5186163fc9cSnatano msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) {
5196163fc9cSnatano 	struct denode ndirent;
5206163fc9cSnatano 	struct denode *dep;
5216163fc9cSnatano 	struct componentname cn;
5226163fc9cSnatano 	struct stat *st = &node->inode->st;
5236163fc9cSnatano 	struct msdosfsmount *pmp = pdep->de_pmp;
5246163fc9cSnatano 	int error;
5256163fc9cSnatano 	u_long newcluster, pcl, bn;
5266163fc9cSnatano 	daddr_t lbn;
5276163fc9cSnatano 	struct direntry *denp;
5284af638d7Stedu 	struct mkfsbuf *bp;
5296163fc9cSnatano 
5306163fc9cSnatano 	cn.cn_nameptr = node->name;
5316163fc9cSnatano 	cn.cn_namelen = strlen(node->name);
5326163fc9cSnatano 	/*
5336163fc9cSnatano 	 * If this is the root directory and there is no space left we
5346163fc9cSnatano 	 * can't do anything.  This is because the root directory can not
5356163fc9cSnatano 	 * change size.
5366163fc9cSnatano 	 */
5376163fc9cSnatano 	if (pdep->de_StartCluster == MSDOSFSROOT
5386163fc9cSnatano 	    && pdep->de_fndoffset >= pdep->de_FileSize) {
5396163fc9cSnatano 		error = ENOSPC;
5406163fc9cSnatano 		goto bad2;
5416163fc9cSnatano 	}
5426163fc9cSnatano 
5436163fc9cSnatano 	/*
5446163fc9cSnatano 	 * Allocate a cluster to hold the about to be created directory.
5456163fc9cSnatano 	 */
5466163fc9cSnatano 	error = clusteralloc(pmp, 0, 1, &newcluster, NULL);
5476163fc9cSnatano 	if (error)
5486163fc9cSnatano 		goto bad2;
5496163fc9cSnatano 
5506163fc9cSnatano 	memset(&ndirent, 0, sizeof(ndirent));
5516163fc9cSnatano 	ndirent.de_pmp = pmp;
5526163fc9cSnatano 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
5536163fc9cSnatano 	msdosfs_times(pmp, &ndirent, st);
5546163fc9cSnatano 
5556163fc9cSnatano 	/*
5566163fc9cSnatano 	 * Now fill the cluster with the "." and ".." entries. And write
5576163fc9cSnatano 	 * the cluster to disk.	 This way it is there for the parent
5586163fc9cSnatano 	 * directory to be pointing at if there were a crash.
5596163fc9cSnatano 	 */
5606163fc9cSnatano 	bn = cntobn(pmp, newcluster);
5616163fc9cSnatano 	lbn = de_bn2kb(pmp, bn);
5626163fc9cSnatano 	DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__, newcluster,
5636163fc9cSnatano 	    bn, lbn));
5646163fc9cSnatano 	/* always succeeds */
5656163fc9cSnatano 	bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
5666163fc9cSnatano 	memset(bp->b_data, 0, pmp->pm_bpcluster);
5676163fc9cSnatano 	memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
5686163fc9cSnatano 	denp = (struct direntry *)bp->b_data;
5696163fc9cSnatano 	putushort(denp[0].deStartCluster, newcluster);
5706163fc9cSnatano 	putushort(denp[0].deCDate, ndirent.de_CDate);
5716163fc9cSnatano 	putushort(denp[0].deCTime, ndirent.de_CTime);
572800675fdSnatano 	denp[0].deCTimeHundredth = ndirent.de_CHun;
5736163fc9cSnatano 	putushort(denp[0].deADate, ndirent.de_ADate);
5746163fc9cSnatano 	putushort(denp[0].deMDate, ndirent.de_MDate);
5756163fc9cSnatano 	putushort(denp[0].deMTime, ndirent.de_MTime);
5766163fc9cSnatano 	pcl = pdep->de_StartCluster;
5776163fc9cSnatano 	DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl,
5786163fc9cSnatano 	    pmp->pm_rootdirblk));
5796163fc9cSnatano 	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
5806163fc9cSnatano 		pcl = 0;
5816163fc9cSnatano 	putushort(denp[1].deStartCluster, pcl);
5826163fc9cSnatano 	putushort(denp[1].deCDate, ndirent.de_CDate);
5836163fc9cSnatano 	putushort(denp[1].deCTime, ndirent.de_CTime);
584800675fdSnatano 	denp[1].deCTimeHundredth = ndirent.de_CHun;
5856163fc9cSnatano 	putushort(denp[1].deADate, ndirent.de_ADate);
5866163fc9cSnatano 	putushort(denp[1].deMDate, ndirent.de_MDate);
5876163fc9cSnatano 	putushort(denp[1].deMTime, ndirent.de_MTime);
5886163fc9cSnatano 	if (FAT32(pmp)) {
5896163fc9cSnatano 		putushort(denp[0].deHighClust, newcluster >> 16);
5906163fc9cSnatano 		putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
5916163fc9cSnatano 	} else {
5926163fc9cSnatano 		putushort(denp[0].deHighClust, 0);
5936163fc9cSnatano 		putushort(denp[1].deHighClust, 0);
5946163fc9cSnatano 	}
5956163fc9cSnatano 
5966163fc9cSnatano 	if ((error = bwrite(bp)) != 0)
5976163fc9cSnatano 		goto bad;
5986163fc9cSnatano 
5996163fc9cSnatano 	/*
6006163fc9cSnatano 	 * Now build up a directory entry pointing to the newly allocated
6016163fc9cSnatano 	 * cluster.  This will be written to an empty slot in the parent
6026163fc9cSnatano 	 * directory.
6036163fc9cSnatano 	 */
6046163fc9cSnatano 	if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
6056163fc9cSnatano 		goto bad;
6066163fc9cSnatano 
6076163fc9cSnatano 	ndirent.de_Attributes = ATTR_DIRECTORY;
6086163fc9cSnatano 	ndirent.de_StartCluster = newcluster;
6096163fc9cSnatano 	ndirent.de_FileSize = 0;
6106163fc9cSnatano 	ndirent.de_dev = pdep->de_dev;
6116163fc9cSnatano 	ndirent.de_devvp = pdep->de_devvp;
6126163fc9cSnatano 	ndirent.de_pmp = pdep->de_pmp;
6136163fc9cSnatano 	if ((error = msdosfs_findslot(pdep, &cn)) != 0)
6146163fc9cSnatano 		goto bad;
6156163fc9cSnatano 	if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0)
6166163fc9cSnatano 		goto bad;
6176163fc9cSnatano 	if ((error = msdosfs_updatede(dep)) != 0)
6186163fc9cSnatano 		goto bad;
6196163fc9cSnatano 	return dep;
6206163fc9cSnatano 
6216163fc9cSnatano bad:
6226163fc9cSnatano 	clusterfree(pmp, newcluster, NULL);
6236163fc9cSnatano bad2:
6246163fc9cSnatano 	errno = error;
6256163fc9cSnatano 	return NULL;
6266163fc9cSnatano }
627