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