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