Index: usr.sbin/makefs/msdos.h =================================================================== --- /dev/null +++ usr.sbin/makefs/msdos.h @@ -0,0 +1,39 @@ +/* $NetBSD: msdos.h,v 1.3 2015/10/16 16:40:02 christos Exp $ */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +struct vnode; +struct denode; + +struct msdosfsmount *msdosfs_mount(struct vnode *, int); +int msdosfs_root(struct msdosfsmount *, struct vnode *); + +struct denode *msdosfs_mkfile(const char *, struct denode *, fsnode *); +struct denode *msdosfs_mkdire(const char *, struct denode *, fsnode *); Index: usr.sbin/makefs/msdos.c =================================================================== --- /dev/null +++ usr.sbin/makefs/msdos.c @@ -0,0 +1,272 @@ +/* $NetBSD: msdos.c,v 1.20 2017/04/14 15:40:35 christos Exp $ */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: msdos.c,v 1.20 2017/04/14 15:40:35 christos Exp $"); +#endif /* !__lint */ + +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "makefs.h" +#include "msdos.h" +#include "mkfs_msdos.h" + +static int msdos_populate_dir(const char *, struct denode *, fsnode *, + fsnode *, fsinfo_t *); + +struct msdos_options_ex { + struct msdos_options options; + bool utf8; +}; + +void +msdos_prep_opts(fsinfo_t *fsopts) +{ + struct msdos_options_ex *msdos_opt = ecalloc(1, sizeof(*msdos_opt)); + const option_t msdos_options[] = { +#define AOPT(_opt, _type, _name, _min, _desc) { \ + .letter = _opt, \ + .name = # _name, \ + .type = _min == -1 ? OPT_STRPTR : \ + (_min == -2 ? OPT_BOOL : \ + (sizeof(_type) == 1 ? OPT_INT8 : \ + (sizeof(_type) == 2 ? OPT_INT16 : \ + (sizeof(_type) == 4 ? OPT_INT32 : OPT_INT64)))), \ + .value = &msdos_opt->options._name, \ + .minimum = _min, \ + .maximum = sizeof(_type) == 1 ? 0xff : \ + (sizeof(_type) == 2 ? 0xffff : \ + (sizeof(_type) == 4 ? 0xffffffff : 0xffffffffffffffffLL)), \ + .desc = _desc, \ +}, +ALLOPTS +#undef AOPT + { 'U', "utf8", &msdos_opt->utf8, OPT_BOOL, + 0, 1, "Use UTF8 names" }, + { .name = NULL } + }; + + fsopts->fs_specific = msdos_opt; + fsopts->fs_options = copy_opts(msdos_options); +} + +void +msdos_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_specific); + free(fsopts->fs_options); +} + +int +msdos_parse_opts(const char *option, fsinfo_t *fsopts) +{ + struct msdos_options *msdos_opt = fsopts->fs_specific; + option_t *msdos_options = fsopts->fs_options; + + int rv; + + assert(option != NULL); + assert(fsopts != NULL); + assert(msdos_opt != NULL); + + if (debug & DEBUG_FS_PARSE_OPTS) + printf("msdos_parse_opts: got `%s'\n", option); + + rv = set_option(msdos_options, option, NULL, 0); + if (rv == -1) + return rv; + + if (strcmp(msdos_options[rv].name, "volume_id") == 0) + msdos_opt->volume_id_set = 1; + else if (strcmp(msdos_options[rv].name, "media_descriptor") == 0) + msdos_opt->media_descriptor_set = 1; + else if (strcmp(msdos_options[rv].name, "hidden_sectors") == 0) + msdos_opt->hidden_sectors_set = 1; + + if (stampst.st_ino) { + msdos_opt->timestamp_set = 1; + msdos_opt->timestamp = stampst.st_mtime; + } + + return 1; +} + + +void +msdos_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct msdos_options_ex *msdos_opt = fsopts->fs_specific; + struct vnode vp, rootvp; + struct timeval start; + struct msdosfsmount *pmp; + uint32_t flags; + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + fsopts->size = fsopts->maxsize; + msdos_opt->options.create_size = MAX(msdos_opt->options.create_size, + fsopts->offset + fsopts->size); + msdos_opt->options.offset = fsopts->offset; + if (msdos_opt->options.bytes_per_sector == 0) { + if (fsopts->sectorsize == -1) + fsopts->sectorsize = 512; + msdos_opt->options.bytes_per_sector = fsopts->sectorsize; + } else if (fsopts->sectorsize == -1) { + fsopts->sectorsize = msdos_opt->options.bytes_per_sector; + } else if (fsopts->sectorsize != msdos_opt->options.bytes_per_sector) { + err(1, "inconsistent sectorsize -S %u" + "!= -o bytes_per_sector %u", + fsopts->sectorsize, msdos_opt->options.bytes_per_sector); + } + + /* create image */ + printf("Creating `%s'\n", image); + TIMER_START(start); + if (mkfs_msdos(image, NULL, &msdos_opt->options) == -1) + return; + TIMER_RESULTS(start, "mkfs_msdos"); + + fsopts->fd = open(image, O_RDWR); + vp.fs = fsopts; + + flags = 0; + if (msdos_opt->utf8) + flags |= MSDOSFSMNT_UTF8; + + if ((pmp = msdosfs_mount(&vp, flags)) == NULL) + err(1, "msdosfs_mount"); + + if (msdosfs_root(pmp, &rootvp) != 0) + err(1, "msdosfs_root"); + + if (debug & DEBUG_FS_MAKEFS) + printf("msdos_makefs: image %s directory %s root %p\n", + image, dir, root); + + /* populate image */ + printf("Populating `%s'\n", image); + TIMER_START(start); + if (msdos_populate_dir(dir, VTODE(&rootvp), root, root, fsopts) == -1) + errx(1, "Image file `%s' not created.", image); + TIMER_RESULTS(start, "msdos_populate_dir"); + + if (debug & DEBUG_FS_MAKEFS) + putchar('\n'); + + /* ensure no outstanding buffers remain */ + if (debug & DEBUG_FS_MAKEFS) + bcleanup(); + + printf("Image `%s' complete\n", image); +} + +static int +msdos_populate_dir(const char *path, struct denode *dir, fsnode *root, + fsnode *parent, fsinfo_t *fsopts) +{ + fsnode *cur; + char pbuf[MAXPATHLEN]; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + for (cur = root->next; cur != NULL; cur = cur->next) { + if ((size_t)snprintf(pbuf, sizeof(pbuf), "%s/%s", path, + cur->name) >= sizeof(pbuf)) { + warnx("path %s too long", pbuf); + return -1; + } + + if ((cur->inode->flags & FI_ALLOCATED) == 0) { + cur->inode->flags |= FI_ALLOCATED; + if (cur != root) { + fsopts->curinode++; + cur->inode->ino = fsopts->curinode; + cur->parent = parent; + } + } + + if (cur->inode->flags & FI_WRITTEN) { + continue; // hard link + } + cur->inode->flags |= FI_WRITTEN; + + if (cur->child) { + struct denode *de; + if ((de = msdosfs_mkdire(pbuf, dir, cur)) == NULL) { + warn("msdosfs_mkdire %s", pbuf); + return -1; + } + if (msdos_populate_dir(pbuf, de, cur->child, cur, + fsopts) == -1) { + warn("msdos_populate_dir %s", pbuf); + return -1; + } + continue; + } else if (!S_ISREG(cur->type)) { + warnx("skipping non-regular file %s/%s", cur->path, + cur->name); + continue; + } + if (msdosfs_mkfile(pbuf, dir, cur) == NULL) { + warn("msdosfs_mkfile %s", pbuf); + return -1; + } + } + return 0; +} Index: usr.sbin/makefs/msdos/Makefile.inc =================================================================== --- /dev/null +++ usr.sbin/makefs/msdos/Makefile.inc @@ -0,0 +1,16 @@ +# $NetBSD: Makefile.inc,v 1.7 2016/02/06 10:40:58 mlelstv Exp $ +# + +MSDOS= ${NETBSDSRCDIR}/sys/fs/msdosfs +MSDOS_NEWFS= ${NETBSDSRCDIR}/sbin/newfs_msdos + +.PATH: ${.CURDIR}/msdos ${MSDOS} ${MSDOS_NEWFS} ${NETBSDSRCDIR}/sys/dev + +CPPFLAGS+= -DMSDOS_EI -I${MSDOS} -I${MSDOS_NEWFS} +.if !defined(HOSTPROGNAME) +CPPFLAGS+= -I${NETBSDSRCDIR}/sys +.endif + +SRCS+= mkfs_msdos.c msdosfs_fat.c msdosfs_conv.c msdosfs_vfsops.c +SRCS+= msdosfs_lookup.c msdosfs_denode.c msdosfs_vnops.c msdosfs_unicode.c +SRCS+= clock_subr.c Index: usr.sbin/makefs/msdos/msdosfs_denode.c =================================================================== --- /dev/null +++ usr.sbin/makefs/msdos/msdosfs_denode.c @@ -0,0 +1,363 @@ +/* $NetBSD: msdosfs_denode.c,v 1.7 2015/03/29 05:52:59 agc Exp $ */ + +/*- + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Written by Paul Popelka (paulp@uts.amdahl.com) + * + * You can do anything you want with this software, just don't say you wrote + * it, and don't remove this notice. + * + * This software is provided "as is". + * + * The author supplies this software to be publicly redistributed on the + * understanding that the author is not responsible for the correct + * functioning of this software in any circumstances and is not liable for + * any damages caused by this software. + * + * October 1992 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.7 2015/03/29 05:52:59 agc Exp $"); + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +/* + * If deget() succeeds it returns with the gotten denode locked(). + * + * pmp - address of msdosfsmount structure of the filesystem containing + * the denode of interest. The pm_dev field and the address of + * the msdosfsmount structure are used. + * dirclust - which cluster bp contains, if dirclust is 0 (root directory) + * diroffset is relative to the beginning of the root directory, + * otherwise it is cluster relative. + * diroffset - offset past begin of cluster of denode we want + * depp - returns the address of the gotten denode. + */ +int +deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset, + struct denode **depp) + /* pmp: so we know the maj/min number */ + /* dirclust: cluster this dir entry came from */ + /* diroffset: index of entry within the cluster */ + /* depp: returns the addr of the gotten denode */ +{ + int error; + struct direntry *direntptr; + struct denode *ldep; + struct buf *bp; + +#ifdef MSDOSFS_DEBUG + printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n", + pmp, dirclust, diroffset, depp); +#endif + + /* + * On FAT32 filesystems, root is a (more or less) normal + * directory + */ + if (FAT32(pmp) && dirclust == MSDOSFSROOT) + dirclust = pmp->pm_rootdirblk; + + ldep = ecalloc(1, sizeof(*ldep)); + ldep->de_vnode = NULL; + ldep->de_flag = 0; + ldep->de_devvp = 0; + ldep->de_lockf = 0; + ldep->de_dev = pmp->pm_dev; + ldep->de_dirclust = dirclust; + ldep->de_diroffset = diroffset; + ldep->de_pmp = pmp; + ldep->de_devvp = pmp->pm_devvp; + ldep->de_refcnt = 1; + fc_purge(ldep, 0); + /* + * Copy the directory entry into the denode area of the vnode. + */ + if ((dirclust == MSDOSFSROOT + || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)) + && diroffset == MSDOSFSROOT_OFS) { + /* + * Directory entry for the root directory. There isn't one, + * so we manufacture one. We should probably rummage + * through the root directory and find a label entry (if it + * exists), and then use the time and date from that entry + * as the time and date for the root denode. + */ + ldep->de_vnode = (struct vnode *)-1; + + ldep->de_Attributes = ATTR_DIRECTORY; + if (FAT32(pmp)) + ldep->de_StartCluster = pmp->pm_rootdirblk; + /* de_FileSize will be filled in further down */ + else { + ldep->de_StartCluster = MSDOSFSROOT; + ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec; + } + /* + * fill in time and date so that dos2unixtime() doesn't + * spit up when called from msdosfs_getattr() with root + * denode + */ + ldep->de_CHun = 0; + ldep->de_CTime = 0x0000; /* 00:00:00 */ + ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) + | (1 << DD_DAY_SHIFT); + /* Jan 1, 1980 */ + ldep->de_ADate = ldep->de_CDate; + ldep->de_MTime = ldep->de_CTime; + ldep->de_MDate = ldep->de_CDate; + /* leave the other fields as garbage */ + } else { + error = readep(pmp, dirclust, diroffset, &bp, &direntptr); + if (error) { + ldep->de_devvp = NULL; + ldep->de_Name[0] = SLOT_DELETED; + return (error); + } + DE_INTERNALIZE(ldep, direntptr); + brelse(bp, 0); + } + + /* + * Fill in a few fields of the vnode and finish filling in the + * denode. Then return the address of the found denode. + */ + if (ldep->de_Attributes & ATTR_DIRECTORY) { + /* + * Since DOS directory entries that describe directories + * have 0 in the filesize field, we take this opportunity + * to find out the length of the directory and plug it into + * the denode structure. + */ + u_long size; + + if (ldep->de_StartCluster != MSDOSFSROOT) { + error = pcbmap(ldep, CLUST_END, 0, &size, 0); + if (error == E2BIG) { + ldep->de_FileSize = de_cn2off(pmp, size); + error = 0; + } else + printf("deget(): pcbmap returned %d\n", error); + } + } + *depp = ldep; + return (0); +} + +/* + * Truncate the file described by dep to the length specified by length. + */ +int +detrunc(struct denode *dep, u_long length, int flags, struct kauth_cred *cred) +{ + int error; + int allerror = 0; + u_long eofentry; + u_long chaintofree = 0; + daddr_t bn, lastblock; + int boff; + int isadir = dep->de_Attributes & ATTR_DIRECTORY; + struct buf *bp; + struct msdosfsmount *pmp = dep->de_pmp; + +#ifdef MSDOSFS_DEBUG + printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags); +#endif + + /* + * Disallow attempts to truncate the root directory since it is of + * fixed size. That's just the way dos filesystems are. We use + * the VROOT bit in the vnode because checking for the directory + * bit and a startcluster of 0 in the denode is not adequate to + * recognize the root directory at this point in a file or + * directory's life. + */ + if (dep->de_vnode != NULL && !FAT32(pmp)) { + printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n", + dep->de_dirclust, dep->de_diroffset); + return (EINVAL); + } + + if (dep->de_FileSize < length) + return (deextend(dep, length, cred)); + lastblock = de_clcount(pmp, length) - 1; + + /* + * If the desired length is 0 then remember the starting cluster of + * the file and set the StartCluster field in the directory entry + * to 0. If the desired length is not zero, then get the number of + * the last cluster in the shortened file. Then get the number of + * the first cluster in the part of the file that is to be freed. + * Then set the next cluster pointer in the last cluster of the + * file to CLUST_EOFE. + */ + if (length == 0) { + chaintofree = dep->de_StartCluster; + dep->de_StartCluster = 0; + eofentry = ~0; + } else { + error = pcbmap(dep, lastblock, 0, &eofentry, 0); + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): pcbmap fails %d\n", error); +#endif + return (error); + } + } + + /* + * If the new length is not a multiple of the cluster size then we + * must zero the tail end of the new last cluster in case it + * becomes part of the file again because of a seek. + */ + if ((boff = length & pmp->pm_crbomask) != 0) { + if (isadir) { + bn = cntobn(pmp, eofentry); + error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), + pmp->pm_bpcluster, B_MODIFY, &bp); + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): bread fails %d\n", error); +#endif + return (error); + } + memset((char *)bp->b_data + boff, 0, + pmp->pm_bpcluster - boff); + if (flags & IO_SYNC) + bwrite(bp); + else + bdwrite(bp); + } + } + + /* + * Write out the updated directory entry. Even if the update fails + * we free the trailing clusters. + */ + dep->de_FileSize = length; + if (!isadir) + dep->de_flag |= DE_UPDATE|DE_MODIFIED; +#ifdef MSDOSFS_DEBUG + printf("detrunc(): allerror %d, eofentry %lu\n", + allerror, eofentry); +#endif + + /* + * If we need to break the cluster chain for the file then do it + * now. + */ + if (eofentry != (u_long)~0) { + error = fatentry(FAT_GET_AND_SET, pmp, eofentry, + &chaintofree, CLUST_EOFE); + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): fatentry errors %d\n", error); +#endif + return (error); + } + } + + /* + * Now free the clusters removed from the file because of the + * truncation. + */ + if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask)) + freeclusterchain(pmp, chaintofree); + + return (allerror); +} + +/* + * Extend the file described by dep to length specified by length. + */ +int +deextend(struct denode *dep, u_long length, struct kauth_cred *cred) +{ + struct msdosfsmount *pmp = dep->de_pmp; + u_long count; + int error; + + /* + * The root of a DOS filesystem cannot be extended. + */ + if (dep->de_vnode != NULL && !FAT32(pmp)) + return EINVAL; + + /* + * Directories cannot be extended. + */ + if (dep->de_Attributes & ATTR_DIRECTORY) + return EISDIR; + + if (length <= dep->de_FileSize) + return E2BIG; + + /* + * Compute the number of clusters to allocate. + */ + count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); + if (count > 0) { + if (count > pmp->pm_freeclustercount) + return (ENOSPC); + error = extendfile(dep, count, NULL, NULL, DE_CLEAR); + if (error) { + /* truncate the added clusters away again */ + (void) detrunc(dep, dep->de_FileSize, 0, cred); + return (error); + } + } + + /* + * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a + * memset(); we set the write size so ubc won't read in file data that + * is zero'd later. + */ + dep->de_FileSize = length; + dep->de_flag |= DE_UPDATE|DE_MODIFIED; + return 0; +} Index: usr.sbin/makefs/msdos/msdosfs_vfsops.c =================================================================== --- /dev/null +++ usr.sbin/makefs/msdos/msdosfs_vfsops.c @@ -0,0 +1,431 @@ +/*- + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Written by Paul Popelka (paulp@uts.amdahl.com) + * + * You can do anything you want with this software, just don't say you wrote + * it, and don't remove this notice. + * + * This software is provided "as is". + * + * The author supplies this software to be publicly redistributed on the + * understanding that the author is not responsible for the correct + * functioning of this software in any circumstances and is not liable for + * any damages caused by this software. + * + * October 1992 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.10 2016/01/30 09:59:27 mlelstv Exp $"); + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "msdos.h" +#include "mkfs_msdos.h" + +#ifdef MSDOSFS_DEBUG +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif + +struct msdosfsmount * +msdosfs_mount(struct vnode *devvp, int flags) +{ + struct msdosfsmount *pmp = NULL; + struct buf *bp; + union bootsector *bsp; + struct byte_bpb33 *b33; + struct byte_bpb50 *b50; + struct byte_bpb710 *b710; + uint8_t SecPerClust; + int ronly = 0, error, tmp; + int bsize; + struct msdos_options *m = devvp->fs->fs_specific; + uint64_t psize = m->create_size; + unsigned secsize = 512; + + DPRINTF(("%s(bread 0)\n", __func__)); + if ((error = bread(devvp, 0, secsize, 0, &bp)) != 0) + goto error_exit; + + bsp = (union bootsector *)bp->b_data; + b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; + b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; + b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; + + if (!(flags & MSDOSFSMNT_GEMDOSFS)) { + if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 + || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { + DPRINTF(("bootsig0 %d bootsig1 %d\n", + bsp->bs50.bsBootSectSig0, + bsp->bs50.bsBootSectSig1)); + error = EINVAL; + goto error_exit; + } + bsize = 0; + } else + bsize = 512; + + pmp = ecalloc(1, sizeof *pmp); + /* + * Compute several useful quantities from the bpb in the + * bootsector. Copy in the dos 5 variant of the bpb then fix up + * the fields that are different between dos 5 and dos 3.3. + */ + SecPerClust = b50->bpbSecPerClust; + pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); + pmp->pm_ResSectors = getushort(b50->bpbResSectors); + pmp->pm_FATs = b50->bpbFATs; + pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); + pmp->pm_Sectors = getushort(b50->bpbSectors); + pmp->pm_FATsecs = getushort(b50->bpbFATsecs); + pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); + pmp->pm_Heads = getushort(b50->bpbHeads); + pmp->pm_Media = b50->bpbMedia; + + DPRINTF(("%s(BytesPerSec=%u, ResSectors=%u, FATs=%d, RootDirEnts=%u, " + "Sectors=%u, FATsecs=%lu, SecPerTrack=%u, Heads=%u, Media=%u)\n", + __func__, pmp->pm_BytesPerSec, pmp->pm_ResSectors, pmp->pm_FATs, + pmp->pm_RootDirEnts, pmp->pm_Sectors, pmp->pm_FATsecs, + pmp->pm_SecPerTrack, pmp->pm_Heads, pmp->pm_Media)); + if (!(flags & MSDOSFSMNT_GEMDOSFS)) { + /* XXX - We should probably check more values here */ + if (!pmp->pm_BytesPerSec || !SecPerClust + || pmp->pm_SecPerTrack > 63) { + DPRINTF(("bytespersec %d secperclust %d " + "secpertrack %d\n", + pmp->pm_BytesPerSec, SecPerClust, + pmp->pm_SecPerTrack)); + error = EINVAL; + goto error_exit; + } + } + + pmp->pm_flags = flags & MSDOSFSMNT_MNTOPT; + if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS) + pmp->pm_flags |= MSDOSFSMNT_NOWIN95; + if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) + pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; + + if (pmp->pm_Sectors == 0) { + pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); + pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); + } else { + pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); + pmp->pm_HugeSectors = pmp->pm_Sectors; + } + + if (pmp->pm_RootDirEnts == 0) { + unsigned short vers = getushort(b710->bpbFSVers); + /* + * Some say that bsBootSectSig[23] must be zero, but + * Windows does not require this and some digital cameras + * do not set these to zero. Therefore, do not insist. + */ + if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) { + DPRINTF(("sectors %d fatsecs %lu vers %d\n", + pmp->pm_Sectors, pmp->pm_FATsecs, vers)); + error = EINVAL; + goto error_exit; + } + pmp->pm_fatmask = FAT32_MASK; + pmp->pm_fatmult = 4; + pmp->pm_fatdiv = 1; + pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); + + /* mirrorring is enabled if the FATMIRROR bit is not set */ + if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0) + pmp->pm_flags |= MSDOSFS_FATMIRROR; + else + pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; + } else + pmp->pm_flags |= MSDOSFS_FATMIRROR; + + if (flags & MSDOSFSMNT_GEMDOSFS) { + if (FAT32(pmp)) { + DPRINTF(("FAT32 for GEMDOS\n")); + /* + * GEMDOS doesn't know FAT32. + */ + error = EINVAL; + goto error_exit; + } + + /* + * Check a few values (could do some more): + * - logical sector size: power of 2, >= block size + * - sectors per cluster: power of 2, >= 1 + * - number of sectors: >= 1, <= size of partition + */ + if ( (SecPerClust == 0) + || (SecPerClust & (SecPerClust - 1)) + || (pmp->pm_BytesPerSec < bsize) + || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) + || (pmp->pm_HugeSectors == 0) + || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize) + > psize)) { + DPRINTF(("consistency checks for GEMDOS\n")); + error = EINVAL; + goto error_exit; + } + /* + * XXX - Many parts of the msdosfs driver seem to assume that + * the number of bytes per logical sector (BytesPerSec) will + * always be the same as the number of bytes per disk block + * Let's pretend it is. + */ + tmp = pmp->pm_BytesPerSec / bsize; + pmp->pm_BytesPerSec = bsize; + pmp->pm_HugeSectors *= tmp; + pmp->pm_HiddenSects *= tmp; + pmp->pm_ResSectors *= tmp; + pmp->pm_Sectors *= tmp; + pmp->pm_FATsecs *= tmp; + SecPerClust *= tmp; + } + + /* Check that fs has nonzero FAT size */ + if (pmp->pm_FATsecs == 0) { + DPRINTF(("FATsecs is 0\n")); + error = EINVAL; + goto error_exit; + } + + pmp->pm_fatblk = pmp->pm_ResSectors; + if (FAT32(pmp)) { + pmp->pm_rootdirblk = getulong(b710->bpbRootClust); + pmp->pm_firstcluster = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_fsinfo = getushort(b710->bpbFSInfo); + } else { + pmp->pm_rootdirblk = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) + + pmp->pm_BytesPerSec - 1) + / pmp->pm_BytesPerSec;/* in sectors */ + pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; + } + + pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / + SecPerClust; + pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; + pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; + + if (flags & MSDOSFSMNT_GEMDOSFS) { + if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) { + pmp->pm_fatmask = FAT12_MASK; + pmp->pm_fatmult = 3; + pmp->pm_fatdiv = 2; + } else { + pmp->pm_fatmask = FAT16_MASK; + pmp->pm_fatmult = 2; + pmp->pm_fatdiv = 1; + } + } else if (pmp->pm_fatmask == 0) { + if (pmp->pm_maxcluster + <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { + /* + * This will usually be a floppy disk. This size makes + * sure that one FAT entry will not be split across + * multiple blocks. + */ + pmp->pm_fatmask = FAT12_MASK; + pmp->pm_fatmult = 3; + pmp->pm_fatdiv = 2; + } else { + pmp->pm_fatmask = FAT16_MASK; + pmp->pm_fatmult = 2; + pmp->pm_fatdiv = 1; + } + } + if (FAT12(pmp)) + pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; + else + pmp->pm_fatblocksize = MAXBSIZE; + + pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec; + pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1; + + /* + * Compute mask and shift value for isolating cluster relative byte + * offsets and cluster numbers from a file offset. + */ + pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec; + pmp->pm_crbomask = pmp->pm_bpcluster - 1; + pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; + + DPRINTF(("%s(fatmask=%lu, fatmult=%u, fatdiv=%u, fatblocksize=%lu, " + "fatblocksec=%lu, bnshift=%lu, pbcluster=%lu, crbomask=%lu, " + "cnshift=%lu)\n", + __func__, pmp->pm_fatmask, pmp->pm_fatmult, pmp->pm_fatdiv, + pmp->pm_fatblocksize, pmp->pm_fatblocksec, pmp->pm_bnshift, + pmp->pm_bpcluster, pmp->pm_crbomask, pmp->pm_cnshift)); + /* + * Check for valid cluster size + * must be a power of 2 + */ + if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { + DPRINTF(("bpcluster %lu cnshift %lu\n", + pmp->pm_bpcluster, pmp->pm_cnshift)); + error = EINVAL; + goto error_exit; + } + + /* + * Release the bootsector buffer. + */ + brelse(bp, BC_AGE); + bp = NULL; + + /* + * Check FSInfo. + */ + if (pmp->pm_fsinfo) { + struct fsinfo *fp; + + /* + * XXX If the fsinfo block is stored on media with + * 2KB or larger sectors, is the fsinfo structure + * padded at the end or in the middle? + */ + DPRINTF(("%s(bread %lu)\n", __func__, + (unsigned long)de_bn2kb(pmp, pmp->pm_fsinfo))); + if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo), + pmp->pm_BytesPerSec, 0, &bp)) != 0) + goto error_exit; + fp = (struct fsinfo *)bp->b_data; + if (!memcmp(fp->fsisig1, "RRaA", 4) + && !memcmp(fp->fsisig2, "rrAa", 4) + && !memcmp(fp->fsisig3, "\0\0\125\252", 4) + && !memcmp(fp->fsisig4, "\0\0\125\252", 4)) + pmp->pm_nxtfree = getulong(fp->fsinxtfree); + else + pmp->pm_fsinfo = 0; + brelse(bp, 0); + bp = NULL; + } + + /* + * Check and validate (or perhaps invalidate?) the fsinfo structure? + * XXX + */ + if (pmp->pm_fsinfo) { + if ((pmp->pm_nxtfree == 0xffffffffUL) || + (pmp->pm_nxtfree > pmp->pm_maxcluster)) + pmp->pm_fsinfo = 0; + } + + /* + * Allocate memory for the bitmap of allocated clusters, and then + * fill it in. + */ + pmp->pm_inusemap = ecalloc(sizeof(*pmp->pm_inusemap), + ((pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS)); + /* + * fillinusemap() needs pm_devvp. + */ + pmp->pm_dev = 0; + pmp->pm_devvp = devvp; + + /* + * Have the inuse map filled in. + */ + if ((error = fillinusemap(pmp)) != 0) { + DPRINTF(("fillinusemap %d\n", error)); + goto error_exit; + } + + /* + * Finish up. + */ + if (ronly) + pmp->pm_flags |= MSDOSFSMNT_RONLY; + else + pmp->pm_fmod = 1; + + /* + * If we ever do quotas for DOS filesystems this would be a place + * to fill in the info in the msdosfsmount structure. You dolt, + * quotas on dos filesystems make no sense because files have no + * owners on dos filesystems. of course there is some empty space + * in the directory entry where we could put uid's and gid's. + */ + + return pmp; + +error_exit: + if (bp) + brelse(bp, BC_AGE); + if (pmp) { + if (pmp->pm_inusemap) + free(pmp->pm_inusemap); + free(pmp); + } + errno = error; + return NULL; +} + +int +msdosfs_root(struct msdosfsmount *pmp, struct vnode *vp) { + struct denode *ndep; + int error; + + *vp = *pmp->pm_devvp; + if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep)) != 0) { + errno = error; + return -1; + } + vp->v_data = ndep; + return 0; +} Index: usr.sbin/makefs/msdos/msdosfs_vnops.c =================================================================== --- /dev/null +++ usr.sbin/makefs/msdos/msdosfs_vnops.c @@ -0,0 +1,647 @@ +/* $NetBSD: msdosfs_vnops.c,v 1.19 2017/04/13 17:10:12 christos Exp $ */ + +/*- + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Written by Paul Popelka (paulp@uts.amdahl.com) + * + * You can do anything you want with this software, just don't say you wrote + * it, and don't remove this notice. + * + * This software is provided "as is". + * + * The author supplies this software to be publicly redistributed on the + * understanding that the author is not responsible for the correct + * functioning of this software in any circumstances and is not liable for + * any damages caused by this software. + * + * October 1992 + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.19 2017/04/13 17:10:12 christos Exp $"); + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "msdos.h" + +#ifdef MSDOSFS_DEBUG +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif +/* + * Some general notes: + * + * In the ufs filesystem the inodes, superblocks, and indirect blocks are + * read/written using the vnode for the filesystem. Blocks that represent + * the contents of a file are read/written using the vnode for the file + * (including directories when they are read/written as files). This + * presents problems for the dos filesystem because data that should be in + * an inode (if dos had them) resides in the directory itself. Since we + * must update directory entries without the benefit of having the vnode + * for the directory we must use the vnode for the filesystem. This means + * that when a directory is actually read/written (via read, write, or + * readdir, or seek) we must use the vnode for the filesystem instead of + * the vnode for the directory as would happen in ufs. This is to insure we + * retrieve the correct block from the buffer cache since the hash value is + * based upon the vnode address and the desired block number. + */ + +static int msdosfs_wfile(const char *, struct denode *, fsnode *); + +static void +msdosfs_times(struct msdosfsmount *pmp, struct denode *dep, + const struct stat *st) +{ + struct timespec at; + struct timespec mt; + + if (stampst.st_ino) + st = &stampst; + +#ifndef HAVE_NBTOOL_CONFIG_H + at = st->st_atimespec; + mt = st->st_mtimespec; +#else + at.tv_sec = st->st_atime; + at.tv_nsec = 0; + mt.tv_sec = st->st_mtime; + mt.tv_nsec = 0; +#endif + unix2dostime(&at, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL); + unix2dostime(&mt, pmp->pm_gmtoff, &dep->de_MDate, &dep->de_MTime, NULL); +} + +/* + * When we search a directory the blocks containing directory entries are + * read and examined. The directory entries contain information that would + * normally be in the inode of a unix filesystem. This means that some of + * a directory's contents may also be in memory resident denodes (sort of + * an inode). This can cause problems if we are searching while some other + * process is modifying a directory. To prevent one process from accessing + * incompletely modified directory information we depend upon being the + * sole owner of a directory block. bread/brelse provide this service. + * This being the case, when a process modifies a directory it must first + * acquire the disk block that contains the directory entry to be modified. + * Then update the disk block and the denode, and then write the disk block + * out to disk. This way disk blocks containing directory entries and in + * memory denode's will be in synch. + */ +static int +msdosfs_findslot(struct denode *dp, struct componentname *cnp) +{ + daddr_t bn; + int error; + int slotcount; + int slotoffset = 0; + int frcn; + u_long cluster; + int blkoff; + u_int diroff; + int blsize; + struct msdosfsmount *pmp; + struct buf *bp = 0; + struct direntry *dep; + u_char dosfilename[12]; + int wincnt = 1; + int chksum = -1, chksum_ok; + int olddos = 1; + + pmp = dp->de_pmp; + + switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, + cnp->cn_namelen, 0)) { + case 0: + return (EINVAL); + case 1: + break; + case 2: + wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1; + break; + case 3: + olddos = 0; + wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1; + break; + } + + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + wincnt = 1; + + /* + * Suppress search for slots unless creating + * file and at end of pathname, in which case + * we watch for a place to put the new file in + * case it doesn't already exist. + */ + slotcount = 0; + DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename)); + /* + * Search the directory pointed at by vdp for the name pointed at + * by cnp->cn_nameptr. + */ + /* + * The outer loop ranges over the clusters that make up the + * directory. Note that the root directory is different from all + * other directories. It has a fixed number of blocks that are not + * part of the pool of allocatable clusters. So, we treat it a + * little differently. The root directory starts at "cluster" 0. + */ + diroff = 0; + for (frcn = 0; diroff < dp->de_FileSize; frcn++) { + if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) { + if (error == E2BIG) + break; + return (error); + } + error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, + 0, &bp); + if (error) { + return (error); + } + for (blkoff = 0; blkoff < blsize; + blkoff += sizeof(struct direntry), + diroff += sizeof(struct direntry)) { + dep = (struct direntry *)((char *)bp->b_data + blkoff); + /* + * If the slot is empty and we are still looking + * for an empty then remember this one. If the + * slot is not empty then check to see if it + * matches what we are looking for. If the slot + * has never been filled with anything, then the + * remainder of the directory has never been used, + * so there is no point in searching it. + */ + if (dep->deName[0] == SLOT_EMPTY || + dep->deName[0] == SLOT_DELETED) { + /* + * Drop memory of previous long matches + */ + chksum = -1; + + if (slotcount < wincnt) { + slotcount++; + slotoffset = diroff; + } + if (dep->deName[0] == SLOT_EMPTY) { + brelse(bp, 0); + goto notfound; + } + } else { + /* + * If there wasn't enough space for our + * winentries, forget about the empty space + */ + if (slotcount < wincnt) + slotcount = 0; + + /* + * Check for Win95 long filename entry + */ + if (dep->deAttributes == ATTR_WIN95) { + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + continue; + + chksum = winChkName((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen, + (struct winentry *)dep, + chksum, + pmp->pm_flags & MSDOSFSMNT_UTF8); + continue; + } + + /* + * Ignore volume labels (anywhere, not just + * the root directory). + */ + if (dep->deAttributes & ATTR_VOLUME) { + chksum = -1; + continue; + } + + /* + * Check for a checksum or name match + */ + chksum_ok = (chksum == winChksum(dep->deName)); + if (!chksum_ok + && (!olddos || memcmp(dosfilename, dep->deName, 11))) { + chksum = -1; + continue; + } + DPRINTF(("%s(): match blkoff %d, diroff %d\n", + __func__, blkoff, diroff)); + /* + * Remember where this directory + * entry came from for whoever did + * this lookup. + */ + dp->de_fndoffset = diroff; + dp->de_fndcnt = 0; + + return EEXIST; + } + } /* for (blkoff = 0; .... */ + /* + * Release the buffer holding the directory cluster just + * searched. + */ + brelse(bp, 0); + } /* for (frcn = 0; ; frcn++) */ + +notfound: + /* + * We hold no disk buffers at this point. + */ + + /* + * If we get here we didn't find the entry we were looking for. But + * that's ok if we are creating or renaming and are at the end of + * the pathname and the directory hasn't been removed. + */ + DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n", + __func__, dp->de_refcnt, slotcount, slotoffset)); + /* + * Fixup the slot description to point to the place where + * we might put the new DOS direntry (putting the Win95 + * long name entries before that) + */ + if (!slotcount) { + slotcount = 1; + slotoffset = diroff; + } + if (wincnt > slotcount) { + slotoffset += sizeof(struct direntry) * (wincnt - slotcount); + } + + /* + * Return an indication of where the new directory + * entry should be put. + */ + dp->de_fndoffset = slotoffset; + dp->de_fndcnt = wincnt - 1; + + /* + * We return with the directory locked, so that + * the parameters we set up above will still be + * valid if we actually decide to do a direnter(). + * We return ni_vp == NULL to indicate that the entry + * does not currently exist; we leave a pointer to + * the (locked) directory inode in ndp->ni_dvp. + * + * NB - if the directory is unlocked, then this + * information cannot be used. + */ + return 0; +} + +/* + * Create a regular file. On entry the directory to contain the file being + * created is locked. We must release before we return. + */ +struct denode * +msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node) +{ + struct componentname cn; + struct denode ndirent; + struct denode *dep; + int error; + struct stat *st = &node->inode->st; + struct msdosfsmount *pmp = pdep->de_pmp; + + cn.cn_nameptr = node->name; + cn.cn_namelen = strlen(node->name); + + DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name, + st->st_mode, (size_t)st->st_size)); + + /* + * If this is the root directory and there is no space left we + * can't do anything. This is because the root directory can not + * change size. + */ + if (pdep->de_StartCluster == MSDOSFSROOT + && pdep->de_fndoffset >= pdep->de_FileSize) { + error = ENOSPC; + goto bad; + } + + /* + * Create a directory entry for the file, then call createde() to + * have it installed. NOTE: DOS files are always executable. We + * use the absence of the owner write bit to make the file + * readonly. + */ + memset(&ndirent, 0, sizeof(ndirent)); + if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) + goto bad; + + ndirent.de_Attributes = (st->st_mode & S_IWUSR) ? + ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; + ndirent.de_StartCluster = 0; + ndirent.de_FileSize = 0; + ndirent.de_dev = pdep->de_dev; + ndirent.de_devvp = pdep->de_devvp; + ndirent.de_pmp = pdep->de_pmp; + ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; + msdosfs_times(pmp, &ndirent, st); + if ((error = msdosfs_findslot(pdep, &cn)) != 0) + goto bad; + if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0) + goto bad; + if ((error = msdosfs_wfile(path, dep, node)) != 0) + goto bad; + return dep; + +bad: + errno = error; + return NULL; +} +static int +msdosfs_updatede(struct denode *dep) +{ + struct buf *bp; + struct direntry *dirp; + int error; + + dep->de_flag &= ~DE_MODIFIED; + error = readde(dep, &bp, &dirp); + if (error) + return error; + DE_EXTERNALIZE(dirp, dep); + error = bwrite(bp); + return error; +} + +/* + * Write data to a file or directory. + */ +static int +msdosfs_wfile(const char *path, struct denode *dep, fsnode *node) +{ + int error, fd; + size_t osize = dep->de_FileSize; + struct stat *st = &node->inode->st; + size_t nsize, offs; + struct msdosfsmount *pmp = dep->de_pmp; + struct buf *bp; + char *dat; + u_long cn = 0; + + error = 0; /* XXX: gcc/vax */ + DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__, + dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster)); + if (st->st_size == 0) + return 0; + + /* Don't bother to try to write files larger than the fs limit */ + if (st->st_size > MSDOSFS_FILESIZE_MAX) + return EFBIG; + + nsize = st->st_size; + DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize)); + if (nsize > osize) { + if ((error = deextend(dep, nsize, NULL)) != 0) + return error; + if ((error = msdosfs_updatede(dep)) != 0) + return error; + } + + if ((fd = open(path, O_RDONLY)) == -1) { + error = errno; + DPRINTF((1, "open %s: %s", path, strerror(error))); + return error; + } + + if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)) + == MAP_FAILED) { + error = errno; + DPRINTF(("%s: mmap %s: %s", __func__, node->name, + strerror(error))); + close(fd); + goto out; + } + close(fd); + + for (offs = 0; offs < nsize;) { + int blsize, cpsize; + daddr_t bn; + u_long on = offs & pmp->pm_crbomask; +#ifdef HACK + cn = dep->de_StartCluster; + if (cn == MSDOSFSROOT) { + DPRINTF(("%s: bad lbn %lu", __func__, cn)); + error = EINVAL; + goto out; + } + bn = cntobn(pmp, cn); + blsize = pmp->pm_bpcluster; +#else + if ((error = pcbmap(dep, cn++, &bn, NULL, &blsize)) != 0) { + DPRINTF(("%s: pcbmap %lu", __func__, bn)); + goto out; + } +#endif + DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__, + cn, (unsigned long long)bn, + (unsigned long long)de_bn2kb(pmp, bn), blsize)); + if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, + 0, &bp)) != 0) { + DPRINTF(("bread %d\n", error)); + goto out; + } + cpsize = MIN((nsize - offs), blsize - on); + memcpy((char *)bp->b_data + on, dat + offs, cpsize); + bwrite(bp); + offs += cpsize; + } + + munmap(dat, nsize); + return 0; +out: + munmap(dat, nsize); + return error; +} + + +static const struct { + struct direntry dot; + struct direntry dotdot; +} dosdirtemplate = { + { ". ", " ", /* the . entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, /* reserved */ + 0, { 0, 0 }, { 0, 0 }, /* create time & date */ + { 0, 0 }, /* access date */ + { 0, 0 }, /* high bits of start cluster */ + { 210, 4 }, { 210, 4 }, /* modify time & date */ + { 0, 0 }, /* startcluster */ + { 0, 0, 0, 0 } /* filesize */ + }, + { ".. ", " ", /* the .. entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, /* reserved */ + 0, { 0, 0 }, { 0, 0 }, /* create time & date */ + { 0, 0 }, /* access date */ + { 0, 0 }, /* high bits of start cluster */ + { 210, 4 }, { 210, 4 }, /* modify time & date */ + { 0, 0 }, /* startcluster */ + { 0, 0, 0, 0 } /* filesize */ + } +}; + +struct denode * +msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) { + struct denode ndirent; + struct denode *dep; + struct componentname cn; + struct stat *st = &node->inode->st; + struct msdosfsmount *pmp = pdep->de_pmp; + int error; + u_long newcluster, pcl, bn; + daddr_t lbn; + struct direntry *denp; + struct buf *bp; + + cn.cn_nameptr = node->name; + cn.cn_namelen = strlen(node->name); + /* + * If this is the root directory and there is no space left we + * can't do anything. This is because the root directory can not + * change size. + */ + if (pdep->de_StartCluster == MSDOSFSROOT + && pdep->de_fndoffset >= pdep->de_FileSize) { + error = ENOSPC; + goto bad2; + } + + /* + * Allocate a cluster to hold the about to be created directory. + */ + error = clusteralloc(pmp, 0, 1, &newcluster, NULL); + if (error) + goto bad2; + + memset(&ndirent, 0, sizeof(ndirent)); + ndirent.de_pmp = pmp; + ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; + msdosfs_times(pmp, &ndirent, st); + + /* + * Now fill the cluster with the "." and ".." entries. And write + * the cluster to disk. This way it is there for the parent + * directory to be pointing at if there were a crash. + */ + bn = cntobn(pmp, newcluster); + lbn = de_bn2kb(pmp, bn); + DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__, newcluster, + bn, lbn)); + /* always succeeds */ + bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0); + memset(bp->b_data, 0, pmp->pm_bpcluster); + memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate); + denp = (struct direntry *)bp->b_data; + putushort(denp[0].deStartCluster, newcluster); + putushort(denp[0].deCDate, ndirent.de_CDate); + putushort(denp[0].deCTime, ndirent.de_CTime); + denp[0].deCHundredth = ndirent.de_CHun; + putushort(denp[0].deADate, ndirent.de_ADate); + putushort(denp[0].deMDate, ndirent.de_MDate); + putushort(denp[0].deMTime, ndirent.de_MTime); + pcl = pdep->de_StartCluster; + DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl, + pmp->pm_rootdirblk)); + if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) + pcl = 0; + putushort(denp[1].deStartCluster, pcl); + putushort(denp[1].deCDate, ndirent.de_CDate); + putushort(denp[1].deCTime, ndirent.de_CTime); + denp[1].deCHundredth = ndirent.de_CHun; + putushort(denp[1].deADate, ndirent.de_ADate); + putushort(denp[1].deMDate, ndirent.de_MDate); + putushort(denp[1].deMTime, ndirent.de_MTime); + if (FAT32(pmp)) { + putushort(denp[0].deHighClust, newcluster >> 16); + putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16); + } else { + putushort(denp[0].deHighClust, 0); + putushort(denp[1].deHighClust, 0); + } + + if ((error = bwrite(bp)) != 0) + goto bad; + + /* + * Now build up a directory entry pointing to the newly allocated + * cluster. This will be written to an empty slot in the parent + * directory. + */ + if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) + goto bad; + + ndirent.de_Attributes = ATTR_DIRECTORY; + ndirent.de_StartCluster = newcluster; + ndirent.de_FileSize = 0; + ndirent.de_dev = pdep->de_dev; + ndirent.de_devvp = pdep->de_devvp; + ndirent.de_pmp = pdep->de_pmp; + if ((error = msdosfs_findslot(pdep, &cn)) != 0) + goto bad; + if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0) + goto bad; + if ((error = msdosfs_updatede(dep)) != 0) + goto bad; + return dep; + +bad: + clusterfree(pmp, newcluster, NULL); +bad2: + errno = error; + return NULL; +}