xref: /netbsd-src/usr.sbin/makefs/msdos/msdosfs_vnops.c (revision bf2ad7248906fbda5c0a68bac7ee290dcd2d2b38)
1*bf2ad724Sriastradh /*	$NetBSD: msdosfs_vnops.c,v 1.22 2022/04/09 10:05:35 riastradh Exp $ */
27e2d9be6Schristos 
37e2d9be6Schristos /*-
47e2d9be6Schristos  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
57e2d9be6Schristos  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
67e2d9be6Schristos  * All rights reserved.
77e2d9be6Schristos  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
87e2d9be6Schristos  *
97e2d9be6Schristos  * Redistribution and use in source and binary forms, with or without
107e2d9be6Schristos  * modification, are permitted provided that the following conditions
117e2d9be6Schristos  * are met:
127e2d9be6Schristos  * 1. Redistributions of source code must retain the above copyright
137e2d9be6Schristos  *    notice, this list of conditions and the following disclaimer.
147e2d9be6Schristos  * 2. Redistributions in binary form must reproduce the above copyright
157e2d9be6Schristos  *    notice, this list of conditions and the following disclaimer in the
167e2d9be6Schristos  *    documentation and/or other materials provided with the distribution.
177e2d9be6Schristos  * 3. All advertising materials mentioning features or use of this software
187e2d9be6Schristos  *    must display the following acknowledgement:
197e2d9be6Schristos  *	This product includes software developed by TooLs GmbH.
207e2d9be6Schristos  * 4. The name of TooLs GmbH may not be used to endorse or promote products
217e2d9be6Schristos  *    derived from this software without specific prior written permission.
227e2d9be6Schristos  *
237e2d9be6Schristos  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
247e2d9be6Schristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
257e2d9be6Schristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
267e2d9be6Schristos  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
277e2d9be6Schristos  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
287e2d9be6Schristos  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
297e2d9be6Schristos  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
307e2d9be6Schristos  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
317e2d9be6Schristos  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
327e2d9be6Schristos  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
337e2d9be6Schristos  */
347e2d9be6Schristos /*
357e2d9be6Schristos  * Written by Paul Popelka (paulp@uts.amdahl.com)
367e2d9be6Schristos  *
377e2d9be6Schristos  * You can do anything you want with this software, just don't say you wrote
387e2d9be6Schristos  * it, and don't remove this notice.
397e2d9be6Schristos  *
407e2d9be6Schristos  * This software is provided "as is".
417e2d9be6Schristos  *
427e2d9be6Schristos  * The author supplies this software to be publicly redistributed on the
437e2d9be6Schristos  * understanding that the author is not responsible for the correct
447e2d9be6Schristos  * functioning of this software in any circumstances and is not liable for
457e2d9be6Schristos  * any damages caused by this software.
467e2d9be6Schristos  *
477e2d9be6Schristos  * October 1992
487e2d9be6Schristos  */
49ed8edcd6Schristos #if HAVE_NBTOOL_CONFIG_H
50ed8edcd6Schristos #include "nbtool_config.h"
51ed8edcd6Schristos #endif
527e2d9be6Schristos 
537e2d9be6Schristos #include <sys/cdefs.h>
54*bf2ad724Sriastradh __KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.22 2022/04/09 10:05:35 riastradh Exp $");
557e2d9be6Schristos 
567e2d9be6Schristos #include <sys/param.h>
577e2d9be6Schristos #include <sys/mman.h>
587e2d9be6Schristos #include <fcntl.h>
597e2d9be6Schristos #include <unistd.h>
607e2d9be6Schristos 
617e2d9be6Schristos #include <ffs/buf.h>
627e2d9be6Schristos 
637e2d9be6Schristos #include <fs/msdosfs/bpb.h>
647e2d9be6Schristos #include <fs/msdosfs/direntry.h>
657e2d9be6Schristos #include <fs/msdosfs/denode.h>
667e2d9be6Schristos #include <fs/msdosfs/msdosfsmount.h>
677e2d9be6Schristos #include <fs/msdosfs/fat.h>
687e2d9be6Schristos 
697e2d9be6Schristos #include "makefs.h"
707e2d9be6Schristos #include "msdos.h"
717e2d9be6Schristos 
72e502c2ffSchristos #ifdef MSDOSFS_DEBUG
73e502c2ffSchristos #define DPRINTF(a) printf a
74e502c2ffSchristos #else
75e502c2ffSchristos #define DPRINTF(a)
76e502c2ffSchristos #endif
777e2d9be6Schristos /*
787e2d9be6Schristos  * Some general notes:
797e2d9be6Schristos  *
807e2d9be6Schristos  * In the ufs filesystem the inodes, superblocks, and indirect blocks are
817e2d9be6Schristos  * read/written using the vnode for the filesystem. Blocks that represent
827e2d9be6Schristos  * the contents of a file are read/written using the vnode for the file
837e2d9be6Schristos  * (including directories when they are read/written as files). This
847e2d9be6Schristos  * presents problems for the dos filesystem because data that should be in
857e2d9be6Schristos  * an inode (if dos had them) resides in the directory itself.	Since we
867e2d9be6Schristos  * must update directory entries without the benefit of having the vnode
877e2d9be6Schristos  * for the directory we must use the vnode for the filesystem.	This means
887e2d9be6Schristos  * that when a directory is actually read/written (via read, write, or
897e2d9be6Schristos  * readdir, or seek) we must use the vnode for the filesystem instead of
907e2d9be6Schristos  * the vnode for the directory as would happen in ufs. This is to insure we
917e2d9be6Schristos  * retrieve the correct block from the buffer cache since the hash value is
927e2d9be6Schristos  * based upon the vnode address and the desired block number.
937e2d9be6Schristos  */
947e2d9be6Schristos 
957e2d9be6Schristos static int msdosfs_wfile(const char *, struct denode *, fsnode *);
967e2d9be6Schristos 
977e2d9be6Schristos static void
msdosfs_times(struct msdosfsmount * pmp,struct denode * dep,const struct stat * st)987e2d9be6Schristos msdosfs_times(struct msdosfsmount *pmp, struct denode *dep,
997e2d9be6Schristos     const struct stat *st)
1007e2d9be6Schristos {
101e93222b7Schristos 	struct timespec at;
102e93222b7Schristos 	struct timespec mt;
103e93222b7Schristos 
104e93222b7Schristos 	if (stampst.st_ino)
105e93222b7Schristos 	    st = &stampst;
106e93222b7Schristos 
107c1cffbf8Schristos #ifndef HAVE_NBTOOL_CONFIG_H
108e93222b7Schristos 	at = st->st_atimespec;
109e93222b7Schristos 	mt = st->st_mtimespec;
110ed8edcd6Schristos #else
111e93222b7Schristos 	at.tv_sec = st->st_atime;
112e93222b7Schristos 	at.tv_nsec = 0;
113e93222b7Schristos 	mt.tv_sec = st->st_mtime;
114e93222b7Schristos 	mt.tv_nsec = 0;
115ed8edcd6Schristos #endif
1168086f46eSthorpej 	msdosfs_unix2dostime(&at, pmp->pm_gmtoff, &dep->de_ADate,
1178086f46eSthorpej 	    NULL, NULL);
1188086f46eSthorpej 	msdosfs_unix2dostime(&mt, pmp->pm_gmtoff, &dep->de_MDate,
1198086f46eSthorpej 	    &dep->de_MTime, NULL);
1207e2d9be6Schristos }
1217e2d9be6Schristos 
1227e2d9be6Schristos /*
123804eae6bSchristos  * When we search a directory the blocks containing directory entries are
124804eae6bSchristos  * read and examined.  The directory entries contain information that would
125804eae6bSchristos  * normally be in the inode of a unix filesystem.  This means that some of
126804eae6bSchristos  * a directory's contents may also be in memory resident denodes (sort of
127804eae6bSchristos  * an inode).  This can cause problems if we are searching while some other
128804eae6bSchristos  * process is modifying a directory.  To prevent one process from accessing
129804eae6bSchristos  * incompletely modified directory information we depend upon being the
130804eae6bSchristos  * sole owner of a directory block.  bread/brelse provide this service.
131804eae6bSchristos  * This being the case, when a process modifies a directory it must first
132804eae6bSchristos  * acquire the disk block that contains the directory entry to be modified.
133804eae6bSchristos  * Then update the disk block and the denode, and then write the disk block
134804eae6bSchristos  * out to disk.	 This way disk blocks containing directory entries and in
135804eae6bSchristos  * memory denode's will be in synch.
136804eae6bSchristos  */
137804eae6bSchristos static int
msdosfs_findslot(struct denode * dp,struct componentname * cnp)138804eae6bSchristos msdosfs_findslot(struct denode *dp, struct componentname *cnp)
139804eae6bSchristos {
140804eae6bSchristos 	daddr_t bn;
141804eae6bSchristos 	int error;
142804eae6bSchristos 	int slotcount;
143804eae6bSchristos 	int slotoffset = 0;
144804eae6bSchristos 	int frcn;
145804eae6bSchristos 	u_long cluster;
146804eae6bSchristos 	int blkoff;
147804eae6bSchristos 	u_int diroff;
148804eae6bSchristos 	int blsize;
149804eae6bSchristos 	struct msdosfsmount *pmp;
150804eae6bSchristos 	struct buf *bp = 0;
151804eae6bSchristos 	struct direntry *dep;
152804eae6bSchristos 	u_char dosfilename[12];
153804eae6bSchristos 	int wincnt = 1;
154804eae6bSchristos 	int chksum = -1, chksum_ok;
155804eae6bSchristos 	int olddos = 1;
156804eae6bSchristos 
157804eae6bSchristos 	pmp = dp->de_pmp;
1585f60de37Schristos 
1598086f46eSthorpej 	switch (msdosfs_unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,
160804eae6bSchristos 	    cnp->cn_namelen, 0)) {
161804eae6bSchristos 	case 0:
162804eae6bSchristos 		return (EINVAL);
163804eae6bSchristos 	case 1:
164804eae6bSchristos 		break;
165804eae6bSchristos 	case 2:
1668086f46eSthorpej 		wincnt = msdosfs_winSlotCnt((const u_char *)cnp->cn_nameptr,
167223c7df5Smlelstv 		    cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1;
168804eae6bSchristos 		break;
169804eae6bSchristos 	case 3:
170804eae6bSchristos 		olddos = 0;
1718086f46eSthorpej 		wincnt = msdosfs_winSlotCnt((const u_char *)cnp->cn_nameptr,
172223c7df5Smlelstv 		    cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1;
173804eae6bSchristos 		break;
174804eae6bSchristos 	}
175804eae6bSchristos 
176804eae6bSchristos 	if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
177804eae6bSchristos 		wincnt = 1;
178804eae6bSchristos 
179804eae6bSchristos 	/*
180804eae6bSchristos 	 * Suppress search for slots unless creating
181804eae6bSchristos 	 * file and at end of pathname, in which case
182804eae6bSchristos 	 * we watch for a place to put the new file in
183804eae6bSchristos 	 * case it doesn't already exist.
184804eae6bSchristos 	 */
185804eae6bSchristos 	slotcount = 0;
186e502c2ffSchristos 	DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename));
187804eae6bSchristos 	/*
188804eae6bSchristos 	 * Search the directory pointed at by vdp for the name pointed at
189804eae6bSchristos 	 * by cnp->cn_nameptr.
190804eae6bSchristos 	 */
191804eae6bSchristos 	/*
192804eae6bSchristos 	 * The outer loop ranges over the clusters that make up the
193804eae6bSchristos 	 * directory.  Note that the root directory is different from all
194804eae6bSchristos 	 * other directories.  It has a fixed number of blocks that are not
195804eae6bSchristos 	 * part of the pool of allocatable clusters.  So, we treat it a
196804eae6bSchristos 	 * little differently. The root directory starts at "cluster" 0.
197804eae6bSchristos 	 */
198804eae6bSchristos 	diroff = 0;
199804eae6bSchristos 	for (frcn = 0; diroff < dp->de_FileSize; frcn++) {
2008086f46eSthorpej 		if ((error = msdosfs_pcbmap(dp, frcn, &bn, &cluster,
2018086f46eSthorpej 		    &blsize)) != 0) {
202804eae6bSchristos 			if (error == E2BIG)
203804eae6bSchristos 				break;
204804eae6bSchristos 			return (error);
205804eae6bSchristos 		}
206aa7e0e4bSagc 		error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
207804eae6bSchristos 		    0, &bp);
208804eae6bSchristos 		if (error) {
209804eae6bSchristos 			return (error);
210804eae6bSchristos 		}
211804eae6bSchristos 		for (blkoff = 0; blkoff < blsize;
212804eae6bSchristos 		     blkoff += sizeof(struct direntry),
213804eae6bSchristos 		     diroff += sizeof(struct direntry)) {
214804eae6bSchristos 			dep = (struct direntry *)((char *)bp->b_data + blkoff);
215804eae6bSchristos 			/*
216804eae6bSchristos 			 * If the slot is empty and we are still looking
217804eae6bSchristos 			 * for an empty then remember this one.	 If the
218804eae6bSchristos 			 * slot is not empty then check to see if it
219804eae6bSchristos 			 * matches what we are looking for.  If the slot
220804eae6bSchristos 			 * has never been filled with anything, then the
221804eae6bSchristos 			 * remainder of the directory has never been used,
222804eae6bSchristos 			 * so there is no point in searching it.
223804eae6bSchristos 			 */
224804eae6bSchristos 			if (dep->deName[0] == SLOT_EMPTY ||
225804eae6bSchristos 			    dep->deName[0] == SLOT_DELETED) {
226804eae6bSchristos 				/*
227804eae6bSchristos 				 * Drop memory of previous long matches
228804eae6bSchristos 				 */
229804eae6bSchristos 				chksum = -1;
230804eae6bSchristos 
231804eae6bSchristos 				if (slotcount < wincnt) {
232804eae6bSchristos 					slotcount++;
233804eae6bSchristos 					slotoffset = diroff;
234804eae6bSchristos 				}
235804eae6bSchristos 				if (dep->deName[0] == SLOT_EMPTY) {
236804eae6bSchristos 					brelse(bp, 0);
237804eae6bSchristos 					goto notfound;
238804eae6bSchristos 				}
239804eae6bSchristos 			} else {
240804eae6bSchristos 				/*
241804eae6bSchristos 				 * If there wasn't enough space for our
242804eae6bSchristos 				 * winentries, forget about the empty space
243804eae6bSchristos 				 */
244804eae6bSchristos 				if (slotcount < wincnt)
245804eae6bSchristos 					slotcount = 0;
246804eae6bSchristos 
247804eae6bSchristos 				/*
248804eae6bSchristos 				 * Check for Win95 long filename entry
249804eae6bSchristos 				 */
250804eae6bSchristos 				if (dep->deAttributes == ATTR_WIN95) {
251804eae6bSchristos 					if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
252804eae6bSchristos 						continue;
253804eae6bSchristos 
2548086f46eSthorpej 					chksum = msdosfs_winChkName((const u_char *)cnp->cn_nameptr,
255804eae6bSchristos 							    cnp->cn_namelen,
256804eae6bSchristos 							    (struct winentry *)dep,
257223c7df5Smlelstv 							    chksum,
258223c7df5Smlelstv 							    pmp->pm_flags & MSDOSFSMNT_UTF8);
259804eae6bSchristos 					continue;
260804eae6bSchristos 				}
261804eae6bSchristos 
262804eae6bSchristos 				/*
263804eae6bSchristos 				 * Ignore volume labels (anywhere, not just
264804eae6bSchristos 				 * the root directory).
265804eae6bSchristos 				 */
266804eae6bSchristos 				if (dep->deAttributes & ATTR_VOLUME) {
267804eae6bSchristos 					chksum = -1;
268804eae6bSchristos 					continue;
269804eae6bSchristos 				}
270804eae6bSchristos 
271804eae6bSchristos 				/*
272804eae6bSchristos 				 * Check for a checksum or name match
273804eae6bSchristos 				 */
2748086f46eSthorpej 				chksum_ok =
2758086f46eSthorpej 				    (chksum == msdosfs_winChksum(dep->deName));
276804eae6bSchristos 				if (!chksum_ok
277804eae6bSchristos 				    && (!olddos || memcmp(dosfilename, dep->deName, 11))) {
278804eae6bSchristos 					chksum = -1;
279804eae6bSchristos 					continue;
280804eae6bSchristos 				}
281e502c2ffSchristos 				DPRINTF(("%s(): match blkoff %d, diroff %d\n",
282e502c2ffSchristos 				    __func__, blkoff, diroff));
283804eae6bSchristos 				/*
284804eae6bSchristos 				 * Remember where this directory
285804eae6bSchristos 				 * entry came from for whoever did
286804eae6bSchristos 				 * this lookup.
287804eae6bSchristos 				 */
28825fae635Shannken 				dp->de_crap.mlr_fndoffset = diroff;
28925fae635Shannken 				dp->de_crap.mlr_fndcnt = 0;
290804eae6bSchristos 
291804eae6bSchristos 				return EEXIST;
292804eae6bSchristos 			}
293804eae6bSchristos 		}	/* for (blkoff = 0; .... */
294804eae6bSchristos 		/*
295804eae6bSchristos 		 * Release the buffer holding the directory cluster just
296804eae6bSchristos 		 * searched.
297804eae6bSchristos 		 */
298804eae6bSchristos 		brelse(bp, 0);
299804eae6bSchristos 	}	/* for (frcn = 0; ; frcn++) */
300804eae6bSchristos 
301804eae6bSchristos notfound:
302804eae6bSchristos 	/*
303804eae6bSchristos 	 * We hold no disk buffers at this point.
304804eae6bSchristos 	 */
305804eae6bSchristos 
306804eae6bSchristos 	/*
307804eae6bSchristos 	 * If we get here we didn't find the entry we were looking for. But
308804eae6bSchristos 	 * that's ok if we are creating or renaming and are at the end of
309804eae6bSchristos 	 * the pathname and the directory hasn't been removed.
310804eae6bSchristos 	 */
311e502c2ffSchristos 	DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n",
312e502c2ffSchristos 	    __func__, dp->de_refcnt, slotcount, slotoffset));
313804eae6bSchristos 	/*
314804eae6bSchristos 	 * Fixup the slot description to point to the place where
315804eae6bSchristos 	 * we might put the new DOS direntry (putting the Win95
316804eae6bSchristos 	 * long name entries before that)
317804eae6bSchristos 	 */
318804eae6bSchristos 	if (!slotcount) {
319804eae6bSchristos 		slotcount = 1;
320804eae6bSchristos 		slotoffset = diroff;
321804eae6bSchristos 	}
322804eae6bSchristos 	if (wincnt > slotcount) {
323804eae6bSchristos 		slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
324804eae6bSchristos 	}
325804eae6bSchristos 
326804eae6bSchristos 	/*
327804eae6bSchristos 	 * Return an indication of where the new directory
328804eae6bSchristos 	 * entry should be put.
329804eae6bSchristos 	 */
33025fae635Shannken 	dp->de_crap.mlr_fndoffset = slotoffset;
33125fae635Shannken 	dp->de_crap.mlr_fndcnt = wincnt - 1;
332804eae6bSchristos 
333804eae6bSchristos 	/*
334804eae6bSchristos 	 * We return with the directory locked, so that
335804eae6bSchristos 	 * the parameters we set up above will still be
336804eae6bSchristos 	 * valid if we actually decide to do a direnter().
337804eae6bSchristos 	 * We return ni_vp == NULL to indicate that the entry
338804eae6bSchristos 	 * does not currently exist; we leave a pointer to
339804eae6bSchristos 	 * the (locked) directory inode in ndp->ni_dvp.
340804eae6bSchristos 	 *
341804eae6bSchristos 	 * NB - if the directory is unlocked, then this
342804eae6bSchristos 	 * information cannot be used.
343804eae6bSchristos 	 */
344804eae6bSchristos 	return 0;
345804eae6bSchristos }
346804eae6bSchristos 
347804eae6bSchristos /*
3487e2d9be6Schristos  * Create a regular file. On entry the directory to contain the file being
3497e2d9be6Schristos  * created is locked.  We must release before we return.
3507e2d9be6Schristos  */
3517e2d9be6Schristos struct denode *
msdosfs_mkfile(const char * path,struct denode * pdep,fsnode * node)3527e2d9be6Schristos msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node)
3537e2d9be6Schristos {
3547e2d9be6Schristos 	struct componentname cn;
3557e2d9be6Schristos 	struct denode ndirent;
3567e2d9be6Schristos 	struct denode *dep;
3577e2d9be6Schristos 	int error;
3587e2d9be6Schristos 	struct stat *st = &node->inode->st;
3597e2d9be6Schristos 	struct msdosfsmount *pmp = pdep->de_pmp;
3607e2d9be6Schristos 
3617e2d9be6Schristos 	cn.cn_nameptr = node->name;
3627e2d9be6Schristos 	cn.cn_namelen = strlen(node->name);
3637e2d9be6Schristos 
364e502c2ffSchristos 	DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name,
365e502c2ffSchristos 	    st->st_mode, (size_t)st->st_size));
3667e2d9be6Schristos 
3677e2d9be6Schristos 	/*
3687e2d9be6Schristos 	 * If this is the root directory and there is no space left we
3697e2d9be6Schristos 	 * can't do anything.  This is because the root directory can not
3707e2d9be6Schristos 	 * change size.
3717e2d9be6Schristos 	 */
3727e2d9be6Schristos 	if (pdep->de_StartCluster == MSDOSFSROOT
37325fae635Shannken 	    && pdep->de_crap.mlr_fndoffset >= pdep->de_FileSize) {
3747e2d9be6Schristos 		error = ENOSPC;
3757e2d9be6Schristos 		goto bad;
3767e2d9be6Schristos 	}
3777e2d9be6Schristos 
3787e2d9be6Schristos 	/*
3797e2d9be6Schristos 	 * Create a directory entry for the file, then call createde() to
3807e2d9be6Schristos 	 * have it installed. NOTE: DOS files are always executable.  We
3817e2d9be6Schristos 	 * use the absence of the owner write bit to make the file
3827e2d9be6Schristos 	 * readonly.
3837e2d9be6Schristos 	 */
3847e2d9be6Schristos 	memset(&ndirent, 0, sizeof(ndirent));
3858086f46eSthorpej 	if ((error = msdosfs_uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
3867e2d9be6Schristos 		goto bad;
3877e2d9be6Schristos 
3887e2d9be6Schristos 	ndirent.de_Attributes = (st->st_mode & S_IWUSR) ?
3897e2d9be6Schristos 				ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY;
3907e2d9be6Schristos 	ndirent.de_StartCluster = 0;
3917e2d9be6Schristos 	ndirent.de_FileSize = 0;
3927e2d9be6Schristos 	ndirent.de_dev = pdep->de_dev;
3937e2d9be6Schristos 	ndirent.de_devvp = pdep->de_devvp;
3947e2d9be6Schristos 	ndirent.de_pmp = pdep->de_pmp;
3957e2d9be6Schristos 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
3967e2d9be6Schristos 	msdosfs_times(pmp, &ndirent, st);
397804eae6bSchristos 	if ((error = msdosfs_findslot(pdep, &cn)) != 0)
398804eae6bSchristos 		goto bad;
3998086f46eSthorpej 	if ((error = msdosfs_createde(&ndirent, pdep, &pdep->de_crap, &dep,
4008086f46eSthorpej 	    &cn)) != 0)
4017e2d9be6Schristos 		goto bad;
402804eae6bSchristos 	if ((error = msdosfs_wfile(path, dep, node)) != 0)
4037e2d9be6Schristos 		goto bad;
4047e2d9be6Schristos 	return dep;
4057e2d9be6Schristos 
4067e2d9be6Schristos bad:
4077e2d9be6Schristos 	errno = error;
4087e2d9be6Schristos 	return NULL;
4097e2d9be6Schristos }
410e502c2ffSchristos static int
msdosfs_updatede(struct denode * dep)411e502c2ffSchristos msdosfs_updatede(struct denode *dep)
412e502c2ffSchristos {
413e502c2ffSchristos 	struct buf *bp;
414e502c2ffSchristos 	struct direntry *dirp;
415e502c2ffSchristos 	int error;
416e502c2ffSchristos 
417e502c2ffSchristos 	dep->de_flag &= ~DE_MODIFIED;
4188086f46eSthorpej 	error = msdosfs_readde(dep, &bp, &dirp);
419e502c2ffSchristos 	if (error)
420e502c2ffSchristos 		return error;
421e502c2ffSchristos 	DE_EXTERNALIZE(dirp, dep);
422e502c2ffSchristos 	error = bwrite(bp);
423e502c2ffSchristos 	return error;
424e502c2ffSchristos }
4257e2d9be6Schristos 
4267e2d9be6Schristos /*
4277e2d9be6Schristos  * Write data to a file or directory.
4287e2d9be6Schristos  */
4297e2d9be6Schristos static int
msdosfs_wfile(const char * path,struct denode * dep,fsnode * node)4307e2d9be6Schristos msdosfs_wfile(const char *path, struct denode *dep, fsnode *node)
4317e2d9be6Schristos {
4327e2d9be6Schristos 	int error, fd;
4337e2d9be6Schristos 	size_t osize = dep->de_FileSize;
4347e2d9be6Schristos 	struct stat *st = &node->inode->st;
435180d4518Schristos 	size_t nsize, offs;
4367e2d9be6Schristos 	struct msdosfsmount *pmp = dep->de_pmp;
4377e2d9be6Schristos 	struct buf *bp;
4387e2d9be6Schristos 	char *dat;
4390a54bc86Schristos 	u_long cn = 0;
4407e2d9be6Schristos 
441aed9f95cSchristos 	error = 0;	/* XXX: gcc/vax */
442e502c2ffSchristos 	DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__,
443e502c2ffSchristos 	    dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster));
444c24cac0fSmartin 	if (st->st_size == 0)
4457e2d9be6Schristos 		return 0;
4467e2d9be6Schristos 
4477e2d9be6Schristos 	/* Don't bother to try to write files larger than the fs limit */
448820702f0Schristos 	if (st->st_size > MSDOSFS_FILESIZE_MAX)
449820702f0Schristos 		return EFBIG;
4507e2d9be6Schristos 
451c24cac0fSmartin 	nsize = st->st_size;
452e502c2ffSchristos 	DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize));
4537e2d9be6Schristos 	if (nsize > osize) {
4548086f46eSthorpej 		if ((error = msdosfs_deextend(dep, nsize, NULL)) != 0)
455820702f0Schristos 			return error;
456820702f0Schristos 		if ((error = msdosfs_updatede(dep)) != 0)
457820702f0Schristos 			return error;
4587e2d9be6Schristos 	}
4597e2d9be6Schristos 
460820702f0Schristos 	if ((fd = open(path, O_RDONLY)) == -1) {
461820702f0Schristos 		error = errno;
462820702f0Schristos 		DPRINTF((1, "open %s: %s", path, strerror(error)));
463820702f0Schristos 		return error;
464820702f0Schristos 	}
4657e2d9be6Schristos 
466804eae6bSchristos 	if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0))
467804eae6bSchristos 	    == MAP_FAILED) {
468820702f0Schristos 		error = errno;
469820702f0Schristos 		DPRINTF(("%s: mmap %s: %s", __func__, node->name,
470820702f0Schristos 		    strerror(error)));
471e502c2ffSchristos 		close(fd);
472804eae6bSchristos 		goto out;
473804eae6bSchristos 	}
4747e2d9be6Schristos 	close(fd);
4757e2d9be6Schristos 
476180d4518Schristos 	for (offs = 0; offs < nsize;) {
4777e2d9be6Schristos 		int blsize, cpsize;
4787e2d9be6Schristos 		daddr_t bn;
479e502c2ffSchristos 		u_long on = offs & pmp->pm_crbomask;
4800a54bc86Schristos #ifdef HACK
4810a54bc86Schristos 		cn = dep->de_StartCluster;
4820a54bc86Schristos 		if (cn == MSDOSFSROOT) {
4830a54bc86Schristos 			DPRINTF(("%s: bad lbn %lu", __func__, cn));
484820702f0Schristos 			error = EINVAL;
4857e2d9be6Schristos 			goto out;
4867e2d9be6Schristos 		}
4870a54bc86Schristos 		bn = cntobn(pmp, cn);
488e502c2ffSchristos 		blsize = pmp->pm_bpcluster;
4890a54bc86Schristos #else
4908086f46eSthorpej 		if ((error = msdosfs_pcbmap(dep, cn++, &bn, NULL,
4918086f46eSthorpej 		    &blsize)) != 0) {
4920a54bc86Schristos 			DPRINTF(("%s: pcbmap %lu", __func__, bn));
4930a54bc86Schristos 			goto out;
4940a54bc86Schristos 		}
4950a54bc86Schristos #endif
4960a54bc86Schristos 		DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__,
4970a54bc86Schristos 		    cn, (unsigned long long)bn,
498e502c2ffSchristos 		    (unsigned long long)de_bn2kb(pmp, bn), blsize));
499804eae6bSchristos 		if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
500aa7e0e4bSagc 		    0, &bp)) != 0) {
501e502c2ffSchristos 			DPRINTF(("bread %d\n", error));
502804eae6bSchristos 			goto out;
5037e2d9be6Schristos 		}
504e502c2ffSchristos 		cpsize = MIN((nsize - offs), blsize - on);
505e502c2ffSchristos 		memcpy((char *)bp->b_data + on, dat + offs, cpsize);
5067e2d9be6Schristos 		bwrite(bp);
507e502c2ffSchristos 		offs += cpsize;
5087e2d9be6Schristos 	}
5097e2d9be6Schristos 
5107e2d9be6Schristos 	munmap(dat, nsize);
5117e2d9be6Schristos 	return 0;
5127e2d9be6Schristos out:
5137e2d9be6Schristos 	munmap(dat, nsize);
514804eae6bSchristos 	return error;
5157e2d9be6Schristos }
5167e2d9be6Schristos 
5177e2d9be6Schristos 
5187e2d9be6Schristos static const struct {
5197e2d9be6Schristos 	struct direntry dot;
5207e2d9be6Schristos 	struct direntry dotdot;
5217e2d9be6Schristos } dosdirtemplate = {
5227e2d9be6Schristos 	{	".       ", "   ",			/* the . entry */
5237e2d9be6Schristos 		ATTR_DIRECTORY,				/* file attribute */
5247e2d9be6Schristos 		0,					/* reserved */
5257e2d9be6Schristos 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
5267e2d9be6Schristos 		{ 0, 0 },				/* access date */
5277e2d9be6Schristos 		{ 0, 0 },				/* high bits of start cluster */
5287e2d9be6Schristos 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
5297e2d9be6Schristos 		{ 0, 0 },				/* startcluster */
5307e2d9be6Schristos 		{ 0, 0, 0, 0 }				/* filesize */
5317e2d9be6Schristos 	},
5327e2d9be6Schristos 	{	"..      ", "   ",			/* the .. entry */
5337e2d9be6Schristos 		ATTR_DIRECTORY,				/* file attribute */
5347e2d9be6Schristos 		0,					/* reserved */
5357e2d9be6Schristos 		0, { 0, 0 }, { 0, 0 },			/* create time & date */
5367e2d9be6Schristos 		{ 0, 0 },				/* access date */
5377e2d9be6Schristos 		{ 0, 0 },				/* high bits of start cluster */
5387e2d9be6Schristos 		{ 210, 4 }, { 210, 4 },			/* modify time & date */
5397e2d9be6Schristos 		{ 0, 0 },				/* startcluster */
5407e2d9be6Schristos 		{ 0, 0, 0, 0 }				/* filesize */
5417e2d9be6Schristos 	}
5427e2d9be6Schristos };
5437e2d9be6Schristos 
5447e2d9be6Schristos struct denode *
msdosfs_mkdire(const char * path,struct denode * pdep,fsnode * node)5457e2d9be6Schristos msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) {
5467e2d9be6Schristos 	struct denode ndirent;
5477e2d9be6Schristos 	struct denode *dep;
5487e2d9be6Schristos 	struct componentname cn;
5497e2d9be6Schristos 	struct stat *st = &node->inode->st;
5507e2d9be6Schristos 	struct msdosfsmount *pmp = pdep->de_pmp;
5517e2d9be6Schristos 	int error;
5527e2d9be6Schristos 	u_long newcluster, pcl, bn;
5537e2d9be6Schristos 	daddr_t lbn;
5547e2d9be6Schristos 	struct direntry *denp;
5557e2d9be6Schristos 	struct buf *bp;
5567e2d9be6Schristos 
5577e2d9be6Schristos 	cn.cn_nameptr = node->name;
5587e2d9be6Schristos 	cn.cn_namelen = strlen(node->name);
5597e2d9be6Schristos 	/*
5607e2d9be6Schristos 	 * If this is the root directory and there is no space left we
5617e2d9be6Schristos 	 * can't do anything.  This is because the root directory can not
5627e2d9be6Schristos 	 * change size.
5637e2d9be6Schristos 	 */
5647e2d9be6Schristos 	if (pdep->de_StartCluster == MSDOSFSROOT
56525fae635Shannken 	    && pdep->de_crap.mlr_fndoffset >= pdep->de_FileSize) {
5667e2d9be6Schristos 		error = ENOSPC;
5677e2d9be6Schristos 		goto bad2;
5687e2d9be6Schristos 	}
5697e2d9be6Schristos 
5707e2d9be6Schristos 	/*
5717e2d9be6Schristos 	 * Allocate a cluster to hold the about to be created directory.
5727e2d9be6Schristos 	 */
5738086f46eSthorpej 	error = msdosfs_clusteralloc(pmp, 0, 1, &newcluster, NULL);
5747e2d9be6Schristos 	if (error)
5757e2d9be6Schristos 		goto bad2;
5767e2d9be6Schristos 
5777e2d9be6Schristos 	memset(&ndirent, 0, sizeof(ndirent));
5787e2d9be6Schristos 	ndirent.de_pmp = pmp;
5797e2d9be6Schristos 	ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
5807e2d9be6Schristos 	msdosfs_times(pmp, &ndirent, st);
5817e2d9be6Schristos 
5827e2d9be6Schristos 	/*
5837e2d9be6Schristos 	 * Now fill the cluster with the "." and ".." entries. And write
5847e2d9be6Schristos 	 * the cluster to disk.	 This way it is there for the parent
5857e2d9be6Schristos 	 * directory to be pointing at if there were a crash.
5867e2d9be6Schristos 	 */
5877e2d9be6Schristos 	bn = cntobn(pmp, newcluster);
5887e2d9be6Schristos 	lbn = de_bn2kb(pmp, bn);
5891df38ec6Schristos 	DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__, newcluster,
5901df38ec6Schristos 	    bn, lbn));
5917e2d9be6Schristos 	/* always succeeds */
5927e2d9be6Schristos 	bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0);
5937e2d9be6Schristos 	memset(bp->b_data, 0, pmp->pm_bpcluster);
5947e2d9be6Schristos 	memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate);
5957e2d9be6Schristos 	denp = (struct direntry *)bp->b_data;
5967e2d9be6Schristos 	putushort(denp[0].deStartCluster, newcluster);
5977e2d9be6Schristos 	putushort(denp[0].deCDate, ndirent.de_CDate);
5987e2d9be6Schristos 	putushort(denp[0].deCTime, ndirent.de_CTime);
5997e2d9be6Schristos 	denp[0].deCHundredth = ndirent.de_CHun;
6007e2d9be6Schristos 	putushort(denp[0].deADate, ndirent.de_ADate);
6017e2d9be6Schristos 	putushort(denp[0].deMDate, ndirent.de_MDate);
6027e2d9be6Schristos 	putushort(denp[0].deMTime, ndirent.de_MTime);
6037e2d9be6Schristos 	pcl = pdep->de_StartCluster;
6045f60de37Schristos 	DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl,
6055f60de37Schristos 	    pmp->pm_rootdirblk));
6067e2d9be6Schristos 	if (FAT32(pmp) && pcl == pmp->pm_rootdirblk)
6077e2d9be6Schristos 		pcl = 0;
6087e2d9be6Schristos 	putushort(denp[1].deStartCluster, pcl);
6097e2d9be6Schristos 	putushort(denp[1].deCDate, ndirent.de_CDate);
6107e2d9be6Schristos 	putushort(denp[1].deCTime, ndirent.de_CTime);
6117e2d9be6Schristos 	denp[1].deCHundredth = ndirent.de_CHun;
6127e2d9be6Schristos 	putushort(denp[1].deADate, ndirent.de_ADate);
6137e2d9be6Schristos 	putushort(denp[1].deMDate, ndirent.de_MDate);
6147e2d9be6Schristos 	putushort(denp[1].deMTime, ndirent.de_MTime);
6157e2d9be6Schristos 	if (FAT32(pmp)) {
6167e2d9be6Schristos 		putushort(denp[0].deHighClust, newcluster >> 16);
6177e2d9be6Schristos 		putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16);
6187e2d9be6Schristos 	} else {
6197e2d9be6Schristos 		putushort(denp[0].deHighClust, 0);
6207e2d9be6Schristos 		putushort(denp[1].deHighClust, 0);
6217e2d9be6Schristos 	}
6227e2d9be6Schristos 
6237e2d9be6Schristos 	if ((error = bwrite(bp)) != 0)
6247e2d9be6Schristos 		goto bad;
6257e2d9be6Schristos 
6267e2d9be6Schristos 	/*
6277e2d9be6Schristos 	 * Now build up a directory entry pointing to the newly allocated
6287e2d9be6Schristos 	 * cluster.  This will be written to an empty slot in the parent
6297e2d9be6Schristos 	 * directory.
6307e2d9be6Schristos 	 */
6318086f46eSthorpej 	if ((error = msdosfs_uniqdosname(pdep, &cn, ndirent.de_Name)) != 0)
6327e2d9be6Schristos 		goto bad;
6337e2d9be6Schristos 
6347e2d9be6Schristos 	ndirent.de_Attributes = ATTR_DIRECTORY;
6357e2d9be6Schristos 	ndirent.de_StartCluster = newcluster;
6367e2d9be6Schristos 	ndirent.de_FileSize = 0;
6377e2d9be6Schristos 	ndirent.de_dev = pdep->de_dev;
6387e2d9be6Schristos 	ndirent.de_devvp = pdep->de_devvp;
6395f60de37Schristos 	ndirent.de_pmp = pdep->de_pmp;
640804eae6bSchristos 	if ((error = msdosfs_findslot(pdep, &cn)) != 0)
641804eae6bSchristos 		goto bad;
6428086f46eSthorpej 	if ((error = msdosfs_createde(&ndirent, pdep, &pdep->de_crap, &dep,
6438086f46eSthorpej 	    &cn)) != 0)
6447e2d9be6Schristos 		goto bad;
6451df38ec6Schristos 	if ((error = msdosfs_updatede(dep)) != 0)
6461df38ec6Schristos 		goto bad;
6477e2d9be6Schristos 	return dep;
6487e2d9be6Schristos 
6497e2d9be6Schristos bad:
6508086f46eSthorpej 	msdosfs_clusterfree(pmp, newcluster, NULL);
6517e2d9be6Schristos bad2:
6527e2d9be6Schristos 	errno = error;
6537e2d9be6Schristos 	return NULL;
6547e2d9be6Schristos }
655