summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordfr <dfr@FreeBSD.org>1994-09-19 15:41:57 +0000
committerdfr <dfr@FreeBSD.org>1994-09-19 15:41:57 +0000
commit7efbb964fdff1348e01bdcd7d4bb59551c389bef (patch)
tree4ee90f451b7756bcd74bbc0c7e0ef0219507ff02
parent7a224749be53b3a6bdb826ffbd93208f0b757586 (diff)
downloadFreeBSD-src-7efbb964fdff1348e01bdcd7d4bb59551c389bef.zip
FreeBSD-src-7efbb964fdff1348e01bdcd7d4bb59551c389bef.tar.gz
Added msdosfs.
Obtained from: NetBSD
-rw-r--r--sys/conf/files6
-rw-r--r--sys/fs/msdosfs/bootsect.h70
-rw-r--r--sys/fs/msdosfs/bpb.h120
-rw-r--r--sys/fs/msdosfs/denode.h267
-rw-r--r--sys/fs/msdosfs/direntry.h104
-rw-r--r--sys/fs/msdosfs/fat.h97
-rw-r--r--sys/fs/msdosfs/msdosfs_conv.c358
-rw-r--r--sys/fs/msdosfs/msdosfs_denode.c689
-rw-r--r--sys/fs/msdosfs/msdosfs_fat.c984
-rw-r--r--sys/fs/msdosfs/msdosfs_lookup.c763
-rw-r--r--sys/fs/msdosfs/msdosfs_vfsops.c733
-rw-r--r--sys/fs/msdosfs/msdosfs_vnops.c1933
-rw-r--r--sys/fs/msdosfs/msdosfsmount.h183
-rw-r--r--sys/kern/kern_ntptime.c1
-rw-r--r--sys/kern/vfs_conf.c15
-rw-r--r--sys/kern/vfs_mount.c15
-rw-r--r--sys/msdosfs/bootsect.h70
-rw-r--r--sys/msdosfs/bpb.h120
-rw-r--r--sys/msdosfs/denode.h267
-rw-r--r--sys/msdosfs/direntry.h104
-rw-r--r--sys/msdosfs/fat.h97
-rw-r--r--sys/msdosfs/msdosfs_conv.c358
-rw-r--r--sys/msdosfs/msdosfs_denode.c689
-rw-r--r--sys/msdosfs/msdosfs_fat.c984
-rw-r--r--sys/msdosfs/msdosfs_lookup.c763
-rw-r--r--sys/msdosfs/msdosfs_vfsops.c733
-rw-r--r--sys/msdosfs/msdosfs_vnops.c1933
-rw-r--r--sys/msdosfs/msdosfsmount.h183
-rw-r--r--sys/sys/malloc.h10
-rw-r--r--sys/sys/mount.h15
-rw-r--r--sys/sys/vnode.h4
31 files changed, 12659 insertions, 9 deletions
diff --git a/sys/conf/files b/sys/conf/files
index f314a1b..a3d4978 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -22,6 +22,12 @@ isofs/cd9660/cd9660_rrip.c optional cd9660
isofs/cd9660/cd9660_util.c optional cd9660
isofs/cd9660/cd9660_vfsops.c optional cd9660
isofs/cd9660/cd9660_vnops.c optional cd9660
+msdosfs/msdosfs_conv.c optional msdosfs
+msdosfs/msdosfs_denode.c optional msdosfs
+msdosfs/msdosfs_fat.c optional msdosfs
+msdosfs/msdosfs_lookup.c optional msdosfs
+msdosfs/msdosfs_vfsops.c optional msdosfs
+msdosfs/msdosfs_vnops.c optional msdosfs
kdb/kdb_access.c optional kadb
kdb/kdb_command.c optional kadb
kdb/kdb_ctype.c optional kadb
diff --git a/sys/fs/msdosfs/bootsect.h b/sys/fs/msdosfs/bootsect.h
new file mode 100644
index 0000000..099b15f
--- /dev/null
+++ b/sys/fs/msdosfs/bootsect.h
@@ -0,0 +1,70 @@
+/* $Id$ */
+/* $NetBSD: bootsect.h,v 1.4 1994/06/29 06:35:28 cgd Exp $ */
+
+/*
+ * 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
+ */
+
+/*
+ * Format of a boot sector. This is the first sector on a DOS floppy disk
+ * or the fist sector of a partition on a hard disk. But, it is not the
+ * first sector of a partitioned hard disk.
+ */
+struct bootsector33 {
+ u_char bsJump[3]; /* jump instruction E9xxxx or EBxx90 */
+ char bsOemName[8]; /* OEM name and version */
+ char bsBPB[19]; /* BIOS parameter block */
+ char bsDriveNumber; /* drive number (0x80) */
+ char bsBootCode[474]; /* pad so structure is 512 bytes long */
+ u_short bsBootSectSig;
+#define BOOTSIG 0xaa55
+};
+
+struct bootsector50 {
+ u_char bsJump[3]; /* jump instruction E9xxxx or EBxx90 */
+ char bsOemName[8]; /* OEM name and version */
+ char bsBPB[25]; /* BIOS parameter block */
+ char bsDriveNumber; /* drive number (0x80) */
+ char bsReserved1; /* reserved */
+ char bsBootSignature; /* extended boot signature (0x29) */
+#define EXBOOTSIG 0x29
+ char bsVolumeID[4]; /* volume ID number */
+ char bsVolumeLabel[11]; /* volume label */
+ char bsFileSysType[8]; /* file system type (FAT12 or FAT16) */
+ char bsBootCode[448]; /* pad so structure is 512 bytes long */
+ u_short bsBootSectSig;
+#define BOOTSIG 0xaa55
+};
+
+union bootsector {
+ struct bootsector33 bs33;
+ struct bootsector50 bs50;
+};
+
+/*
+ * Shorthand for fields in the bpb.
+ */
+#define bsBytesPerSec bsBPB.bpbBytesPerSec
+#define bsSectPerClust bsBPB.bpbSectPerClust
+#define bsResSectors bsBPB.bpbResSectors
+#define bsFATS bsBPB.bpbFATS
+#define bsRootDirEnts bsBPB.bpbRootDirEnts
+#define bsSectors bsBPB.bpbSectors
+#define bsMedia bsBPB.bpbMedia
+#define bsFATsecs bsBPB.bpbFATsecs
+#define bsSectPerTrack bsBPB.bpbSectPerTrack
+#define bsHeads bsBPB.bpbHeads
+#define bsHiddenSecs bsBPB.bpbHiddenSecs
+#define bsHugeSectors bsBPB.bpbHugeSectors
diff --git a/sys/fs/msdosfs/bpb.h b/sys/fs/msdosfs/bpb.h
new file mode 100644
index 0000000..70e4de2
--- /dev/null
+++ b/sys/fs/msdosfs/bpb.h
@@ -0,0 +1,120 @@
+/* $Id$ */
+/* $NetBSD: bpb.h,v 1.3 1994/06/29 06:35:29 cgd Exp $ */
+
+/*
+ * 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
+ */
+
+/*
+ * BIOS Parameter Block (BPB) for DOS 3.3
+ */
+struct bpb33 {
+ u_short bpbBytesPerSec; /* bytes per sector */
+ u_char bpbSecPerClust; /* sectors per cluster */
+ u_short bpbResSectors; /* number of reserved sectors */
+ u_char bpbFATs; /* number of FATs */
+ u_short bpbRootDirEnts; /* number of root directory entries */
+ u_short bpbSectors; /* total number of sectors */
+ u_char bpbMedia; /* media descriptor */
+ u_short bpbFATsecs; /* number of sectors per FAT */
+ u_short bpbSecPerTrack; /* sectors per track */
+ u_short bpbHeads; /* number of heads */
+ u_short bpbHiddenSecs; /* number of hidden sectors */
+};
+
+/*
+ * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3,
+ * and bpbHugeSectors is not in the 3.3 bpb.
+ */
+struct bpb50 {
+ u_short bpbBytesPerSec; /* bytes per sector */
+ u_char bpbSecPerClust; /* sectors per cluster */
+ u_short bpbResSectors; /* number of reserved sectors */
+ u_char bpbFATs; /* number of FATs */
+ u_short bpbRootDirEnts; /* number of root directory entries */
+ u_short bpbSectors; /* total number of sectors */
+ u_char bpbMedia; /* media descriptor */
+ u_short bpbFATsecs; /* number of sectors per FAT */
+ u_short bpbSecPerTrack; /* sectors per track */
+ u_short bpbHeads; /* number of heads */
+ u_long bpbHiddenSecs; /* number of hidden sectors */
+ u_long bpbHugeSectors; /* number of sectrs if bpbSectors == 0 */
+};
+
+/*
+ * The following structures represent how the bpb's look on disk. shorts
+ * and longs are just character arrays of the appropriate length. This is
+ * because the compiler forces shorts and longs to align on word or
+ * halfword boundaries.
+ *
+ * XXX The little-endian code here assumes that the processor can access
+ * 16-bit and 32-bit quantities on byte boundaries. If this is not true,
+ * use the macros for the big-endian case.
+ */
+#include <machine/endian.h>
+#if BYTE_ORDER == LITTLE_ENDIAN /* && can do unaligned accesses */
+#define getushort(x) *((u_short *)(x))
+#define getulong(x) *((u_long *)(x))
+#define putushort(p, v) (*((u_short *)(p)) = (v))
+#define putulong(p, v) (*((u_long *)(p)) = (v))
+
+#else
+#define getushort(x) (((u_char *)(x))[0] + (((u_char *)(x))[1] << 8))
+#define getulong(x) (((u_char *)(x))[0] + (((u_char *)(x))[1] << 8) \
+ + (((u_char *)(x))[2] << 16) \
+ + (((u_char *)(x))[3] << 24))
+#define putushort(p, v) (((u_char *)(p))[0] = (v), \
+ ((u_char *)(p))[1] = (v) >> 8)
+#define putulong(p, v) (((u_char *)(p))[0] = (v), \
+ ((u_char *)(p))[1] = (v) >> 8, \
+ ((u_char *)(p))[2] = (v) >> 16,\
+ ((u_char *)(p))[3] = (v) >> 24)
+#endif
+
+/*
+ * BIOS Parameter Block (BPB) for DOS 3.3
+ */
+struct byte_bpb33 {
+ char bpbBytesPerSec[2]; /* bytes per sector */
+ char bpbSecPerClust; /* sectors per cluster */
+ char bpbResSectors[2]; /* number of reserved sectors */
+ char bpbFATs; /* number of FATs */
+ char bpbRootDirEnts[2]; /* number of root directory entries */
+ char bpbSectors[2]; /* total number of sectors */
+ char bpbMedia; /* media descriptor */
+ char bpbFATsecs[2]; /* number of sectors per FAT */
+ char bpbSecPerTrack[2]; /* sectors per track */
+ char bpbHeads[2]; /* number of heads */
+ char bpbHiddenSecs[2]; /* number of hidden sectors */
+};
+
+/*
+ * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3,
+ * and bpbHugeSectors is not in the 3.3 bpb.
+ */
+struct byte_bpb50 {
+ char bpbBytesPerSec[2]; /* bytes per sector */
+ char bpbSecPerClust; /* sectors per cluster */
+ char bpbResSectors[2]; /* number of reserved sectors */
+ char bpbFATs; /* number of FATs */
+ char bpbRootDirEnts[2]; /* number of root directory entries */
+ char bpbSectors[2]; /* total number of sectors */
+ char bpbMedia; /* media descriptor */
+ char bpbFATsecs[2]; /* number of sectors per FAT */
+ char bpbSecPerTrack[2]; /* sectors per track */
+ char bpbHeads[2]; /* number of heads */
+ char bpbHiddenSecs[4]; /* number of hidden sectors */
+ char bpbHugeSectors[4]; /* number of sectrs if bpbSectors == 0 */
+};
diff --git a/sys/fs/msdosfs/denode.h b/sys/fs/msdosfs/denode.h
new file mode 100644
index 0000000..8df3e4c
--- /dev/null
+++ b/sys/fs/msdosfs/denode.h
@@ -0,0 +1,267 @@
+/* $Id$ */
+/* $NetBSD: denode.h,v 1.8 1994/08/21 18:43:49 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * This is the pc filesystem specific portion of the vnode structure.
+ *
+ * To describe a file uniquely the de_dirclust, de_diroffset, and
+ * de_StartCluster fields are used.
+ *
+ * de_dirclust contains the cluster number of the directory cluster
+ * containing the entry for a file or directory.
+ * de_diroffset is the index into the cluster for the entry describing
+ * a file or directory.
+ * de_StartCluster is the number of the first cluster of the file or directory.
+ *
+ * Now to describe the quirks of the pc filesystem.
+ * - Clusters 0 and 1 are reserved.
+ * - The first allocatable cluster is 2.
+ * - The root directory is of fixed size and all blocks that make it up
+ * are contiguous.
+ * - Cluster 0 refers to the root directory when it is found in the
+ * startcluster field of a directory entry that points to another directory.
+ * - Cluster 0 implies a 0 length file when found in the start cluster field
+ * of a directory entry that points to a file.
+ * - You can't use the cluster number 0 to derive the address of the root
+ * directory.
+ * - Multiple directory entries can point to a directory. The entry in the
+ * parent directory points to a child directory. Any directories in the
+ * child directory contain a ".." entry that points back to the parent.
+ * The child directory itself contains a "." entry that points to itself.
+ * - The root directory does not contain a "." or ".." entry.
+ * - Directory entries for directories are never changed once they are created
+ * (except when removed). The size stays 0, and the last modification time
+ * is never changed. This is because so many directory entries can point to
+ * the physical clusters that make up a directory. It would lead to an
+ * update nightmare.
+ * - The length field in a directory entry pointing to a directory contains 0
+ * (always). The only way to find the end of a directory is to follow the
+ * cluster chain until the "last cluster" marker is found.
+ *
+ * My extensions to make this house of cards work. These apply only to the in
+ * memory copy of the directory entry.
+ * - A reference count for each denode will be kept since dos doesn't keep such
+ * things.
+ */
+
+/*
+ * Internal pseudo-offset for (nonexistent) directory entry for the root
+ * dir in the root dir
+ */
+#define MSDOSFSROOT_OFS 0x1fffffff
+
+/*
+ * The fat cache structure. fc_fsrcn is the filesystem relative cluster
+ * number that corresponds to the file relative cluster number in this
+ * structure (fc_frcn).
+ */
+struct fatcache {
+ u_short fc_frcn; /* file relative cluster number */
+ u_short fc_fsrcn; /* filesystem relative cluster number */
+};
+
+/*
+ * The fat entry cache as it stands helps make extending files a "quick"
+ * operation by avoiding having to scan the fat to discover the last
+ * cluster of the file. The cache also helps sequential reads by
+ * remembering the last cluster read from the file. This also prevents us
+ * from having to rescan the fat to find the next cluster to read. This
+ * cache is probably pretty worthless if a file is opened by multiple
+ * processes.
+ */
+#define FC_SIZE 2 /* number of entries in the cache */
+#define FC_LASTMAP 0 /* entry the last call to pcbmap() resolved
+ * to */
+#define FC_LASTFC 1 /* entry for the last cluster in the file */
+
+#define FCE_EMPTY 0xffff /* doesn't represent an actual cluster # */
+
+/*
+ * Set a slot in the fat cache.
+ */
+#define fc_setcache(dep, slot, frcn, fsrcn) \
+ (dep)->de_fc[slot].fc_frcn = frcn; \
+ (dep)->de_fc[slot].fc_fsrcn = fsrcn;
+
+/*
+ * This is the in memory variant of a dos directory entry. It is usually
+ * contained within a vnode.
+ */
+struct denode {
+ struct denode *de_next; /* Hash chain forward */
+ struct denode **de_prev; /* Hash chain back */
+ struct vnode *de_vnode; /* addr of vnode we are part of */
+ struct vnode *de_devvp; /* vnode of blk dev we live on */
+ u_long de_flag; /* flag bits */
+ dev_t de_dev; /* device where direntry lives */
+ u_long de_dirclust; /* cluster of the directory file containing this entry */
+ u_long de_diroffset; /* ordinal of this entry in the directory */
+ u_long de_fndclust; /* cluster of found dir entry */
+ u_long de_fndoffset; /* offset of found dir entry */
+ long de_refcnt; /* reference count */
+ struct msdosfsmount *de_pmp; /* addr of our mount struct */
+ struct lockf *de_lockf; /* byte level lock list */
+ pid_t de_lockholder; /* current lock holder */
+ pid_t de_lockwaiter; /* lock wanter */
+ /* the next two fields must be contiguous in memory... */
+ u_char de_Name[8]; /* name, from directory entry */
+ u_char de_Extension[3]; /* extension, from directory entry */
+ u_char de_Attributes; /* attributes, from directory entry */
+ u_short de_Time; /* creation time */
+ u_short de_Date; /* creation date */
+ u_short de_StartCluster; /* starting cluster of file */
+ u_long de_FileSize; /* size of file in bytes */
+ struct fatcache de_fc[FC_SIZE]; /* fat cache */
+};
+
+/*
+ * Values for the de_flag field of the denode.
+ */
+#define DE_LOCKED 0x0001 /* directory entry is locked */
+#define DE_WANTED 0x0002 /* someone wants this de */
+#define DE_UPDATE 0x0004 /* file has been modified */
+#define DE_MODIFIED 0x0080 /* denode wants to be written back to disk */
+
+/*
+ * Transfer directory entries between internal and external form.
+ * dep is a struct denode * (internal form),
+ * dp is a struct direntry * (external form).
+ */
+#define DE_INTERNALIZE(dep, dp) \
+ (bcopy((dp)->deName, (dep)->de_Name, 11), \
+ (dep)->de_Attributes = (dp)->deAttributes, \
+ (dep)->de_Time = getushort((dp)->deTime), \
+ (dep)->de_Date = getushort((dp)->deDate), \
+ (dep)->de_StartCluster = getushort((dp)->deStartCluster), \
+ (dep)->de_FileSize = getulong((dp)->deFileSize))
+
+#define DE_EXTERNALIZE(dp, dep) \
+ (bcopy((dep)->de_Name, (dp)->deName, 11), \
+ (dp)->deAttributes = (dep)->de_Attributes, \
+ putushort((dp)->deTime, (dep)->de_Time), \
+ putushort((dp)->deDate, (dep)->de_Date), \
+ putushort((dp)->deStartCluster, (dep)->de_StartCluster), \
+ putulong((dp)->deFileSize, (dep)->de_FileSize))
+
+#define de_forw de_chain[0]
+#define de_back de_chain[1]
+
+#ifdef KERNEL
+
+#define VTODE(vp) ((struct denode *)(vp)->v_data)
+#define DETOV(de) ((de)->de_vnode)
+
+#define DE_UPDAT(dep, t, waitfor) \
+ if (dep->de_flag & DE_UPDATE) \
+ (void) deupdat(dep, t, waitfor);
+
+#define DE_TIMES(dep, t) \
+ if (dep->de_flag & DE_UPDATE) { \
+ (dep)->de_flag |= DE_MODIFIED; \
+ unix2dostime(t, &dep->de_Date, &dep->de_Time); \
+ (dep)->de_flag &= ~DE_UPDATE; \
+ }
+
+/*
+ * This overlays the fid sturcture (see mount.h)
+ */
+struct defid {
+ u_short defid_len; /* length of structure */
+ u_short defid_pad; /* force long alignment */
+
+ u_long defid_dirclust; /* cluster this dir entry came from */
+ u_long defid_dirofs; /* index of entry within the cluster */
+
+ /* u_long defid_gen; generation number */
+};
+
+/*
+ * Prototypes for MSDOSFS vnode operations
+ */
+int msdosfs_lookup __P((struct vop_lookup_args *));
+int msdosfs_create __P((struct vop_create_args *));
+int msdosfs_mknod __P((struct vop_mknod_args *));
+int msdosfs_open __P((struct vop_open_args *));
+int msdosfs_close __P((struct vop_close_args *));
+int msdosfs_access __P((struct vop_access_args *));
+int msdosfs_getattr __P((struct vop_getattr_args *));
+int msdosfs_setattr __P((struct vop_setattr_args *));
+int msdosfs_read __P((struct vop_read_args *));
+int msdosfs_write __P((struct vop_write_args *));
+int msdosfs_ioctl __P((struct vop_ioctl_args *));
+int msdosfs_select __P((struct vop_select_args *));
+int msdosfs_mmap __P((struct vop_mmap_args *));
+int msdosfs_fsync __P((struct vop_fsync_args *));
+int msdosfs_seek __P((struct vop_seek_args *));
+int msdosfs_remove __P((struct vop_remove_args *));
+int msdosfs_link __P((struct vop_link_args *));
+int msdosfs_rename __P((struct vop_rename_args *));
+int msdosfs_mkdir __P((struct vop_mkdir_args *));
+int msdosfs_rmdir __P((struct vop_rmdir_args *));
+int msdosfs_symlink __P((struct vop_symlink_args *));
+int msdosfs_readdir __P((struct vop_readdir_args *));
+int msdosfs_readlink __P((struct vop_readlink_args *));
+int msdosfs_abortop __P((struct vop_abortop_args *));
+int msdosfs_inactive __P((struct vop_inactive_args *));
+int msdosfs_reclaim __P((struct vop_reclaim_args *));
+int msdosfs_lock __P((struct vop_lock_args *));
+int msdosfs_unlock __P((struct vop_unlock_args *));
+int msdosfs_bmap __P((struct vop_bmap_args *));
+int msdosfs_strategy __P((struct vop_strategy_args *));
+int msdosfs_print __P((struct vop_print_args *));
+int msdosfs_islocked __P((struct vop_islocked_args *));
+int msdosfs_advlock __P((struct vop_advlock_args *));
+int msdosfs_reallocblks __P((struct vop_reallocblks_args *));
+
+/*
+ * Internal service routine prototypes.
+ */
+int deget __P((struct msdosfsmount * pmp, u_long dirclust, u_long diroffset, struct direntry * direntptr, struct denode ** depp));
+#endif /* KERNEL */
diff --git a/sys/fs/msdosfs/direntry.h b/sys/fs/msdosfs/direntry.h
new file mode 100644
index 0000000..d2ec019
--- /dev/null
+++ b/sys/fs/msdosfs/direntry.h
@@ -0,0 +1,104 @@
+/* $Id$ */
+/* $NetBSD: direntry.h,v 1.7 1994/08/21 18:43:54 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * Structure of a dos directory entry.
+ */
+struct direntry {
+ u_char deName[8]; /* filename, blank filled */
+#define SLOT_EMPTY 0x00 /* slot has never been used */
+#define SLOT_E5 0x05 /* the real value is 0xe5 */
+#define SLOT_DELETED 0xe5 /* file in this slot deleted */
+ u_char deExtension[3]; /* extension, blank filled */
+ u_char deAttributes; /* file attributes */
+#define ATTR_NORMAL 0x00 /* normal file */
+#define ATTR_READONLY 0x01 /* file is readonly */
+#define ATTR_HIDDEN 0x02 /* file is hidden */
+#define ATTR_SYSTEM 0x04 /* file is a system file */
+#define ATTR_VOLUME 0x08 /* entry is a volume label */
+#define ATTR_DIRECTORY 0x10 /* entry is a directory name */
+#define ATTR_ARCHIVE 0x20 /* file is new or modified */
+ u_char deReserved[10]; /* reserved */
+ u_char deTime[2]; /* create/last update time */
+ u_char deDate[2]; /* create/last update date */
+ u_char deStartCluster[2]; /* starting cluster of file */
+ u_char deFileSize[4]; /* size of file in bytes */
+};
+
+/*
+ * This is the format of the contents of the deTime field in the direntry
+ * structure.
+ * We don't use bitfields because we don't know how compilers for
+ * arbitrary machines will lay them out.
+ */
+#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
+#define DT_2SECONDS_SHIFT 0
+#define DT_MINUTES_MASK 0x7E0 /* minutes */
+#define DT_MINUTES_SHIFT 5
+#define DT_HOURS_MASK 0xF800 /* hours */
+#define DT_HOURS_SHIFT 11
+
+/*
+ * This is the format of the contents of the deDate field in the direntry
+ * structure.
+ */
+#define DD_DAY_MASK 0x1F /* day of month */
+#define DD_DAY_SHIFT 0
+#define DD_MONTH_MASK 0x1E0 /* month */
+#define DD_MONTH_SHIFT 5
+#define DD_YEAR_MASK 0xFE00 /* year - 1980 */
+#define DD_YEAR_SHIFT 9
+
+#ifdef KERNEL
+void unix2dostime __P((struct timespec * tsp, u_short * ddp, u_short * dtp));
+void dos2unixtime __P((u_short dd, u_short dt, struct timespec * tsp));
+int dos2unixfn __P((u_char dn[11], u_char * un));
+void unix2dosfn __P((u_char * un, u_char dn[11], int unlen));
+#endif /* KERNEL */
diff --git a/sys/fs/msdosfs/fat.h b/sys/fs/msdosfs/fat.h
new file mode 100644
index 0000000..3867c87
--- /dev/null
+++ b/sys/fs/msdosfs/fat.h
@@ -0,0 +1,97 @@
+/* $Id$ */
+/* $NetBSD: fat.h,v 1.4 1994/08/21 18:43:57 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * Some useful cluster numbers.
+ */
+#define MSDOSFSROOT 0 /* cluster 0 means the root dir */
+#define CLUST_FREE 0 /* cluster 0 also means a free cluster */
+#define MSDOSFSFREE CLUST_FREE
+#define CLUST_FIRST 2 /* first legal cluster number */
+#define CLUST_RSRVS 0xfff0 /* start of reserved cluster range */
+#define CLUST_RSRVE 0xfff6 /* end of reserved cluster range */
+#define CLUST_BAD 0xfff7 /* a cluster with a defect */
+#define CLUST_EOFS 0xfff8 /* start of eof cluster range */
+#define CLUST_EOFE 0xffff /* end of eof cluster range */
+
+#define FAT12_MASK 0x0fff /* mask for 12 bit cluster numbers */
+#define FAT16_MASK 0xffff /* mask for 16 bit cluster numbers */
+
+/*
+ * Return true if filesystem uses 12 bit fats. Microsoft Programmer's
+ * Reference says if the maximum cluster number in a filesystem is greater
+ * than 4086 then we've got a 16 bit fat filesystem.
+ */
+#define FAT12(pmp) (pmp->pm_maxcluster <= 4086)
+#define FAT16(pmp) (pmp->pm_maxcluster > 4086)
+
+#define MSDOSFSEOF(cn) (((cn) & 0xfff8) == 0xfff8)
+
+#ifdef KERNEL
+/*
+ * These are the values for the function argument to the function
+ * fatentry().
+ */
+#define FAT_GET 0x0001 /* get a fat entry */
+#define FAT_SET 0x0002 /* set a fat entry */
+#define FAT_GET_AND_SET (FAT_GET | FAT_SET)
+
+/*
+ * Flags to extendfile:
+ */
+#define DE_CLEAR 1 /* Zero out the blocks allocated */
+
+int pcbmap __P((struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp));
+int clusterfree __P((struct msdosfsmount *pmp, u_long cn, u_long *oldcnp));
+int clusteralloc __P((struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith, u_long *retcluster, u_long *got));
+int fatentry __P((int function, struct msdosfsmount *pmp, u_long cluster, u_long *oldcontents, u_long newcontents));
+int freeclusterchain __P((struct msdosfsmount *pmp, u_long startchain));
+int extendfile __P((struct denode *dep, u_long count, struct buf **bpp, u_long *ncp, int flags));
+#endif /* KERNEL */
diff --git a/sys/fs/msdosfs/msdosfs_conv.c b/sys/fs/msdosfs/msdosfs_conv.c
new file mode 100644
index 0000000..da8ce97
--- /dev/null
+++ b/sys/fs/msdosfs/msdosfs_conv.c
@@ -0,0 +1,358 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_conv.c,v 1.6.2.1 1994/08/30 02:27:57 cgd Exp $ */
+
+/*
+ * 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
+ */
+
+/*
+ * System include files.
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/kernel.h> /* defines tz */
+
+/*
+ * MSDOSFS include files.
+ */
+#include <msdosfs/direntry.h>
+
+/*
+ * Days in each month in a regular year.
+ */
+u_short regyear[] = {
+ 31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * Days in each month in a leap year.
+ */
+u_short leapyear[] = {
+ 31, 29, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * Variables used to remember parts of the last time conversion. Maybe we
+ * can avoid a full conversion.
+ */
+u_long lasttime;
+u_long lastday;
+u_short lastddate;
+u_short lastdtime;
+
+/*
+ * Convert the unix version of time to dos's idea of time to be used in
+ * file timestamps. The passed in unix time is assumed to be in GMT.
+ */
+void
+unix2dostime(tsp, ddp, dtp)
+ struct timespec *tsp;
+ u_short *ddp;
+ u_short *dtp;
+{
+ u_long t;
+ u_long days;
+ u_long inc;
+ u_long year;
+ u_long month;
+ u_short *months;
+
+ /*
+ * If the time from the last conversion is the same as now, then
+ * skip the computations and use the saved result.
+ */
+ t = tsp->ts_sec - (tz.tz_minuteswest * 60)
+ /* +- daylight savings time correction */ ;
+ if (lasttime != t) {
+ lasttime = t;
+ lastdtime = (((t % 60) >> 1) << DT_2SECONDS_SHIFT)
+ + (((t / 60) % 60) << DT_MINUTES_SHIFT)
+ + (((t / 3600) % 24) << DT_HOURS_SHIFT);
+
+ /*
+ * If the number of days since 1970 is the same as the last
+ * time we did the computation then skip all this leap year
+ * and month stuff.
+ */
+ days = t / (24 * 60 * 60);
+ if (days != lastday) {
+ lastday = days;
+ for (year = 1970;; year++) {
+ inc = year & 0x03 ? 365 : 366;
+ if (days < inc)
+ break;
+ days -= inc;
+ }
+ months = year & 0x03 ? regyear : leapyear;
+ for (month = 0; month < 12; month++) {
+ if (days < months[month])
+ break;
+ days -= months[month];
+ }
+ lastddate = ((days + 1) << DD_DAY_SHIFT)
+ + ((month + 1) << DD_MONTH_SHIFT);
+ /*
+ * Remember dos's idea of time is relative to 1980.
+ * unix's is relative to 1970. If somehow we get a
+ * time before 1980 then don't give totally crazy
+ * results.
+ */
+ if (year > 1980)
+ lastddate += (year - 1980) << DD_YEAR_SHIFT;
+ }
+ }
+ *dtp = lastdtime;
+ *ddp = lastddate;
+}
+
+/*
+ * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
+ * interval there were 8 regular years and 2 leap years.
+ */
+#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
+
+u_short lastdosdate;
+u_long lastseconds;
+
+/*
+ * Convert from dos' idea of time to unix'. This will probably only be
+ * called from the stat(), and fstat() system calls and so probably need
+ * not be too efficient.
+ */
+void
+dos2unixtime(dd, dt, tsp)
+ u_short dd;
+ u_short dt;
+ struct timespec *tsp;
+{
+ u_long seconds;
+ u_long m, month;
+ u_long y, year;
+ u_long days;
+ u_short *months;
+
+ seconds = ((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT)
+ + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60
+ + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600;
+ /*
+ * If the year, month, and day from the last conversion are the
+ * same then use the saved value.
+ */
+ if (lastdosdate != dd) {
+ lastdosdate = dd;
+ days = 0;
+ year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT;
+ for (y = 0; y < year; y++) {
+ days += y & 0x03 ? 365 : 366;
+ }
+ months = year & 0x03 ? regyear : leapyear;
+ /*
+ * Prevent going from 0 to 0xffffffff in the following
+ * loop.
+ */
+ month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT;
+ if (month == 0) {
+ printf("dos2unixtime(): month value out of range (%d)\n",
+ month);
+ month = 1;
+ }
+ for (m = 0; m < month - 1; m++) {
+ days += months[m];
+ }
+ days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
+ lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
+ }
+ tsp->ts_sec = seconds + lastseconds + (tz.tz_minuteswest * 60)
+ /* -+ daylight savings time correction */ ;
+ tsp->ts_nsec = 0;
+}
+
+/*
+ * Cheezy macros to do case detection and conversion for the ascii
+ * character set. DOESN'T work for ebcdic.
+ */
+#define isupper(c) (c >= 'A' && c <= 'Z')
+#define islower(c) (c >= 'a' && c <= 'z')
+#define toupper(c) (c & ~' ')
+#define tolower(c) (c | ' ')
+
+/*
+ * DOS filenames are made of 2 parts, the name part and the extension part.
+ * The name part is 8 characters long and the extension part is 3
+ * characters long. They may contain trailing blanks if the name or
+ * extension are not long enough to fill their respective fields.
+ */
+
+/*
+ * Convert a DOS filename to a unix filename. And, return the number of
+ * characters in the resulting unix filename excluding the terminating
+ * null.
+ */
+int
+dos2unixfn(dn, un)
+ u_char dn[11];
+ u_char *un;
+{
+ int i;
+ int ni;
+ int ei;
+ int thislong = 0;
+ u_char c;
+ u_char *origun = un;
+
+ /*
+ * Find the last character in the name portion of the dos filename.
+ */
+ for (ni = 7; ni >= 0; ni--)
+ if (dn[ni] != ' ')
+ break;
+
+ /*
+ * Find the last character in the extension portion of the
+ * filename.
+ */
+ for (ei = 10; ei >= 8; ei--)
+ if (dn[ei] != ' ')
+ break;
+
+ /*
+ * Copy the name portion into the unix filename string. NOTE: DOS
+ * filenames are usually kept in upper case. To make it more unixy
+ * we convert all DOS filenames to lower case. Some may like this,
+ * some may not.
+ */
+ for (i = 0; i <= ni; i++) {
+ c = dn[i];
+ *un++ = isupper(c) ? tolower(c) : c;
+ thislong++;
+ }
+
+ /*
+ * Now, if there is an extension then put in a period and copy in
+ * the extension.
+ */
+ if (ei >= 8) {
+ *un++ = '.';
+ thislong++;
+ for (i = 8; i <= ei; i++) {
+ c = dn[i];
+ *un++ = isupper(c) ? tolower(c) : c;
+ thislong++;
+ }
+ }
+ *un++ = 0;
+
+ /*
+ * If first char of the filename is SLOT_E5 (0x05), then the real
+ * first char of the filename should be 0xe5. But, they couldn't
+ * just have a 0xe5 mean 0xe5 because that is used to mean a freed
+ * directory slot. Another dos quirk.
+ */
+ if (*origun == SLOT_E5)
+ *origun = 0xe5;
+
+ return thislong;
+}
+
+/*
+ * Convert a unix filename to a DOS filename. This function does not ensure
+ * that valid characters for a dos filename are supplied.
+ */
+void
+unix2dosfn(un, dn, unlen)
+ u_char *un;
+ u_char dn[11];
+ int unlen;
+{
+ int i;
+ u_char c;
+
+ /*
+ * Fill the dos filename string with blanks. These are DOS's pad
+ * characters.
+ */
+ for (i = 0; i <= 10; i++)
+ dn[i] = ' ';
+
+ /*
+ * The filenames "." and ".." are handled specially, since they
+ * don't follow dos filename rules.
+ */
+ if (un[0] == '.' && unlen == 1) {
+ dn[0] = '.';
+ return;
+ }
+ if (un[0] == '.' && un[1] == '.' && unlen == 2) {
+ dn[0] = '.';
+ dn[1] = '.';
+ return;
+ }
+
+ /*
+ * Copy the unix filename into the dos filename string upto the end
+ * of string, a '.', or 8 characters. Whichever happens first stops
+ * us. This forms the name portion of the dos filename. Fold to
+ * upper case.
+ */
+ for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) {
+ dn[i] = islower(c) ? toupper(c) : c;
+ un++;
+ unlen--;
+ }
+
+ /*
+ * If the first char of the filename is 0xe5, then translate it to
+ * 0x05. This is because 0xe5 is the marker for a deleted
+ * directory slot. I guess this means you can't have filenames
+ * that start with 0x05. I suppose we should check for this and
+ * doing something about it.
+ */
+ if (dn[0] == SLOT_DELETED)
+ dn[0] = SLOT_E5;
+
+ /*
+ * Strip any further characters up to a '.' or the end of the
+ * string.
+ */
+ while (unlen && (c = *un)) {
+ un++;
+ unlen--;
+ /* Make sure we've skipped over the dot before stopping. */
+ if (c == '.')
+ break;
+ }
+
+ /*
+ * Copy in the extension part of the name, if any. Force to upper
+ * case. Note that the extension is allowed to contain '.'s.
+ * Filenames in this form are probably inaccessable under dos.
+ */
+ for (i = 8; i <= 10 && unlen && (c = *un); i++) {
+ dn[i] = islower(c) ? toupper(c) : c;
+ un++;
+ unlen--;
+ }
+}
+
+/*
+ * Get rid of these macros before someone discovers we are using such
+ * hideous things.
+ */
+#undef isupper
+#undef islower
+#undef toupper
+#undef tolower
diff --git a/sys/fs/msdosfs/msdosfs_denode.c b/sys/fs/msdosfs/msdosfs_denode.c
new file mode 100644
index 0000000..6812863
--- /dev/null
+++ b/sys/fs/msdosfs/msdosfs_denode.c
@@ -0,0 +1,689 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_denode.c,v 1.9 1994/08/21 18:44:00 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h> /* defines "time" */
+
+#include <msdosfs/bpb.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/fat.h>
+
+struct denode **dehashtbl;
+u_long dehash; /* size of hash table - 1 */
+#define DEHASH(dev, deno) (((dev) + (deno)) & dehash)
+
+int msdosfs_init()
+{
+ dehashtbl = hashinit(desiredvnodes/2, M_MSDOSFSMNT, &dehash);
+ return 0;
+}
+
+static struct denode *
+msdosfs_hashget(dev, dirclust, diroff)
+ dev_t dev;
+ u_long dirclust;
+ u_long diroff;
+{
+ struct denode *dep;
+
+ for (;;)
+ for (dep = dehashtbl[DEHASH(dev, dirclust + diroff)];;
+ dep = dep->de_next) {
+ if (dep == NULL)
+ return NULL;
+ if (dirclust != dep->de_dirclust
+ || diroff != dep->de_diroffset
+ || dev != dep->de_dev
+ || dep->de_refcnt == 0)
+ continue;
+ if (dep->de_flag & DE_LOCKED) {
+ dep->de_flag |= DE_WANTED;
+ sleep((caddr_t)dep, PINOD);
+ break;
+ }
+ if (!vget(DETOV(dep), 1))
+ return dep;
+ break;
+ }
+ /* NOTREACHED */
+}
+
+static void
+msdosfs_hashins(dep)
+ struct denode *dep;
+{
+ struct denode **depp, *deq;
+
+ depp = &dehashtbl[DEHASH(dep->de_dev, dep->de_dirclust + dep->de_diroffset)];
+ if (deq = *depp)
+ deq->de_prev = &dep->de_next;
+ dep->de_next = deq;
+ dep->de_prev = depp;
+ *depp = dep;
+ if (dep->de_flag & DE_LOCKED)
+ panic("msdosfs_hashins: already locked");
+ if (curproc)
+ dep->de_lockholder = curproc->p_pid;
+ else
+ dep->de_lockholder = -1;
+ dep->de_flag |= DE_LOCKED;
+}
+
+static void
+msdosfs_hashrem(dep)
+ struct denode *dep;
+{
+ struct denode *deq;
+ if (deq = dep->de_next)
+ deq->de_prev = dep->de_prev;
+ *dep->de_prev = deq;
+#ifdef DIAGNOSTIC
+ dep->de_next = NULL;
+ dep->de_prev = NULL;
+#endif
+}
+
+/*
+ * 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
+ * direntptr - address of the direntry structure of interest. If direntptr is
+ * NULL, the block is read if necessary.
+ * depp - returns the address of the gotten denode.
+ */
+int
+deget(pmp, dirclust, diroffset, direntptr, depp)
+ struct msdosfsmount *pmp; /* so we know the maj/min number */
+ u_long dirclust; /* cluster this dir entry came from */
+ u_long diroffset; /* index of entry within the cluster */
+ struct direntry *direntptr;
+ struct denode **depp; /* returns the addr of the gotten denode */
+{
+ int error;
+ dev_t dev = pmp->pm_dev;
+ struct mount *mntp = pmp->pm_mountp;
+ extern int (**msdosfs_vnodeop_p)();
+ struct denode *ldep;
+ struct vnode *nvp;
+ struct buf *bp;
+
+#ifdef MSDOSFS_DEBUG
+ printf("deget(pmp %08x, dirclust %d, diroffset %x, direntptr %x, depp %08x)\n",
+ pmp, dirclust, diroffset, direntptr, depp);
+#endif
+
+ /*
+ * If dir entry is given and refers to a directory, convert to
+ * canonical form
+ */
+ if (direntptr && (direntptr->deAttributes & ATTR_DIRECTORY)) {
+ dirclust = getushort(direntptr->deStartCluster);
+ if (dirclust == MSDOSFSROOT)
+ diroffset = MSDOSFSROOT_OFS;
+ else
+ diroffset = 0;
+ }
+
+ /*
+ * See if the denode is in the denode cache. Use the location of
+ * the directory entry to compute the hash value. For subdir use
+ * address of "." entry. for root dir use cluster MSDOSFSROOT,
+ * offset MSDOSFSROOT_OFS
+ *
+ * NOTE: The check for de_refcnt > 0 below insures the denode being
+ * examined does not represent an unlinked but still open file.
+ * These files are not to be accessible even when the directory
+ * entry that represented the file happens to be reused while the
+ * deleted file is still open.
+ */
+ if (ldep = msdosfs_hashget(dev, dirclust, diroffset)) {
+ *depp = ldep;
+ return 0;
+ }
+
+
+ /*
+ * Directory entry was not in cache, have to create a vnode and
+ * copy it from the passed disk buffer.
+ */
+ /* getnewvnode() does a VREF() on the vnode */
+ if (error = getnewvnode(VT_MSDOSFS, mntp, msdosfs_vnodeop_p, &nvp)) {
+ *depp = 0;
+ return error;
+ }
+ MALLOC(ldep, struct denode *, sizeof(struct denode), M_MSDOSFSNODE, M_WAITOK);
+ bzero((caddr_t)ldep, sizeof *ldep);
+ nvp->v_data = ldep;
+ ldep->de_vnode = nvp;
+ ldep->de_flag = 0;
+ ldep->de_devvp = 0;
+ ldep->de_lockf = 0;
+ ldep->de_dev = dev;
+ ldep->de_dirclust = dirclust;
+ ldep->de_diroffset = diroffset;
+ fc_purge(ldep, 0); /* init the fat cache for this denode */
+
+ /*
+ * Insert the denode into the hash queue and lock the denode so it
+ * can't be accessed until we've read it in and have done what we
+ * need to it.
+ */
+ msdosfs_hashins(ldep);
+
+ /*
+ * Copy the directory entry into the denode area of the vnode.
+ */
+ if (dirclust == MSDOSFSROOT && 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_Attributes = ATTR_DIRECTORY;
+ 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_Time = 0x0000; /* 00:00:00 */
+ ldep->de_Date = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
+ | (1 << DD_DAY_SHIFT);
+ /* Jan 1, 1980 */
+ /* leave the other fields as garbage */
+ } else {
+ bp = NULL;
+ if (!direntptr) {
+ error = readep(pmp, dirclust, diroffset, &bp,
+ &direntptr);
+ if (error)
+ return error;
+ }
+ DE_INTERNALIZE(ldep, direntptr);
+ if (bp)
+ brelse(bp);
+ }
+
+ /*
+ * Fill in a few fields of the vnode and finish filling in the
+ * denode. Then return the address of the found denode.
+ */
+ ldep->de_pmp = pmp;
+ ldep->de_devvp = pmp->pm_devvp;
+ ldep->de_refcnt = 1;
+ 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;
+
+ nvp->v_type = VDIR;
+ if (ldep->de_StartCluster == MSDOSFSROOT)
+ nvp->v_flag |= VROOT;
+ else {
+ error = pcbmap(ldep, 0xffff, 0, &size);
+ if (error == E2BIG) {
+ ldep->de_FileSize = size << pmp->pm_cnshift;
+ error = 0;
+ } else
+ printf("deget(): pcbmap returned %d\n", error);
+ }
+ } else
+ nvp->v_type = VREG;
+ VREF(ldep->de_devvp);
+ *depp = ldep;
+ return 0;
+}
+
+int
+deupdat(dep, tp, waitfor)
+ struct denode *dep;
+ struct timespec *tp;
+ int waitfor;
+{
+ int error;
+ daddr_t bn;
+ int diro;
+ struct buf *bp;
+ struct direntry *dirp;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct timespec ts;
+ struct vnode *vp = DETOV(dep);
+
+#ifdef MSDOSFS_DEBUG
+ printf("deupdat(): dep %08x\n", dep);
+#endif
+
+ /*
+ * If the update bit is off, or this denode is from a readonly
+ * filesystem, or this denode is for a directory, or the denode
+ * represents an open but unlinked file then don't do anything. DOS
+ * directory entries that describe a directory do not ever get
+ * updated. This is the way dos treats them.
+ */
+ if ((dep->de_flag & DE_UPDATE) == 0 ||
+ vp->v_mount->mnt_flag & MNT_RDONLY ||
+ dep->de_Attributes & ATTR_DIRECTORY ||
+ dep->de_refcnt <= 0)
+ return 0;
+
+ /*
+ * Read in the cluster containing the directory entry we want to
+ * update.
+ */
+ if (error = readde(dep, &bp, &dirp))
+ return error;
+
+ /*
+ * Put the passed in time into the directory entry.
+ */
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ unix2dostime(&ts, &dep->de_Date, &dep->de_Time);
+ dep->de_flag &= ~DE_UPDATE;
+
+ /*
+ * Copy the directory entry out of the denode into the cluster it
+ * came from.
+ */
+ DE_EXTERNALIZE(dirp, dep);
+
+ /*
+ * Write the cluster back to disk. If they asked for us to wait
+ * for the write to complete, then use bwrite() otherwise use
+ * bdwrite().
+ */
+ error = 0; /* note that error is 0 from above, but ... */
+ if (waitfor)
+ error = bwrite(bp);
+ else
+ bdwrite(bp);
+ return error;
+}
+
+/*
+ * Truncate the file described by dep to the length specified by length.
+ */
+int
+detrunc(dep, length, flags, cred, p)
+ struct denode *dep;
+ u_long length;
+ int flags;
+ struct ucred *cred;
+ struct proc *p;
+{
+ int error;
+ int allerror;
+ int vflags;
+ u_long eofentry;
+ u_long chaintofree;
+ daddr_t bn;
+ 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 %d, flags %d\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 (DETOV(dep)->v_flag & VROOT) {
+ printf("detrunc(): can't truncate root directory, clust %d, offset %d\n",
+ dep->de_dirclust, dep->de_diroffset);
+ return EINVAL;
+ }
+
+ vnode_pager_setsize(DETOV(dep), length);
+
+ if (dep->de_FileSize < length)
+ return deextend(dep, length, cred);
+
+ /*
+ * 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 {
+ if (error = pcbmap(dep, de_clcount(pmp, length) - 1, 0, &eofentry)) {
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): pcbmap fails %d\n", error);
+#endif
+ return error;
+ }
+ }
+
+ fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift);
+
+ /*
+ * 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) {
+ /*
+ * should read from file vnode or filesystem vnode
+ * depending on if file or dir
+ */
+ if (isadir) {
+ bn = cntobn(pmp, eofentry);
+ error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
+ NOCRED, &bp);
+ } else {
+ bn = de_blk(pmp, length);
+ error = bread(DETOV(dep), bn, pmp->pm_bpcluster,
+ NOCRED, &bp);
+ }
+ if (error) {
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): bread fails %d\n", error);
+#endif
+ return error;
+ }
+ vnode_pager_uncache(DETOV(dep)); /* what's this for? */
+ /*
+ * is this the right place for it?
+ */
+ bzero(bp->b_data + boff, 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;
+ dep->de_flag |= DE_UPDATE;
+ vflags = (length > 0 ? V_SAVE : 0) | V_SAVEMETA;
+ vinvalbuf(DETOV(dep), vflags, cred, p, 0, 0);
+ allerror = deupdat(dep, &time, 1);
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): allerror %d, eofentry %d\n",
+ allerror, eofentry);
+#endif
+
+ /*
+ * If we need to break the cluster chain for the file then do it
+ * now.
+ */
+ if (eofentry != ~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;
+ }
+ fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift,
+ eofentry);
+ }
+
+ /*
+ * Now free the clusters removed from the file because of the
+ * truncation.
+ */
+ if (chaintofree != 0 && !MSDOSFSEOF(chaintofree))
+ freeclusterchain(pmp, chaintofree);
+
+ return allerror;
+}
+
+/*
+ * Extend the file described by dep to length specified by length.
+ */
+int
+deextend(dep, length, cred)
+ struct denode *dep;
+ off_t length;
+ struct ucred *cred;
+{
+ struct msdosfsmount *pmp = dep->de_pmp;
+ u_long count;
+ int error;
+
+ /*
+ * The root of a DOS filesystem cannot be extended.
+ */
+ if (DETOV(dep)->v_flag & VROOT)
+ return EINVAL;
+
+ /*
+ * Directories can only be extended by the superuser.
+ * Is this really important?
+ */
+ if (dep->de_Attributes & ATTR_DIRECTORY) {
+ if (error = suser(cred, NULL))
+ return error;
+ }
+
+ if (length <= dep->de_FileSize)
+ panic("deextend: file too large");
+
+ /*
+ * 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;
+ if (error = extendfile(dep, count, NULL, NULL, DE_CLEAR)) {
+ /* truncate the added clusters away again */
+ (void) detrunc(dep, dep->de_FileSize, 0, cred, NULL);
+ return error;
+ }
+ }
+
+ dep->de_flag |= DE_UPDATE;
+ dep->de_FileSize = length;
+ return deupdat(dep, &time, 1);
+}
+
+/*
+ * Move a denode to its correct hash queue after the file it represents has
+ * been moved to a new directory.
+ */
+int reinsert(dep)
+ struct denode *dep;
+{
+ union dehead *deh;
+
+ /*
+ * Fix up the denode cache. If the denode is for a directory,
+ * there is nothing to do since the hash is based on the starting
+ * cluster of the directory file and that hasn't changed. If for a
+ * file the hash is based on the location of the directory entry,
+ * so we must remove it from the cache and re-enter it with the
+ * hash based on the new location of the directory entry.
+ */
+ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
+ msdosfs_hashrem(dep);
+ msdosfs_hashins(dep);
+ }
+ return 0;
+}
+
+int
+msdosfs_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct denode *dep = VTODE(vp);
+ int i;
+ extern int prtactive;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_reclaim(): dep %08x, file %s, refcnt %d\n",
+ dep, dep->de_Name, dep->de_refcnt);
+#endif
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("msdosfs_reclaim(): pushing active", vp);
+
+ /*
+ * Remove the denode from the denode hash chain we are in.
+ */
+ msdosfs_hashrem(dep);
+
+ cache_purge(vp);
+ /*
+ * Indicate that one less file on the filesystem is open.
+ */
+ if (dep->de_devvp) {
+ vrele(dep->de_devvp);
+ dep->de_devvp = 0;
+ }
+
+ dep->de_flag = 0;
+
+ FREE(dep, M_MSDOSFSNODE);
+ vp->v_data = NULL;
+
+ return 0;
+}
+
+int
+msdosfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct denode *dep = VTODE(vp);
+ int error = 0;
+ extern int prtactive;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_inactive(): dep %08x, de_Name[0] %x\n", dep, dep->de_Name[0]);
+#endif
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("msdosfs_inactive(): pushing active", vp);
+
+ /*
+ * Get rid of denodes related to stale file handles. Hmmm, what
+ * does this really do?
+ */
+ if (dep->de_Name[0] == SLOT_DELETED) {
+ if ((vp->v_flag & VXLOCK) == 0)
+ vgone(vp);
+ return 0;
+ }
+
+ /*
+ * If the file has been deleted and it is on a read/write
+ * filesystem, then truncate the file, and mark the directory slot
+ * as empty. (This may not be necessary for the dos filesystem.)
+ */
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_inactive(): dep %08x, refcnt %d, mntflag %x, MNT_RDONLY %x\n",
+ dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY);
+#endif
+ VOP_LOCK(vp);
+ if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+ error = detrunc(dep, (u_long) 0, 0, NOCRED, NULL);
+ dep->de_flag |= DE_UPDATE;
+ dep->de_Name[0] = SLOT_DELETED;
+ }
+ DE_UPDAT(dep, &time, 0);
+ VOP_UNLOCK(vp);
+ dep->de_flag = 0;
+
+ /*
+ * If we are done with the denode, then reclaim it so that it can
+ * be reused now.
+ */
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount,
+ dep->de_Name[0]);
+#endif
+ if (vp->v_usecount == 0 && dep->de_Name[0] == SLOT_DELETED)
+ vgone(vp);
+ return error;
+}
diff --git a/sys/fs/msdosfs/msdosfs_fat.c b/sys/fs/msdosfs/msdosfs_fat.c
new file mode 100644
index 0000000..9a2f117
--- /dev/null
+++ b/sys/fs/msdosfs/msdosfs_fat.c
@@ -0,0 +1,984 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_fat.c,v 1.12 1994/08/21 18:44:04 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * kernel include files.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/namei.h>
+#include <sys/mount.h> /* to define statfs structure */
+#include <sys/vnode.h> /* to define vattr structure */
+#include <sys/errno.h>
+
+/*
+ * msdosfs include files.
+ */
+#include <msdosfs/bpb.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/fat.h>
+
+/*
+ * Fat cache stats.
+ */
+int fc_fileextends; /* # of file extends */
+int fc_lfcempty; /* # of time last file cluster cache entry
+ * was empty */
+int fc_bmapcalls; /* # of times pcbmap was called */
+
+#define LMMAX 20
+int fc_lmdistance[LMMAX]; /* counters for how far off the last
+ * cluster mapped entry was. */
+int fc_largedistance; /* off by more than LMMAX */
+
+/* Byte offset in FAT on filesystem pmp, cluster cn */
+#define FATOFS(pmp, cn) (FAT12(pmp) ? (cn) * 3 / 2 : (cn) * 2)
+
+void fc_lookup(struct denode *dep, u_long findcn,
+ u_long *frcnp, u_long *fsrcnp);
+void fc_purge(struct denode *dep, u_int frcn);
+
+static void
+fatblock(pmp, ofs, bnp, sizep, bop)
+ struct msdosfsmount *pmp;
+ u_long ofs;
+ u_long *bnp;
+ u_long *sizep;
+ u_long *bop;
+{
+ u_long bn, size;
+
+ bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
+ size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn)
+ * pmp->pm_BytesPerSec;
+ bn += pmp->pm_fatblk;
+ if (bnp)
+ *bnp = bn;
+ if (sizep)
+ *sizep = size;
+ if (bop)
+ *bop = ofs % pmp->pm_fatblocksize;
+}
+
+/*
+ * Map the logical cluster number of a file into a physical disk sector
+ * that is filesystem relative.
+ *
+ * dep - address of denode representing the file of interest
+ * findcn - file relative cluster whose filesystem relative cluster number
+ * and/or block number are/is to be found
+ * bnp - address of where to place the file system relative block number.
+ * If this pointer is null then don't return this quantity.
+ * cnp - address of where to place the file system relative cluster number.
+ * If this pointer is null then don't return this quantity.
+ *
+ * NOTE: Either bnp or cnp must be non-null.
+ * This function has one side effect. If the requested file relative cluster
+ * is beyond the end of file, then the actual number of clusters in the file
+ * is returned in *cnp. This is useful for determining how long a directory is.
+ * If cnp is null, nothing is returned.
+ */
+int
+pcbmap(dep, findcn, bnp, cnp)
+ struct denode *dep;
+ u_long findcn; /* file relative cluster to get */
+ daddr_t *bnp; /* returned filesys relative blk number */
+ u_long *cnp; /* returned cluster number */
+{
+ int error;
+ u_long i;
+ u_long cn;
+ u_long prevcn;
+ u_long byteoffset;
+ u_long bn;
+ u_long bo;
+ struct buf *bp = NULL;
+ u_long bp_bn = -1;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ u_long bsize;
+ int fat12 = FAT12(pmp); /* 12 bit fat */
+
+ fc_bmapcalls++;
+
+ /*
+ * If they don't give us someplace to return a value then don't
+ * bother doing anything.
+ */
+ if (bnp == NULL && cnp == NULL)
+ return 0;
+
+ cn = dep->de_StartCluster;
+ /*
+ * The "file" that makes up the root directory is contiguous,
+ * permanently allocated, of fixed size, and is not made up of
+ * clusters. If the cluster number is beyond the end of the root
+ * directory, then return the number of clusters in the file.
+ */
+ if (cn == MSDOSFSROOT) {
+ if (dep->de_Attributes & ATTR_DIRECTORY) {
+ if (findcn * pmp->pm_SectPerClust > pmp->pm_rootdirsize) {
+ if (cnp)
+ *cnp = pmp->pm_rootdirsize / pmp->pm_SectPerClust;
+ return E2BIG;
+ }
+ if (bnp)
+ *bnp = pmp->pm_rootdirblk + (findcn * pmp->pm_SectPerClust);
+ if (cnp)
+ *cnp = MSDOSFSROOT;
+ return 0;
+ } else { /* just an empty file */
+ if (cnp)
+ *cnp = 0;
+ return E2BIG;
+ }
+ }
+
+ /*
+ * Rummage around in the fat cache, maybe we can avoid tromping
+ * thru every fat entry for the file. And, keep track of how far
+ * off the cache was from where we wanted to be.
+ */
+ i = 0;
+ fc_lookup(dep, findcn, &i, &cn);
+ if ((bn = findcn - i) >= LMMAX)
+ fc_largedistance++;
+ else
+ fc_lmdistance[bn]++;
+
+ /*
+ * Handle all other files or directories the normal way.
+ */
+ prevcn = 0;
+ for (; i < findcn; i++) {
+ if (MSDOSFSEOF(cn))
+ goto hiteof;
+ byteoffset = FATOFS(pmp, cn);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (bn != bp_bn) {
+ if (bp)
+ brelse(bp);
+ error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
+ if (error)
+ return error;
+ bp_bn = bn;
+ }
+ prevcn = cn;
+ cn = getushort(&bp->b_data[bo]);
+ if (fat12) {
+ if (prevcn & 1)
+ cn >>= 4;
+ cn &= 0x0fff;
+ /*
+ * Force the special cluster numbers in the range
+ * 0x0ff0-0x0fff to be the same as for 16 bit
+ * cluster numbers to let the rest of msdosfs think
+ * it is always dealing with 16 bit fats.
+ */
+ if ((cn & 0x0ff0) == 0x0ff0)
+ cn |= 0xf000;
+ }
+ }
+
+ if (!MSDOSFSEOF(cn)) {
+ if (bp)
+ brelse(bp);
+ if (bnp)
+ *bnp = cntobn(pmp, cn);
+ if (cnp)
+ *cnp = cn;
+ fc_setcache(dep, FC_LASTMAP, i, cn);
+ return 0;
+ }
+
+hiteof:;
+ if (cnp)
+ *cnp = i;
+ if (bp)
+ brelse(bp);
+ /* update last file cluster entry in the fat cache */
+ fc_setcache(dep, FC_LASTFC, i - 1, prevcn);
+ return E2BIG;
+}
+
+/*
+ * Find the closest entry in the fat cache to the cluster we are looking
+ * for.
+ */
+void fc_lookup(dep, findcn, frcnp, fsrcnp)
+ struct denode *dep;
+ u_long findcn;
+ u_long *frcnp;
+ u_long *fsrcnp;
+{
+ int i;
+ u_long cn;
+ struct fatcache *closest = 0;
+
+ for (i = 0; i < FC_SIZE; i++) {
+ cn = dep->de_fc[i].fc_frcn;
+ if (cn != FCE_EMPTY && cn <= findcn) {
+ if (closest == 0 || cn > closest->fc_frcn)
+ closest = &dep->de_fc[i];
+ }
+ }
+ if (closest) {
+ *frcnp = closest->fc_frcn;
+ *fsrcnp = closest->fc_fsrcn;
+ }
+}
+
+/*
+ * Purge the fat cache in denode dep of all entries relating to file
+ * relative cluster frcn and beyond.
+ */
+void fc_purge(dep, frcn)
+ struct denode *dep;
+ u_int frcn;
+{
+ int i;
+ struct fatcache *fcp;
+
+ fcp = dep->de_fc;
+ for (i = 0; i < FC_SIZE; i++, fcp++) {
+ if (fcp->fc_frcn >= frcn)
+ fcp->fc_frcn = FCE_EMPTY;
+ }
+}
+
+/*
+ * Update all copies of the fat. The first copy is updated last.
+ *
+ * pmp - msdosfsmount structure for filesystem to update
+ * bp - addr of modified fat block
+ * fatbn - block number relative to begin of filesystem of the modified fat block.
+ */
+void
+updatefats(pmp, bp, fatbn)
+ struct msdosfsmount *pmp;
+ struct buf *bp;
+ u_long fatbn;
+{
+ int i;
+ struct buf *bpn;
+
+#ifdef MSDOSFS_DEBUG
+ printf("updatefats(pmp %08x, bp %08x, fatbn %d)\n",
+ pmp, bp, fatbn);
+#endif
+
+ /*
+ * Now copy the block(s) of the modified fat to the other copies of
+ * the fat and write them out. This is faster than reading in the
+ * other fats and then writing them back out. This could tie up
+ * the fat for quite a while. Preventing others from accessing it.
+ * To prevent us from going after the fat quite so much we use
+ * delayed writes, unless they specfied "synchronous" when the
+ * filesystem was mounted. If synch is asked for then use
+ * bwrite()'s and really slow things down.
+ */
+ for (i = 1; i < pmp->pm_FATs; i++) {
+ fatbn += pmp->pm_FATsecs;
+ /* getblk() never fails */
+ bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount, 0, 0);
+ bcopy(bp->b_data, bpn->b_data, bp->b_bcount);
+ if (pmp->pm_waitonfat)
+ bwrite(bpn);
+ else
+ bdwrite(bpn);
+ }
+ /*
+ * Write out the first fat last.
+ */
+ if (pmp->pm_waitonfat)
+ bwrite(bp);
+ else
+ bdwrite(bp);
+}
+
+/*
+ * Updating entries in 12 bit fats is a pain in the butt.
+ *
+ * The following picture shows where nibbles go when moving from a 12 bit
+ * cluster number into the appropriate bytes in the FAT.
+ *
+ * byte m byte m+1 byte m+2
+ * +----+----+ +----+----+ +----+----+
+ * | 0 1 | | 2 3 | | 4 5 | FAT bytes
+ * +----+----+ +----+----+ +----+----+
+ *
+ * +----+----+----+ +----+----+----+
+ * | 3 0 1 | | 4 5 2 |
+ * +----+----+----+ +----+----+----+
+ * cluster n cluster n+1
+ *
+ * Where n is even. m = n + (n >> 2)
+ *
+ */
+extern __inline void
+usemap_alloc(pmp, cn)
+ struct msdosfsmount *pmp;
+ u_long cn;
+{
+ pmp->pm_inusemap[cn / N_INUSEBITS]
+ |= 1 << (cn % N_INUSEBITS);
+ pmp->pm_freeclustercount--;
+}
+
+extern __inline void
+usemap_free(pmp, cn)
+ struct msdosfsmount *pmp;
+ u_long cn;
+{
+ pmp->pm_freeclustercount++;
+ pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS));
+}
+
+int
+clusterfree(pmp, cluster, oldcnp)
+ struct msdosfsmount *pmp;
+ u_long cluster;
+ u_long *oldcnp;
+{
+ int error;
+ u_long oldcn;
+
+ error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE);
+ if (error == 0) {
+ /*
+ * If the cluster was successfully marked free, then update
+ * the count of free clusters, and turn off the "allocated"
+ * bit in the "in use" cluster bit map.
+ */
+ usemap_free(pmp, cluster);
+ if (oldcnp)
+ *oldcnp = oldcn;
+ }
+ return error;
+}
+
+/*
+ * Get or Set or 'Get and Set' the cluster'th entry in the fat.
+ *
+ * function - whether to get or set a fat entry
+ * pmp - address of the msdosfsmount structure for the filesystem
+ * whose fat is to be manipulated.
+ * cn - which cluster is of interest
+ * oldcontents - address of a word that is to receive the contents of the
+ * cluster'th entry if this is a get function
+ * newcontents - the new value to be written into the cluster'th element of
+ * the fat if this is a set function.
+ *
+ * This function can also be used to free a cluster by setting the fat entry
+ * for a cluster to 0.
+ *
+ * All copies of the fat are updated if this is a set function. NOTE: If
+ * fatentry() marks a cluster as free it does not update the inusemap in
+ * the msdosfsmount structure. This is left to the caller.
+ */
+int
+fatentry(function, pmp, cn, oldcontents, newcontents)
+ int function;
+ struct msdosfsmount *pmp;
+ u_long cn;
+ u_long *oldcontents;
+ u_long newcontents;
+{
+ int error;
+ u_long readcn;
+ u_long bn, bo, bsize, byteoffset;
+ struct buf *bp;
+
+ /*
+ * printf("fatentry(func %d, pmp %08x, clust %d, oldcon %08x, newcon %d)\n",
+ * function, pmp, cluster, oldcontents, newcontents);
+ */
+
+#ifdef DIAGNOSTIC
+ /*
+ * Be sure they asked us to do something.
+ */
+ if ((function & (FAT_SET | FAT_GET)) == 0) {
+ printf("fatentry(): function code doesn't specify get or set\n");
+ return EINVAL;
+ }
+
+ /*
+ * If they asked us to return a cluster number but didn't tell us
+ * where to put it, give them an error.
+ */
+ if ((function & FAT_GET) && oldcontents == NULL) {
+ printf("fatentry(): get function with no place to put result\n");
+ return EINVAL;
+ }
+#endif
+
+ /*
+ * Be sure the requested cluster is in the filesystem.
+ */
+ if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster)
+ return EINVAL;
+
+ byteoffset = FATOFS(pmp, cn);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp))
+ return error;
+
+ if (function & FAT_GET) {
+ readcn = getushort(&bp->b_data[bo]);
+ if (FAT12(pmp)) {
+ if (cn & 1)
+ readcn >>= 4;
+ readcn &= 0x0fff;
+ /* map certain 12 bit fat entries to 16 bit */
+ if ((readcn & 0x0ff0) == 0x0ff0)
+ readcn |= 0xf000;
+ }
+ *oldcontents = readcn;
+ }
+ if (function & FAT_SET) {
+ if (FAT12(pmp)) {
+ readcn = getushort(&bp->b_data[bo]);
+ if (cn & 1) {
+ readcn &= 0x000f;
+ readcn |= newcontents << 4;
+ } else {
+ readcn &= 0xf000;
+ readcn |= newcontents & 0xfff;
+ }
+ putushort(&bp->b_data[bo], readcn);
+ } else
+ putushort(&bp->b_data[bo], newcontents);
+ updatefats(pmp, bp, bn);
+ bp = NULL;
+ pmp->pm_fmod = 1;
+ }
+ if (bp)
+ brelse(bp);
+ return 0;
+}
+
+/*
+ * Update a contiguous cluster chain
+ *
+ * pmp - mount point
+ * start - first cluster of chain
+ * count - number of clusters in chain
+ * fillwith - what to write into fat entry of last cluster
+ */
+static int
+fatchain(pmp, start, count, fillwith)
+ struct msdosfsmount *pmp;
+ u_long start;
+ u_long count;
+ u_long fillwith;
+{
+ int error;
+ u_long bn, bo, bsize, byteoffset, readcn, newc;
+ struct buf *bp;
+
+#ifdef MSDOSFS_DEBUG
+ printf("fatchain(pmp %08x, start %d, count %d, fillwith %d)\n",
+ pmp, start, count, fillwith);
+#endif
+ /*
+ * Be sure the clusters are in the filesystem.
+ */
+ if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster)
+ return EINVAL;
+
+ while (count > 0) {
+ byteoffset = FATOFS(pmp, start);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp))
+ return error;
+ while (count > 0) {
+ start++;
+ newc = --count > 0 ? start : fillwith;
+ if (FAT12(pmp)) {
+ readcn = getushort(&bp->b_data[bo]);
+ if (start & 1) {
+ readcn &= 0xf000;
+ readcn |= newc & 0xfff;
+ } else {
+ readcn &= 0x000f;
+ readcn |= newc << 4;
+ }
+ putushort(&bp->b_data[bo], readcn);
+ bo++;
+ if (!(start & 1))
+ bo++;
+ } else {
+ putushort(&bp->b_data[bo], newc);
+ bo += 2;
+ }
+ if (bo >= bsize)
+ break;
+ }
+ updatefats(pmp, bp, bn);
+ }
+ pmp->pm_fmod = 1;
+ return 0;
+}
+
+/*
+ * Check the length of a free cluster chain starting at start.
+ *
+ * pmp - mount point
+ * start - start of chain
+ * count - maximum interesting length
+ */
+int
+chainlength(pmp, start, count)
+ struct msdosfsmount *pmp;
+ u_long start;
+ u_long count;
+{
+ u_long idx, max_idx;
+ u_int map;
+ u_long len;
+
+ max_idx = pmp->pm_maxcluster / N_INUSEBITS;
+ idx = start / N_INUSEBITS;
+ start %= N_INUSEBITS;
+ map = pmp->pm_inusemap[idx];
+ map &= ~((1 << start) - 1);
+ if (map) {
+ len = ffs(map) - 1 - start;
+ return len > count ? count : len;
+ }
+ len = N_INUSEBITS - start;
+ if (len >= count)
+ return count;
+ while (++idx <= max_idx) {
+ if (len >= count)
+ break;
+ if (map = pmp->pm_inusemap[idx]) {
+ len += ffs(map) - 1;
+ break;
+ }
+ len += N_INUSEBITS;
+ }
+ return len > count ? count : len;
+}
+
+/*
+ * Allocate contigous free clusters.
+ *
+ * pmp - mount point.
+ * start - start of cluster chain.
+ * count - number of clusters to allocate.
+ * fillwith - put this value into the fat entry for the
+ * last allocated cluster.
+ * retcluster - put the first allocated cluster's number here.
+ * got - how many clusters were actually allocated.
+ */
+int
+chainalloc(pmp, start, count, fillwith, retcluster, got)
+ struct msdosfsmount *pmp;
+ u_long start;
+ u_long count;
+ u_long fillwith;
+ u_long *retcluster;
+ u_long *got;
+{
+ int error;
+
+ error = fatchain(pmp, start, count, fillwith);
+ if (error == 0) {
+#ifdef MSDOSFS_DEBUG
+ printf("clusteralloc(): allocated cluster chain at %d (%d clusters)\n",
+ start, count);
+#endif
+ if (retcluster)
+ *retcluster = start;
+ if (got)
+ *got = count;
+ while (count-- > 0)
+ usemap_alloc(pmp, start++);
+ }
+ return error;
+}
+
+/*
+ * Allocate contiguous free clusters.
+ *
+ * pmp - mount point.
+ * start - preferred start of cluster chain.
+ * count - number of clusters requested.
+ * fillwith - put this value into the fat entry for the
+ * last allocated cluster.
+ * retcluster - put the first allocated cluster's number here.
+ * got - how many clusters were actually allocated.
+ */
+int
+clusteralloc(pmp, start, count, fillwith, retcluster, got)
+ struct msdosfsmount *pmp;
+ u_long start;
+ u_long count;
+ u_long fillwith;
+ u_long *retcluster;
+ u_long *got;
+{
+ int error;
+ u_long idx;
+ u_long len, newst, foundcn, foundl, cn, l;
+ u_int map;
+
+#ifdef MSDOSFS_DEBUG
+ printf("clusteralloc(): find %d clusters\n",count);
+#endif
+ if (start) {
+ if ((len = chainlength(pmp, start, count)) >= count)
+ return chainalloc(pmp, start, count, fillwith, retcluster, got);
+ } else {
+ /*
+ * This is a new file, initialize start
+ */
+ struct timeval tv;
+
+ microtime(&tv);
+ start = (tv.tv_usec >> 10)|tv.tv_usec;
+ len = 0;
+ }
+
+ /*
+ * Start at a (pseudo) random place to maximize cluster runs
+ * under multiple writers.
+ */
+ foundcn = newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1);
+ foundl = 0;
+
+ for (cn = newst; cn <= pmp->pm_maxcluster;) {
+ idx = cn / N_INUSEBITS;
+ map = pmp->pm_inusemap[idx];
+ map |= (1 << (cn % N_INUSEBITS)) - 1;
+ if (map != (u_int)-1) {
+ cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1;
+ if ((l = chainlength(pmp, cn, count)) >= count)
+ return chainalloc(pmp, cn, count, fillwith, retcluster, got);
+ if (l > foundl) {
+ foundcn = cn;
+ foundl = l;
+ }
+ cn += l + 1;
+ continue;
+ }
+ cn += N_INUSEBITS - cn % N_INUSEBITS;
+ }
+ for (cn = 0; cn < newst;) {
+ idx = cn / N_INUSEBITS;
+ map = pmp->pm_inusemap[idx];
+ map |= (1 << (cn % N_INUSEBITS)) - 1;
+ if (map != (u_int)-1) {
+ cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1;
+ if ((l = chainlength(pmp, cn, count)) >= count)
+ return chainalloc(pmp, cn, count, fillwith, retcluster, got);
+ if (l > foundl) {
+ foundcn = cn;
+ foundl = l;
+ }
+ cn += l + 1;
+ continue;
+ }
+ cn += N_INUSEBITS - cn % N_INUSEBITS;
+ }
+
+ if (!foundl)
+ return ENOSPC;
+
+ if (len)
+ return chainalloc(pmp, start, len, fillwith, retcluster, got);
+ else
+ return chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got);
+}
+
+
+/*
+ * Free a chain of clusters.
+ *
+ * pmp - address of the msdosfs mount structure for the filesystem
+ * containing the cluster chain to be freed.
+ * startcluster - number of the 1st cluster in the chain of clusters to be
+ * freed.
+ */
+int
+freeclusterchain(pmp, cluster)
+ struct msdosfsmount *pmp;
+ u_long cluster;
+{
+ int error = 0;
+ struct buf *bp = NULL;
+ u_long bn, bo, bsize, byteoffset;
+ u_long readcn, lbn = -1;
+
+ while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) {
+ byteoffset = FATOFS(pmp, cluster);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (lbn != bn) {
+ if (bp)
+ updatefats(pmp, bp, bn);
+ if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp))
+ return error;
+ lbn = bn;
+ }
+ usemap_free(pmp, cluster);
+ readcn = getushort(&bp->b_data[bo]);
+ if (FAT12(pmp)) {
+ if (cluster & 1) {
+ cluster = readcn >> 4;
+ readcn &= 0x000f;
+ readcn |= MSDOSFSFREE << 4;
+ } else {
+ cluster = readcn;
+ readcn &= 0xf000;
+ readcn |= MSDOSFSFREE & 0xfff;
+ }
+ putushort(&bp->b_data[bo], readcn);
+ cluster &= 0x0fff;
+ if ((cluster&0x0ff0) == 0x0ff0)
+ cluster |= 0xf000;
+ } else {
+ cluster = readcn;
+ putushort(&bp->b_data[bo], MSDOSFSFREE);
+ }
+ }
+ if (bp)
+ updatefats(pmp, bp, bn);
+ return error;
+}
+
+/*
+ * Read in fat blocks looking for free clusters. For every free cluster
+ * found turn off its corresponding bit in the pm_inusemap.
+ */
+int
+fillinusemap(pmp)
+ struct msdosfsmount *pmp;
+{
+ struct buf *bp = NULL;
+ u_long cn, readcn;
+ int error;
+ int fat12 = FAT12(pmp);
+ u_long bn, bo, bsize, byteoffset;
+
+ /*
+ * Mark all clusters in use, we mark the free ones in the fat scan
+ * loop further down.
+ */
+ for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++)
+ pmp->pm_inusemap[cn] = (u_int)-1;
+
+ /*
+ * Figure how many free clusters are in the filesystem by ripping
+ * through the fat counting the number of entries whose content is
+ * zero. These represent free clusters.
+ */
+ pmp->pm_freeclustercount = 0;
+ for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) {
+ byteoffset = FATOFS(pmp, cn);
+ bo = byteoffset % pmp->pm_fatblocksize;
+ if (!bo || !bp) {
+ /* Read new FAT block */
+ if (bp)
+ brelse(bp);
+ fatblock(pmp, byteoffset, &bn, &bsize, NULL);
+ error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
+ if (error)
+ return error;
+ }
+ readcn = getushort(&bp->b_data[bo]);
+ if (fat12) {
+ if (cn & 1)
+ readcn >>= 4;
+ readcn &= 0x0fff;
+ }
+
+ if (readcn == 0)
+ usemap_free(pmp, cn);
+ }
+ brelse(bp);
+ return 0;
+}
+
+/*
+ * Allocate a new cluster and chain it onto the end of the file.
+ *
+ * dep - the file to extend
+ * count - number of clusters to allocate
+ * bpp - where to return the address of the buf header for the first new
+ * file block
+ * ncp - where to put cluster number of the first newly allocated cluster
+ * If this pointer is 0, do not return the cluster number.
+ * flags - see fat.h
+ *
+ * NOTE: This function is not responsible for turning on the DE_UPDATE bit of
+ * the de_flag field of the denode and it does not change the de_FileSize
+ * field. This is left for the caller to do.
+ */
+int
+extendfile(dep, count, bpp, ncp, flags)
+ struct denode *dep;
+ u_long count;
+ struct buf **bpp;
+ u_long *ncp;
+ int flags;
+{
+ int error = 0;
+ u_long frcn;
+ u_long cn, got;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct buf *bp;
+
+ /*
+ * Don't try to extend the root directory
+ */
+ if (DETOV(dep)->v_flag & VROOT) {
+ printf("extendfile(): attempt to extend root directory\n");
+ return ENOSPC;
+ }
+
+ /*
+ * If the "file's last cluster" cache entry is empty, and the file
+ * is not empty, then fill the cache entry by calling pcbmap().
+ */
+ fc_fileextends++;
+ if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY &&
+ dep->de_StartCluster != 0) {
+ fc_lfcempty++;
+ error = pcbmap(dep, 0xffff, 0, &cn);
+ /* we expect it to return E2BIG */
+ if (error != E2BIG)
+ return error;
+ error = 0;
+ }
+
+ while (count > 0) {
+ /*
+ * Allocate a new cluster chain and cat onto the end of the file.
+ * If the file is empty we make de_StartCluster point to the new
+ * block. Note that de_StartCluster being 0 is sufficient to be
+ * sure the file is empty since we exclude attempts to extend the
+ * root directory above, and the root dir is the only file with a
+ * startcluster of 0 that has blocks allocated (sort of).
+ */
+ if (dep->de_StartCluster == 0)
+ cn = 0;
+ else
+ cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1;
+ if (error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got))
+ return error;
+
+ count -= got;
+
+ /*
+ * Give them the filesystem relative cluster number if they want
+ * it.
+ */
+ if (ncp) {
+ *ncp = cn;
+ ncp = NULL;
+ }
+
+ if (dep->de_StartCluster == 0) {
+ dep->de_StartCluster = cn;
+ frcn = 0;
+ } else {
+ error = fatentry(FAT_SET, pmp, dep->de_fc[FC_LASTFC].fc_fsrcn,
+ 0, cn);
+ if (error) {
+ clusterfree(pmp, cn, NULL);
+ return error;
+ }
+
+ frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1;
+ }
+
+ /*
+ * Update the "last cluster of the file" entry in the denode's fat
+ * cache.
+ */
+ fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1);
+
+ if (flags & DE_CLEAR) {
+ while (got-- > 0) {
+ /*
+ * Get the buf header for the new block of the file.
+ */
+ if (dep->de_Attributes & ATTR_DIRECTORY)
+ bp = getblk(pmp->pm_devvp, cntobn(pmp, cn++),
+ pmp->pm_bpcluster, 0, 0);
+ else {
+ bp = getblk(DETOV(dep), frcn++, pmp->pm_bpcluster, 0, 0);
+ /*
+ * Do the bmap now, as in msdosfs_write
+ */
+ if (pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0))
+ bp->b_blkno = -1;
+ if (bp->b_blkno == -1)
+ panic("extendfile: pcbmap");
+ }
+ clrbuf(bp);
+ if (bpp) {
+ *bpp = bp;
+ bpp = NULL;
+ } else {
+ bp->b_flags |= B_AGE;
+ bawrite(bp);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/sys/fs/msdosfs/msdosfs_lookup.c b/sys/fs/msdosfs/msdosfs_lookup.c
new file mode 100644
index 0000000..c67f888
--- /dev/null
+++ b/sys/fs/msdosfs/msdosfs_lookup.c
@@ -0,0 +1,763 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_lookup.c,v 1.14 1994/08/21 18:44:07 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <msdosfs/bpb.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/fat.h>
+
+/*
+ * 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
+ * soul 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.
+ */
+int
+msdosfs_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ struct vnode *vdp = ap->a_dvp;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ daddr_t bn;
+ int error;
+ int lockparent;
+ int wantparent;
+ int slotstatus;
+
+#define NONE 0
+#define FOUND 1
+ int slotoffset = -1;
+ int slotcluster = -1;
+ int frcn;
+ u_long cluster;
+ int rootreloff;
+ int diroff;
+ int isadir; /* ~0 if found direntry is a directory */
+ u_long scn; /* starting cluster number */
+ struct vnode *pdp;
+ struct denode *dp;
+ struct denode *tdp;
+ struct msdosfsmount *pmp;
+ struct buf *bp = 0;
+ struct direntry *dep = NULL;
+ u_char dosfilename[12];
+ int flags = cnp->cn_flags;
+ int nameiop = cnp->cn_nameiop;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr);
+#endif
+ dp = VTODE(vdp);
+ pmp = dp->de_pmp;
+ *vpp = NULL;
+ lockparent = flags & LOCKPARENT;
+ wantparent = flags & (LOCKPARENT | WANTPARENT);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): vdp %08x, dp %08x, Attr %02x\n",
+ vdp, dp, dp->de_Attributes);
+#endif
+
+ /*
+ * Be sure vdp is a directory. Since dos filesystems don't have
+ * the concept of execute permission anybody can search a
+ * directory.
+ */
+ if ((dp->de_Attributes & ATTR_DIRECTORY) == 0)
+ return ENOTDIR;
+
+ /*
+ * See if the component of the pathname we are looking for is in
+ * the directory cache. If so then do a few things and return.
+ */
+ if (error = cache_lookup(vdp, vpp, cnp)) {
+ int vpid;
+
+ if (error == ENOENT)
+ return error;
+ pdp = vdp;
+ vdp = *vpp;
+ dp = VTODE(vdp);
+ vpid = vdp->v_id;
+ if (pdp == vdp) {
+ VREF(vdp);
+ error = 0;
+ } else if (flags & ISDOTDOT) {
+ VOP_UNLOCK(pdp);
+ error = vget(vdp, 1);
+ if (!error && lockparent && (flags & ISLASTCN))
+ error = VOP_LOCK(pdp);
+ } else {
+ error = vget(vdp, 1);
+ if (!lockparent || error || !(flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ }
+
+ if (!error) {
+ if (vpid == vdp->v_id) {
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): cache hit, vnode %08x, file %s\n",
+ vdp, dp->de_Name);
+#endif
+ return 0;
+ }
+ vput(vdp);
+ if (lockparent && pdp != vdp && (flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ }
+ if (error = VOP_LOCK(pdp))
+ return error;
+ vdp = pdp;
+ dp = VTODE(vdp);
+ *vpp = NULL;
+ }
+
+ /*
+ * If they are going after the . or .. entry in the root directory,
+ * they won't find it. DOS filesystems don't have them in the root
+ * directory. So, we fake it. deget() is in on this scam too.
+ */
+ if ((vdp->v_flag & VROOT) && cnp->cn_nameptr[0] == '.' &&
+ (cnp->cn_namelen == 1 ||
+ (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {
+ isadir = ATTR_DIRECTORY;
+ scn = MSDOSFSROOT;
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): looking for . or .. in root directory\n");
+#endif
+ cluster = MSDOSFSROOT;
+ diroff = MSDOSFSROOT_OFS;
+ goto foundroot;
+ }
+
+ /*
+ * Don't search for free slots unless we are creating a filename
+ * and we are at the end of the pathname.
+ */
+ slotstatus = FOUND;
+ if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) {
+ slotstatus = NONE;
+ slotoffset = -1;
+ }
+
+ unix2dosfn((u_char *) cnp->cn_nameptr, dosfilename, cnp->cn_namelen);
+ dosfilename[11] = 0;
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): dos version of filename %s, length %d\n",
+ dosfilename, cnp->cn_namelen);
+#endif
+ /*
+ * Search the directory pointed at by vdp for the name pointed at
+ * by cnp->cn_nameptr.
+ */
+ tdp = NULL;
+ /*
+ * 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.
+ */
+ rootreloff = 0;
+ for (frcn = 0;; frcn++) {
+ if (error = pcbmap(dp, frcn, &bn, &cluster)) {
+ if (error == E2BIG)
+ break;
+ return error;
+ }
+ if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp))
+ return error;
+ for (diroff = 0; diroff < pmp->pm_depclust; diroff++) {
+ dep = (struct direntry *) bp->b_data + diroff;
+
+ /*
+ * 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) {
+ if (slotstatus != FOUND) {
+ slotstatus = FOUND;
+ if (cluster == MSDOSFSROOT)
+ slotoffset = rootreloff;
+ else
+ slotoffset = diroff;
+ slotcluster = cluster;
+ }
+ if (dep->deName[0] == SLOT_EMPTY) {
+ brelse(bp);
+ goto notfound;
+ }
+ } else {
+ /*
+ * Ignore volume labels (anywhere, not just
+ * the root directory).
+ */
+ if ((dep->deAttributes & ATTR_VOLUME) == 0 &&
+ bcmp(dosfilename, dep->deName, 11) == 0) {
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): match diroff %d, rootreloff %d\n",
+ diroff, rootreloff);
+#endif
+ /*
+ * Remember where this directory
+ * entry came from for whoever did
+ * this lookup. If this is the root
+ * directory we are interested in
+ * the offset relative to the
+ * beginning of the directory (not
+ * the beginning of the cluster).
+ */
+ if (cluster == MSDOSFSROOT)
+ diroff = rootreloff;
+ dp->de_fndoffset = diroff;
+ dp->de_fndclust = cluster;
+ goto found;
+ }
+ }
+ rootreloff++;
+ } /* for (diroff = 0; .... */
+ /*
+ * Release the buffer holding the directory cluster just
+ * searched.
+ */
+ brelse(bp);
+ } /* 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.
+ */
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): op %d, refcnt %d, slotstatus %d\n",
+ nameiop, dp->de_refcnt, slotstatus);
+ printf(" slotoffset %d, slotcluster %d\n",
+ slotoffset, slotcluster);
+#endif
+ if ((nameiop == CREATE || nameiop == RENAME) &&
+ (flags & ISLASTCN) && dp->de_refcnt != 0) {
+ if (slotstatus == NONE) {
+ dp->de_fndoffset = (u_long)-1;
+ dp->de_fndclust = (u_long)-1;
+ } else {
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): saving empty slot location\n");
+#endif
+ dp->de_fndoffset = slotoffset;
+ dp->de_fndclust = slotcluster;
+ }
+ /* dp->de_flag |= DE_UPDATE; never update dos directories */
+ cnp->cn_flags |= SAVENAME;
+ if (!lockparent)/* leave searched dir locked? */
+ VOP_UNLOCK(vdp);
+ return EJUSTRETURN;
+ }
+ /*
+ * Insert name in cache as non-existant if not trying to create it.
+ */
+ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
+ cache_enter(vdp, *vpp, cnp);
+ return ENOENT;
+
+found: ;
+ /*
+ * NOTE: We still have the buffer with matched directory entry at
+ * this point.
+ */
+ isadir = dep->deAttributes & ATTR_DIRECTORY;
+ scn = getushort(dep->deStartCluster);
+
+foundroot:;
+ /*
+ * If we entered at foundroot, then we are looking for the . or ..
+ * entry of the filesystems root directory. isadir and scn were
+ * setup before jumping here. And, bp is null. There is no buf
+ * header.
+ */
+
+ /*
+ * If deleting and at the end of the path, then if we matched on
+ * "." then don't deget() we would probably panic(). Otherwise
+ * deget() the directory entry.
+ */
+ if (nameiop == DELETE && (flags & ISLASTCN)) {
+ if (dp->de_StartCluster == scn && isadir) { /* "." */
+ VREF(vdp);
+ *vpp = vdp;
+ if (bp)
+ brelse(bp);
+ return 0;
+ }
+ error = deget(pmp, cluster, diroff, dep, &tdp);
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return error;
+ }
+ *vpp = DETOV(tdp);
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ if (bp)
+ brelse(bp);
+ return 0;
+ }
+
+ /*
+ * If renaming.
+ */
+ if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
+ if (dp->de_StartCluster == scn && isadir) {
+ if (bp)
+ brelse(bp);
+ return EISDIR;
+ }
+ error = deget(pmp, cluster, diroff, dep, &tdp);
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return error;
+ }
+ *vpp = DETOV(tdp);
+ cnp->cn_flags |= SAVENAME;
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ if (bp)
+ brelse(bp);
+ return 0;
+ }
+
+ /*
+ * ?
+ */
+ pdp = vdp;
+ if (flags & ISDOTDOT) {
+ VOP_UNLOCK(pdp);
+ error = deget(pmp, cluster, diroff, dep, &tdp);
+ if (error) {
+ VOP_LOCK(pdp);
+ if (bp)
+ brelse(bp);
+ return error;
+ }
+ if (lockparent && (flags & ISLASTCN)
+ && (error = VOP_LOCK(pdp))) {
+ vput(DETOV(tdp));
+ return error;
+ }
+ *vpp = DETOV(tdp);
+ } else if (dp->de_StartCluster == scn && isadir) { /* "." */
+ VREF(vdp);
+ *vpp = vdp;
+ } else {
+ error = deget(pmp, cluster, diroff, dep, &tdp);
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return error;
+ }
+ if (!lockparent || !(flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ *vpp = DETOV(tdp);
+ }
+ if (bp)
+ brelse(bp);
+
+ /*
+ * Insert name in cache if wanted.
+ */
+ if (cnp->cn_flags & MAKEENTRY)
+ cache_enter(vdp, *vpp, cnp);
+ return 0;
+}
+
+/*
+ * dep - directory entry to copy into the directory
+ * ddep - directory to add to
+ * depp - return the address of the denode for the created directory entry
+ * if depp != 0
+ */
+int
+createde(dep, ddep, depp)
+ struct denode *dep;
+ struct denode *ddep;
+ struct denode **depp;
+{
+ int bn;
+ int error;
+ u_long dirclust, diroffset;
+ struct direntry *ndep;
+ struct msdosfsmount *pmp = ddep->de_pmp;
+ struct buf *bp;
+
+#ifdef MSDOSFS_DEBUG
+ printf("createde(dep %08x, ddep %08x, depp %08x)\n", dep, ddep, depp);
+#endif
+
+ /*
+ * If no space left in the directory then allocate another cluster
+ * and chain it onto the end of the file. There is one exception
+ * to this. That is, if the root directory has no more space it
+ * can NOT be expanded. extendfile() checks for and fails attempts
+ * to extend the root directory. We just return an error in that
+ * case.
+ */
+ if (ddep->de_fndclust == (u_long)-1) {
+ if (error = extendfile(ddep, 1, &bp, &dirclust, DE_CLEAR))
+ return error;
+ ndep = (struct direntry *) bp->b_data;
+ /*
+ * Let caller know where we put the directory entry.
+ */
+ ddep->de_fndclust = dirclust;
+ ddep->de_fndoffset = diroffset = 0;
+ /*
+ * Update the size of the directory
+ */
+ ddep->de_FileSize += pmp->pm_bpcluster;
+ } else {
+ /*
+ * There is space in the existing directory. So, we just
+ * read in the cluster with space. Copy the new directory
+ * entry in. Then write it to disk. NOTE: DOS directories
+ * do not get smaller as clusters are emptied.
+ */
+ dirclust = ddep->de_fndclust;
+ diroffset = ddep->de_fndoffset;
+
+ error = readep(pmp, dirclust, diroffset, &bp, &ndep);
+ if (error)
+ return error;
+ }
+ DE_EXTERNALIZE(ndep, dep);
+
+ /*
+ * If they want us to return with the denode gotten.
+ */
+ if (depp) {
+ error = deget(pmp, dirclust, diroffset, ndep, depp);
+ if (error)
+ return error;
+ }
+ if (error = bwrite(bp)) {
+ vput(DETOV(*depp)); /* free the vnode we got on error */
+ return error;
+ }
+ return 0;
+}
+
+/*
+ * Read in a directory entry and mark it as being deleted.
+ */
+int
+markdeleted(pmp, dirclust, diroffset)
+ struct msdosfsmount *pmp;
+ u_long dirclust;
+ u_long diroffset;
+{
+ int error;
+ struct direntry *ep;
+ struct buf *bp;
+
+ error = readep(pmp, dirclust, diroffset, &bp, &ep);
+ if (error)
+ return error;
+ ep->deName[0] = SLOT_DELETED;
+ return bwrite(bp);
+}
+
+/*
+ * Remove a directory entry. At this point the file represented by the
+ * directory entry to be removed is still full length until no one has it
+ * open. When the file no longer being used msdosfs_inactive() is called
+ * and will truncate the file to 0 length. When the vnode containing the
+ * denode is needed for some other purpose by VFS it will call
+ * msdosfs_reclaim() which will remove the denode from the denode cache.
+ */
+int
+removede(pdep,dep)
+ struct denode *pdep; /* directory where the entry is removed */
+ struct denode *dep; /* file to be removed */
+{
+ struct msdosfsmount *pmp = pdep->de_pmp;
+ int error;
+
+#ifdef MSDOSFS_DEBUG
+ printf("removede(): filename %s\n", dep->de_Name);
+ printf("removede(): dep %08x, ndpcluster %d, ndpoffset %d\n",
+ dep, pdep->de_fndclust, pdep->de_fndoffset);
+#endif
+
+ /*
+ * Read the directory block containing the directory entry we are
+ * to make free. The nameidata structure holds the cluster number
+ * and directory entry index number of the entry to free.
+ */
+ error = markdeleted(pmp, pdep->de_fndclust, pdep->de_fndoffset);
+
+ if (error == 0)
+ dep->de_refcnt--;
+ return error;
+}
+
+/*
+ * Be sure a directory is empty except for "." and "..". Return 1 if empty,
+ * return 0 if not empty or error.
+ */
+int
+dosdirempty(dep)
+ struct denode *dep;
+{
+ int dei;
+ int error;
+ u_long cn;
+ daddr_t bn;
+ struct buf *bp;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct direntry *dentp;
+
+ /*
+ * Since the filesize field in directory entries for a directory is
+ * zero, we just have to feel our way through the directory until
+ * we hit end of file.
+ */
+ for (cn = 0;; cn++) {
+ error = pcbmap(dep, cn, &bn, 0);
+ if (error == E2BIG)
+ return 1; /* it's empty */
+ error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED,
+ &bp);
+ if (error)
+ return error;
+ dentp = (struct direntry *) bp->b_data;
+ for (dei = 0; dei < pmp->pm_depclust; dei++) {
+ if (dentp->deName[0] != SLOT_DELETED) {
+ /*
+ * In dos directories an entry whose name
+ * starts with SLOT_EMPTY (0) starts the
+ * beginning of the unused part of the
+ * directory, so we can just return that it
+ * is empty.
+ */
+ if (dentp->deName[0] == SLOT_EMPTY) {
+ brelse(bp);
+ return 1;
+ }
+ /*
+ * Any names other than "." and ".." in a
+ * directory mean it is not empty.
+ */
+ if (bcmp(dentp->deName, ". ", 11) &&
+ bcmp(dentp->deName, ".. ", 11)) {
+ brelse(bp);
+#ifdef MSDOSFS_DEBUG
+ printf("dosdirempty(): entry %d found %02x, %02x\n",
+ dei, dentp->deName[0], dentp->deName[1]);
+#endif
+ return 0; /* not empty */
+ }
+ }
+ dentp++;
+ }
+ brelse(bp);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Check to see if the directory described by target is in some
+ * subdirectory of source. This prevents something like the following from
+ * succeeding and leaving a bunch or files and directories orphaned. mv
+ * /a/b/c /a/b/c/d/e/f Where c and f are directories.
+ *
+ * source - the inode for /a/b/c
+ * target - the inode for /a/b/c/d/e/f
+ *
+ * Returns 0 if target is NOT a subdirectory of source.
+ * Otherwise returns a non-zero error number.
+ * The target inode is always unlocked on return.
+ */
+int
+doscheckpath(source, target)
+ struct denode *source;
+ struct denode *target;
+{
+ daddr_t scn;
+ struct denode dummy;
+ struct msdosfsmount *pmp;
+ struct direntry *ep;
+ struct denode *dep;
+ struct buf *bp = NULL;
+ int error = 0;
+
+ dep = target;
+ if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
+ (source->de_Attributes & ATTR_DIRECTORY) == 0) {
+ error = ENOTDIR;
+ goto out;
+ }
+ if (dep->de_StartCluster == source->de_StartCluster) {
+ error = EEXIST;
+ goto out;
+ }
+ if (dep->de_StartCluster == MSDOSFSROOT)
+ goto out;
+ for (;;) {
+ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
+ error = ENOTDIR;
+ goto out;
+ }
+ pmp = dep->de_pmp;
+ scn = dep->de_StartCluster;
+ error = bread(pmp->pm_devvp, cntobn(pmp, scn),
+ pmp->pm_bpcluster, NOCRED, &bp);
+ if (error) {
+ break;
+ }
+ ep = (struct direntry *) bp->b_data + 1;
+ if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
+ bcmp(ep->deName, ".. ", 11) != 0) {
+ error = ENOTDIR;
+ break;
+ }
+ scn = getushort(ep->deStartCluster);
+ if (scn == source->de_StartCluster) {
+ error = EINVAL;
+ break;
+ }
+ if (scn == MSDOSFSROOT)
+ break;
+ vput(DETOV(dep));
+ /* NOTE: deget() clears dep on error */
+ error = deget(pmp, scn, 0, ep, &dep);
+ brelse(bp);
+ bp = NULL;
+ if (error)
+ break;
+ }
+out: ;
+ if (bp)
+ brelse(bp);
+ if (error == ENOTDIR)
+ printf("doscheckpath(): .. not a directory?\n");
+ if (dep != NULL)
+ vput(DETOV(dep));
+ return error;
+}
+
+/*
+ * Read in the disk block containing the directory entry (dirclu, dirofs)
+ * and return the address of the buf header, and the address of the
+ * directory entry within the block.
+ */
+int
+readep(pmp, dirclu, dirofs, bpp, epp)
+ struct msdosfsmount *pmp;
+ u_long dirclu, dirofs;
+ struct buf **bpp;
+ struct direntry **epp;
+{
+ int error;
+ daddr_t bn;
+
+ bn = detobn(pmp, dirclu, dirofs);
+ if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp)) {
+ *bpp = NULL;
+ return error;
+ }
+ if (epp)
+ *epp = bptoep(pmp, *bpp, dirofs);
+ return 0;
+}
+
+
+/*
+ * Read in the disk block containing the directory entry dep came from and
+ * return the address of the buf header, and the address of the directory
+ * entry within the block.
+ */
+int
+readde(dep, bpp, epp)
+ struct denode *dep;
+ struct buf **bpp;
+ struct direntry **epp;
+{
+ return readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
+ bpp, epp);
+}
diff --git a/sys/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c
new file mode 100644
index 0000000..ac9c284
--- /dev/null
+++ b/sys/fs/msdosfs/msdosfs_vfsops.c
@@ -0,0 +1,733 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/malloc.h>
+
+#include <msdosfs/bpb.h>
+#include <msdosfs/bootsect.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/fat.h>
+
+int msdosfsdoforce = 1; /* 1 = force unmount */
+
+/*
+ * mp - path - addr in user space of mount point (ie /usr or whatever)
+ * data - addr in user space of mount params including the name of the block
+ * special file to treat as a filesystem.
+ */
+int
+msdosfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *devvp; /* vnode for blk device to mount */
+ struct msdosfs_args args; /* will hold data from mount request */
+ struct msdosfsmount *pmp; /* msdosfs specific mount control block */
+ int error, flags;
+ u_int size;
+ struct ucred *cred, *scred;
+ struct vattr va;
+
+ /*
+ * Copy in the args for the mount request.
+ */
+ if (error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args)))
+ return error;
+
+ /*
+ * If they just want to update then be sure we can do what is
+ * asked. Can't change a filesystem from read/write to read only.
+ * Why? And if they've supplied a new device file name then we
+ * continue, otherwise return.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ pmp = (struct msdosfsmount *) mp->mnt_data;
+ error = 0;
+ if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
+ flags = WRITECLOSE;
+ if (mp->mnt_flag & MNT_FORCE)
+ flags |= FORCECLOSE;
+ if (vfs_busy(mp))
+ return EBUSY;
+ error = vflush(mp, NULLVP, flags);
+ vfs_unbusy(mp);
+ }
+ if (!error && (mp->mnt_flag & MNT_RELOAD))
+ /* not yet implemented */
+ error = EINVAL;
+ if (error)
+ return error;
+ if (pmp->pm_ronly && (mp->mnt_flag & MNT_RDONLY) == 0)
+ pmp->pm_ronly = 0;
+ if (args.fspec == 0) {
+ /*
+ * Process export requests.
+ */
+ return vfs_export(mp, &pmp->pm_export, &args.export);
+ }
+ } else
+ pmp = NULL;
+
+ /*
+ * check to see that the user in owns the target directory.
+ * Note the very XXX trick to make sure we're checking as the
+ * real user -- were mount() executable by anyone, this wouldn't
+ * be a problem.
+ *
+ * XXX there should be one consistent error out.
+ */
+ cred = crdup(p->p_ucred); /* XXX */
+ cred->cr_uid = p->p_cred->p_ruid; /* XXX */
+ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
+ if (error) {
+ crfree(cred); /* XXX */
+ return error;
+ }
+ if (cred->cr_uid != 0) {
+ if (va.va_uid != cred->cr_uid) {
+ error = EACCES;
+ crfree(cred); /* XXX */
+ return error;
+ }
+
+ /* a user mounted it; we'll verify permissions when unmounting */
+ mp->mnt_flag |= MNT_USER;
+ }
+
+ /*
+ * Now, lookup the name of the block device this mount or name
+ * update request is to apply to.
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+ scred = p->p_ucred; /* XXX */
+ p->p_ucred = cred; /* XXX */
+ error = namei(ndp);
+ p->p_ucred = scred; /* XXX */
+ crfree(cred); /* XXX */
+ if (error != 0)
+ return error;
+
+ /*
+ * Be sure they've given us a block device to treat as a
+ * filesystem. And, that its major number is within the bdevsw
+ * table.
+ */
+ devvp = ndp->ni_vp;
+ if (devvp->v_type != VBLK) {
+ vrele(devvp);
+ return ENOTBLK;
+ }
+ if (major(devvp->v_rdev) >= nblkdev) {
+ vrele(devvp);
+ return ENXIO;
+ }
+
+ /*
+ * If this is an update, then make sure the vnode for the block
+ * special device is the same as the one our filesystem is in.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ if (devvp != pmp->pm_devvp)
+ error = EINVAL;
+ else
+ vrele(devvp);
+ } else {
+
+ /*
+ * Well, it's not an update, it's a real mount request.
+ * Time to get dirty.
+ */
+ error = mountmsdosfs(devvp, mp, p);
+ }
+ if (error) {
+ vrele(devvp);
+ return error;
+ }
+
+ /*
+ * Copy in the name of the directory the filesystem is to be
+ * mounted on. Then copy in the name of the block special file
+ * representing the filesystem being mounted. And we clear the
+ * remainder of the character strings to be tidy. Set up the
+ * user id/group id/mask as specified by the user. Then, we try to
+ * fill in the filesystem stats structure as best we can with
+ * whatever applies from a dos file system.
+ */
+ pmp = (struct msdosfsmount *) mp->mnt_data;
+ copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname,
+ sizeof(mp->mnt_stat.f_mntonname) - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size,
+ sizeof(mp->mnt_stat.f_mntonname) - size);
+ copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntfromname + size,
+ MNAMELEN - size);
+ pmp->pm_mounter = p->p_cred->p_ruid;
+ pmp->pm_gid = args.gid;
+ pmp->pm_uid = args.uid;
+ pmp->pm_mask = args.mask;
+ (void) msdosfs_statfs(mp, &mp->mnt_stat, p);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_mount(): mp %x, pmp %x, inusemap %x\n", mp, pmp, pmp->pm_inusemap);
+#endif
+ return 0;
+}
+
+int
+mountmsdosfs(devvp, mp, p)
+ struct vnode *devvp;
+ struct mount *mp;
+ struct proc *p;
+{
+ int i;
+ int bpc;
+ int bit;
+ int error;
+ int needclose;
+ int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+ dev_t dev = devvp->v_rdev;
+ union bootsector *bsp;
+ struct msdosfsmount *pmp = NULL;
+ struct buf *bp0 = NULL;
+ struct byte_bpb33 *b33;
+ struct byte_bpb50 *b50;
+
+ /*
+ * Multiple mounts of the same block special file aren't allowed.
+ * Make sure no one else has the special file open. And flush any
+ * old buffers from this filesystem. Presumably this prevents us
+ * from running into buffers that are the wrong blocksize.
+ */
+ if (error = vfs_mountedon(devvp))
+ return error;
+ if (vcount(devvp) > 1)
+ return EBUSY;
+ if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0))
+ return error;
+
+ /*
+ * Now open the block special file.
+ */
+ if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p))
+ return error;
+ needclose = 1;
+#ifdef HDSUPPORT
+ /*
+ * Put this in when we support reading dos filesystems from
+ * partitioned harddisks.
+ */
+ if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) {
+ }
+#endif
+
+ /*
+ * Read the boot sector of the filesystem, and then check the boot
+ * signature. If not a dos boot sector then error out. We could
+ * also add some checking on the bsOemName field. So far I've seen
+ * the following values: "IBM 3.3" "MSDOS3.3" "MSDOS5.0"
+ */
+ if (error = bread(devvp, 0, 512, NOCRED, &bp0))
+ goto error_exit;
+ bp0->b_flags |= B_AGE;
+ bsp = (union bootsector *) bp0->b_data;
+ b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB;
+ b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB;
+#ifdef MSDOSFS_CHECKSIG
+ if (bsp->bs50.bsBootSectSig != BOOTSIG) {
+ error = EINVAL;
+ goto error_exit;
+ }
+#endif
+ if ( bsp->bs50.bsJump[0] != 0xe9 &&
+ (bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) {
+ error = EINVAL;
+ goto error_exit;
+ }
+
+ pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK);
+ bzero((caddr_t)pmp, sizeof *pmp);
+ pmp->pm_mountp = mp;
+
+ /*
+ * 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.
+ */
+ pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
+ pmp->pm_SectPerClust = b50->bpbSecPerClust;
+ 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_Media = b50->bpbMedia;
+ pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
+ pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
+ pmp->pm_Heads = getushort(b50->bpbHeads);
+
+ /* XXX - We should probably check more values here */
+ if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust ||
+ !pmp->pm_Heads || pmp->pm_Heads > 255 ||
+ !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) {
+ error = EINVAL;
+ goto error_exit;
+ }
+
+ 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;
+ }
+ pmp->pm_fatblk = pmp->pm_ResSectors;
+ pmp->pm_rootdirblk = pmp->pm_fatblk +
+ (pmp->pm_FATs * pmp->pm_FATsecs);
+ pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry))
+ /
+ pmp->pm_BytesPerSec;/* in sectors */
+ pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
+ pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
+ pmp->pm_SectPerClust;
+ pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
+ pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
+ if (FAT12(pmp))
+ /*
+ * This will usually be a floppy disk. This size makes sure
+ * that one fat entry will not be split across multiple
+ * blocks.
+ */
+ pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
+ else
+ /*
+ * This will usually be a hard disk. Reading or writing one
+ * block should be quite fast.
+ */
+ pmp->pm_fatblocksize = MAXBSIZE;
+ pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
+
+
+ if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0)
+ printf("mountmsdosfs(): root directory is not a multiple of the clustersize in length\n");
+
+ /*
+ * Compute mask and shift value for isolating cluster relative byte
+ * offsets and cluster numbers from a file offset.
+ */
+ bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec;
+ pmp->pm_bpcluster = bpc;
+ pmp->pm_depclust = bpc / sizeof(struct direntry);
+ pmp->pm_crbomask = bpc - 1;
+ if (bpc == 0) {
+ error = EINVAL;
+ goto error_exit;
+ }
+ bit = 1;
+ for (i = 0; i < 32; i++) {
+ if (bit & bpc) {
+ if (bit ^ bpc) {
+ error = EINVAL;
+ goto error_exit;
+ }
+ pmp->pm_cnshift = i;
+ break;
+ }
+ bit <<= 1;
+ }
+
+ pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */
+ pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */
+
+ /*
+ * Release the bootsector buffer.
+ */
+ brelse(bp0);
+ bp0 = NULL;
+
+ /*
+ * Allocate memory for the bitmap of allocated clusters, and then
+ * fill it in.
+ */
+ pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1)
+ / N_INUSEBITS)
+ * sizeof(*pmp->pm_inusemap),
+ M_MSDOSFSFAT, M_WAITOK);
+
+ /*
+ * fillinusemap() needs pm_devvp.
+ */
+ pmp->pm_dev = dev;
+ pmp->pm_devvp = devvp;
+
+ /*
+ * Have the inuse map filled in.
+ */
+ error = fillinusemap(pmp);
+ if (error)
+ goto error_exit;
+
+ /*
+ * If they want fat updates to be synchronous then let them suffer
+ * the performance degradation in exchange for the on disk copy of
+ * the fat being correct just about all the time. I suppose this
+ * would be a good thing to turn on if the kernel is still flakey.
+ */
+ pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS;
+
+ /*
+ * Finish up.
+ */
+ pmp->pm_ronly = ronly;
+ if (ronly == 0)
+ pmp->pm_fmod = 1;
+ mp->mnt_data = (qaddr_t) pmp;
+ mp->mnt_stat.f_fsid.val[0] = (long)dev;
+ mp->mnt_stat.f_fsid.val[1] = MOUNT_MSDOS;
+ mp->mnt_flag |= MNT_LOCAL;
+#ifdef QUOTA
+ /*
+ * 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.
+ */
+#endif
+ devvp->v_specflags |= SI_MOUNTEDON;
+
+ return 0;
+
+error_exit:;
+ if (bp0)
+ brelse(bp0);
+ if (needclose)
+ (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE,
+ NOCRED, p);
+ if (pmp) {
+ if (pmp->pm_inusemap)
+ free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
+ free((caddr_t) pmp, M_MSDOSFSMNT);
+ mp->mnt_data = (qaddr_t) 0;
+ }
+ return error;
+}
+
+int
+msdosfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return 0;
+}
+
+/*
+ * Unmount the filesystem described by mp.
+ */
+int
+msdosfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int flags = 0;
+ int error;
+ struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
+ struct vnode *vp = pmp->pm_devvp;
+
+ /* only the mounter, or superuser can unmount */
+ if ((p->p_cred->p_ruid != pmp->pm_mounter) &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ return error;
+
+ if (mntflags & MNT_FORCE) {
+ if (!msdosfsdoforce)
+ return EINVAL;
+ flags |= FORCECLOSE;
+ }
+#ifdef QUOTA
+#endif
+ if (error = vflush(mp, NULLVP, flags))
+ return error;
+ pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON;
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_umount(): just before calling VOP_CLOSE()\n");
+ printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n",
+ vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt);
+ printf("lastr %d, id %d, mount %08x, op %08x\n",
+ vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op);
+ printf("freef %08x, freeb %08x, mountf %08x, mountb %08x\n",
+ vp->v_freef, vp->v_freeb, vp->v_mountf, vp->v_mountb);
+ printf("cleanblkhd %08x, dirtyblkhd %08x, numoutput %d, type %d\n",
+ vp->v_cleanblkhd, vp->v_dirtyblkhd, vp->v_numoutput, vp->v_type);
+ printf("union %08x, tag %d, data[0] %08x, data[1] %08x\n",
+ vp->v_socket, vp->v_tag, vp->v_data[0], vp->v_data[1]);
+#endif
+ error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE,
+ NOCRED, p);
+ vrele(pmp->pm_devvp);
+ free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
+ free((caddr_t) pmp, M_MSDOSFSMNT);
+ mp->mnt_data = (qaddr_t) 0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ return error;
+}
+
+int
+msdosfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct denode *ndep;
+ struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data);
+ int error;
+
+ error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_root(); mp %08x, pmp %08x, ndep %08x, vp %08x\n",
+ mp, pmp, ndep, DETOV(ndep));
+#endif
+ if (error == 0)
+ *vpp = DETOV(ndep);
+ return error;
+}
+
+int
+msdosfs_quotactl(mp, cmds, uid, arg, p)
+ struct mount *mp;
+ int cmds;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+#ifdef QUOTA
+#else
+ return EOPNOTSUPP;
+#endif
+}
+
+int
+msdosfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
+
+ /*
+ * Fill in the stat block.
+ */
+#ifdef COMPAT_09
+ sbp->f_type = 4;
+#else
+ sbp->f_type = 0;
+#endif
+ sbp->f_bsize = pmp->pm_bpcluster;
+ sbp->f_iosize = pmp->pm_bpcluster;
+ sbp->f_blocks = pmp->pm_nmbrofclusters;
+ sbp->f_bfree = pmp->pm_freeclustercount;
+ sbp->f_bavail = pmp->pm_freeclustercount;
+ sbp->f_files = pmp->pm_RootDirEnts; /* XXX */
+ sbp->f_ffree = 0; /* what to put in here? */
+
+ /*
+ * Copy the mounted on and mounted from names into the passed in
+ * stat block, if it is not the one in the mount structure.
+ */
+ if (sbp != &mp->mnt_stat) {
+ bcopy((caddr_t) mp->mnt_stat.f_mntonname,
+ (caddr_t) & sbp->f_mntonname[0], MNAMELEN);
+ bcopy((caddr_t) mp->mnt_stat.f_mntfromname,
+ (caddr_t) & sbp->f_mntfromname[0], MNAMELEN);
+ }
+#if 0
+ strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN);
+ sbp->f_fstypename[MFSNAMELEN] = '\0';
+#endif
+ return 0;
+}
+
+int
+msdosfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ struct vnode *vp;
+ struct denode *dep;
+ struct msdosfsmount *pmp;
+ int error;
+ int allerror = 0;
+
+ pmp = (struct msdosfsmount *) mp->mnt_data;
+
+ /*
+ * If we ever switch to not updating all of the fats all the time,
+ * this would be the place to update them from the first one.
+ */
+ if (pmp->pm_fmod)
+ if (pmp->pm_ronly)
+ panic("msdosfs_sync: rofs mod");
+ else {
+ /* update fats here */
+ }
+
+ /*
+ * Go thru in memory denodes and write them out along with
+ * unwritten file blocks.
+ */
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first; vp;
+ vp = vp->v_mntvnodes.le_next) {
+ if (vp->v_mount != mp) /* not ours anymore */
+ goto loop;
+ if (VOP_ISLOCKED(vp)) /* file is busy */
+ continue;
+ dep = VTODE(vp);
+ if ((dep->de_flag & DE_UPDATE) == 0 &&
+ vp->v_dirtyblkhd.lh_first == NULL)
+ continue;
+ if (vget(vp, 1)) /* not there anymore? */
+ goto loop;
+ if (error = VOP_FSYNC(vp, cred, waitfor, p))
+ allerror = error;
+ vput(vp); /* done with this one */
+ }
+
+ /*
+ * Flush filesystem control info.
+ */
+ if (error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p))
+ allerror = error;
+ return allerror;
+}
+
+int
+msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
+ struct defid *defhp = (struct defid *) fhp;
+ struct denode *dep;
+ struct netcred *np;
+ int error;
+
+ np = vfs_export_lookup(mp, &pmp->pm_export, nam);
+ if (np == NULL)
+ return EACCES;
+ error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs,
+ NULL, &dep);
+ if (error) {
+ *vpp = NULLVP;
+ return error;
+ }
+ *vpp = DETOV(dep);
+ *exflagsp = np->netc_exflags;
+ *credanonp = &np->netc_anon;
+ return 0;
+}
+
+
+int
+msdosfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ struct denode *dep = VTODE(vp);
+ struct defid *defhp = (struct defid *) fhp;
+
+ defhp->defid_len = sizeof(struct defid);
+ defhp->defid_dirclust = dep->de_dirclust;
+ defhp->defid_dirofs = dep->de_diroffset;
+ /* defhp->defid_gen = ip->i_gen; */
+ return 0;
+}
+
+int
+msdosfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+ return EOPNOTSUPP;
+}
+
+struct vfsops msdosfs_vfsops = {
+ msdosfs_mount,
+ msdosfs_start,
+ msdosfs_unmount,
+ msdosfs_root,
+ msdosfs_quotactl,
+ msdosfs_statfs,
+ msdosfs_sync,
+ msdosfs_vget,
+ msdosfs_fhtovp,
+ msdosfs_vptofh,
+ msdosfs_init
+};
diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c
new file mode 100644
index 0000000..4294034
--- /dev/null
+++ b/sys/fs/msdosfs/msdosfs_vnops.c
@@ -0,0 +1,1933 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h> /* defines plimit structure in proc struct */
+#include <sys/kernel.h>
+#include <sys/file.h> /* define FWRITE ... */
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
+#include <sys/malloc.h>
+#include <sys/dir.h> /* defines dirent structure */
+
+#include <msdosfs/bpb.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/fat.h>
+/*
+ * 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
+ * retreive the correct block from the buffer cache since the hash value is
+ * based upon the vnode address and the desired block number.
+ */
+
+/*
+ * Create a regular file. On entry the directory to contain the file being
+ * created is locked. We must release before we return. We must also free
+ * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
+ * only if the SAVESTART bit in cn_flags is clear on success.
+ */
+int
+msdosfs_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct componentname *cnp = ap->a_cnp;
+ struct denode ndirent;
+ struct denode *dep;
+ struct denode *pdep = VTODE(ap->a_dvp);
+ struct timespec ts;
+ int error;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap);
+#endif
+
+ /*
+ * 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.
+ */
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & SAVENAME) == 0)
+ panic("msdosfs_create: no name");
+#endif
+ bzero(&ndirent, sizeof(ndirent));
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time);
+ unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen);
+ ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? 0 : ATTR_READONLY;
+ ndirent.de_StartCluster = 0;
+ ndirent.de_FileSize = 0;
+ ndirent.de_dev = pdep->de_dev;
+ ndirent.de_devvp = pdep->de_devvp;
+ if ((error = createde(&ndirent, pdep, &dep)) == 0) {
+ *ap->a_vpp = DETOV(dep);
+ if ((cnp->cn_flags & SAVESTART) == 0)
+ free(cnp->cn_pnbuf, M_NAMEI);
+ } else {
+ free(cnp->cn_pnbuf, M_NAMEI);
+ }
+ vput(ap->a_dvp); /* release parent dir */
+ return error;
+}
+
+int
+msdosfs_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int error;
+ struct denode *pdep = VTODE(ap->a_dvp);
+
+ switch (ap->a_vap->va_type) {
+ case VDIR:
+ error = msdosfs_mkdir((struct vop_mkdir_args *)ap);
+ break;
+
+ case VREG:
+ error = msdosfs_create((struct vop_create_args *)ap);
+ break;
+
+ default:
+ error = EINVAL;
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ break;
+ }
+ return error;
+}
+
+int
+msdosfs_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return 0;
+}
+
+int
+msdosfs_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct denode *dep = VTODE(vp);
+ struct timespec ts;
+
+ if (vp->v_usecount > 1 && !(dep->de_flag & DE_LOCKED)) {
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ DE_TIMES(dep, &ts);
+ }
+ return 0;
+}
+
+/*
+ * This routine will go into sys/kern/vfs_subr.c one day!
+ *
+ * Do the usual access checking.
+ * file_node, uid and gid are from the vnode in question,
+ * while acc_mode and cred are from the VOP_ACCESS parameter list.
+ */
+static int
+vaccess(file_mode, uid, gid, acc_mode, cred)
+ mode_t file_mode;
+ uid_t uid;
+ gid_t gid;
+ mode_t acc_mode;
+ struct ucred *cred;
+{
+ mode_t mask;
+ int i;
+ register gid_t *gp;
+
+ /* User id 0 always gets access. */
+ if (cred->cr_uid == 0)
+ return 0;
+
+ mask = 0;
+
+ /* Otherwise, check the owner. */
+ if (cred->cr_uid == uid) {
+ if (acc_mode & VEXEC)
+ mask |= S_IXUSR;
+ if (acc_mode & VREAD)
+ mask |= S_IRUSR;
+ if (acc_mode & VWRITE)
+ mask |= S_IWUSR;
+ return (file_mode & mask) == mask ? 0 : EACCES;
+ }
+
+ /* Otherwise, check the groups. */
+ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
+ if (gid == *gp) {
+ if (acc_mode & VEXEC)
+ mask |= S_IXGRP;
+ if (acc_mode & VREAD)
+ mask |= S_IRGRP;
+ if (acc_mode & VWRITE)
+ mask |= S_IWGRP;
+ return (file_mode & mask) == mask ? 0 : EACCES;
+ }
+
+ /* Otherwise, check everyone else. */
+ if (acc_mode & VEXEC)
+ mask |= S_IXOTH;
+ if (acc_mode & VREAD)
+ mask |= S_IROTH;
+ if (acc_mode & VWRITE)
+ mask |= S_IWOTH;
+ return (file_mode & mask) == mask ? 0 : EACCES;
+}
+
+int
+msdosfs_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ mode_t dosmode;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+
+ dosmode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
+ ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
+ dosmode &= pmp->pm_mask;
+
+ return vaccess(dosmode, pmp->pm_uid, pmp->pm_gid, ap->a_mode, ap->a_cred);
+}
+
+int
+msdosfs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ u_int cn;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct vattr *vap = ap->a_vap;
+ struct timespec ts;
+
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ DE_TIMES(dep, &ts);
+ vap->va_fsid = dep->de_dev;
+ /*
+ * The following computation of the fileid must be the same as that
+ * used in msdosfs_readdir() to compute d_fileno. If not, pwd
+ * doesn't work.
+ */
+ if (dep->de_Attributes & ATTR_DIRECTORY) {
+ if ((cn = dep->de_StartCluster) == MSDOSFSROOT)
+ cn = 1;
+ } else {
+ if ((cn = dep->de_dirclust) == MSDOSFSROOT)
+ cn = 1;
+ cn = (cn << 16) | (dep->de_diroffset & 0xffff);
+ }
+ vap->va_fileid = cn;
+ vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
+ ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
+ vap->va_mode &= dep->de_pmp->pm_mask;
+ if (dep->de_Attributes & ATTR_DIRECTORY)
+ vap->va_mode |= S_IFDIR;
+ vap->va_nlink = 1;
+ vap->va_gid = dep->de_pmp->pm_gid;
+ vap->va_uid = dep->de_pmp->pm_uid;
+ vap->va_rdev = 0;
+ vap->va_size = dep->de_FileSize;
+ dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime);
+ vap->va_mtime = vap->va_atime;
+#ifndef MSDOSFS_NODIRMOD
+ if (vap->va_mode & S_IFDIR)
+ TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime);
+#endif
+ vap->va_ctime = vap->va_atime;
+ vap->va_flags = dep->de_flag;
+ vap->va_gen = 0;
+ vap->va_blocksize = dep->de_pmp->pm_bpcluster;
+ vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) &
+ ~(dep->de_pmp->pm_crbomask);
+ vap->va_type = ap->a_vp->v_type;
+ return 0;
+}
+
+int
+msdosfs_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = 0;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct vattr *vap = ap->a_vap;
+ struct ucred *cred = ap->a_cred;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n",
+ ap->a_vp, vap, cred, ap->a_p);
+#endif
+ if ((vap->va_type != VNON) ||
+ (vap->va_nlink != VNOVAL) ||
+ (vap->va_fsid != VNOVAL) ||
+ (vap->va_fileid != VNOVAL) ||
+ (vap->va_blocksize != VNOVAL) ||
+ (vap->va_rdev != VNOVAL) ||
+ (vap->va_bytes != VNOVAL) ||
+ (vap->va_gen != VNOVAL) ||
+ (vap->va_uid != VNOVAL) ||
+ (vap->va_gid != VNOVAL)) {
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_setattr(): returning EINVAL\n");
+ printf(" va_type %d, va_nlink %x, va_fsid %x, va_fileid %x\n",
+ vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid);
+ printf(" va_blocksize %x, va_rdev %x, va_bytes %x, va_gen %x\n",
+ vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
+ printf(" va_uid %x, va_gid %x\n",
+ vap->va_uid, vap->va_gid);
+#endif
+ return EINVAL;
+ }
+
+ if (vap->va_size != VNOVAL) {
+ if (ap->a_vp->v_type == VDIR)
+ return EISDIR;
+ if (error = detrunc(dep, vap->va_size, 0, cred, ap->a_p))
+ return error;
+ }
+ if (vap->va_mtime.ts_sec != VNOVAL) {
+ dep->de_flag |= DE_UPDATE;
+ if (error = deupdat(dep, &vap->va_mtime, 1))
+ return error;
+ }
+
+ /*
+ * DOS files only have the ability to have thier writability
+ * attribute set, so we use the owner write bit to set the readonly
+ * attribute.
+ */
+ if (vap->va_mode != (u_short) VNOVAL) {
+ /* We ignore the read and execute bits */
+ if (vap->va_mode & VWRITE)
+ dep->de_Attributes &= ~ATTR_READONLY;
+ else
+ dep->de_Attributes |= ATTR_READONLY;
+ dep->de_flag |= DE_UPDATE;
+ }
+
+ if (vap->va_flags != VNOVAL) {
+ if (error = suser(cred, &ap->a_p->p_acflag))
+ return error;
+ if (cred->cr_uid == 0)
+ dep->de_flag = vap->va_flags;
+ else {
+ dep->de_flag &= 0xffff0000;
+ dep->de_flag |= (vap->va_flags & 0xffff);
+ }
+ dep->de_flag |= DE_UPDATE;
+ }
+ return error;
+}
+
+int
+msdosfs_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = 0;
+ int diff;
+ int isadir;
+ long n;
+ long on;
+ daddr_t bn;
+ daddr_t lbn;
+ daddr_t rablock;
+ int rasize;
+ struct buf *bp;
+ struct vnode *vp = ap->a_vp;
+ struct denode *dep = VTODE(vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct uio *uio = ap->a_uio;
+
+ /*
+ * If they didn't ask for any data, then we are done.
+ */
+ if (uio->uio_resid == 0)
+ return 0;
+ if (uio->uio_offset < 0)
+ return EINVAL;
+
+ isadir = dep->de_Attributes & ATTR_DIRECTORY;
+ do {
+ lbn = uio->uio_offset >> pmp->pm_cnshift;
+ on = uio->uio_offset & pmp->pm_crbomask;
+ n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
+ diff = dep->de_FileSize - uio->uio_offset;
+ if (diff <= 0)
+ return 0;
+ /* convert cluster # to block # if a directory */
+ if (isadir) {
+ error = pcbmap(dep, lbn, &lbn, 0);
+ if (error)
+ return error;
+ }
+ if (diff < n)
+ n = diff;
+ /*
+ * If we are operating on a directory file then be sure to
+ * do i/o with the vnode for the filesystem instead of the
+ * vnode for the directory.
+ */
+ if (isadir) {
+ error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster,
+ NOCRED, &bp);
+ } else {
+ rablock = lbn + 1;
+ if (vp->v_lastr + 1 == lbn &&
+ rablock * pmp->pm_bpcluster < dep->de_FileSize) {
+ rasize = pmp->pm_bpcluster;
+ error = breadn(vp, lbn, pmp->pm_bpcluster,
+ &rablock, &rasize, 1,
+ NOCRED, &bp);
+ } else {
+ error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED,
+ &bp);
+ }
+ vp->v_lastr = lbn;
+ }
+ n = min(n, pmp->pm_bpcluster - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ error = uiomove(bp->b_data + on, (int) n, uio);
+ /*
+ * If we have read everything from this block or have read
+ * to end of file then we are done with this block. Mark
+ * it to say the buffer can be reused if need be.
+ */
+#if 0
+ if (n + on == pmp->pm_bpcluster ||
+ uio->uio_offset == dep->de_FileSize)
+ bp->b_flags |= B_AGE;
+#endif
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return error;
+}
+
+/*
+ * Write data to a file or directory.
+ */
+int
+msdosfs_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int n;
+ int isadir;
+ int croffset;
+ int resid;
+ int osize;
+ int error = 0;
+ u_long count;
+ daddr_t bn, lastcn;
+ struct buf *bp;
+ int ioflag = ap->a_ioflag;
+ struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ struct vnode *vp = ap->a_vp;
+ struct vnode *thisvp;
+ struct denode *dep = VTODE(vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct ucred *cred = ap->a_cred;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n",
+ vp, uio, ioflag, cred);
+ printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n",
+ dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
+#endif
+
+ switch (vp->v_type) {
+ case VREG:
+ if (ioflag & IO_APPEND)
+ uio->uio_offset = dep->de_FileSize;
+ isadir = 0;
+ thisvp = vp;
+ break;
+
+ case VDIR:
+ if ((ioflag & IO_SYNC) == 0)
+ panic("msdosfs_write(): non-sync directory update");
+ isadir = 1;
+ thisvp = pmp->pm_devvp;
+ break;
+
+ default:
+ panic("msdosfs_write(): bad file type");
+ break;
+ }
+
+ if (uio->uio_offset < 0)
+ return EINVAL;
+
+ if (uio->uio_resid == 0)
+ return 0;
+
+ /*
+ * If they've exceeded their filesize limit, tell them about it.
+ */
+ if (vp->v_type == VREG && p &&
+ ((uio->uio_offset + uio->uio_resid) >
+ p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) {
+ psignal(p, SIGXFSZ);
+ return EFBIG;
+ }
+
+ /*
+ * If attempting to write beyond the end of the root directory we
+ * stop that here because the root directory can not grow.
+ */
+ if ((dep->de_Attributes & ATTR_DIRECTORY) &&
+ dep->de_StartCluster == MSDOSFSROOT &&
+ (uio->uio_offset + uio->uio_resid) > dep->de_FileSize)
+ return ENOSPC;
+
+ /*
+ * If the offset we are starting the write at is beyond the end of
+ * the file, then they've done a seek. Unix filesystems allow
+ * files with holes in them, DOS doesn't so we must fill the hole
+ * with zeroed blocks.
+ */
+ if (uio->uio_offset > dep->de_FileSize) {
+ error = deextend(dep, uio->uio_offset, cred);
+ if (error)
+ return error;
+ }
+
+ /*
+ * Remember some values in case the write fails.
+ */
+ resid = uio->uio_resid;
+ osize = dep->de_FileSize;
+
+
+ /*
+ * If we write beyond the end of the file, extend it to its ultimate
+ * size ahead of the time to hopefully get a contiguous area.
+ */
+ if (uio->uio_offset + resid > osize) {
+ count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize);
+ if ((error = extendfile(dep, count, NULL, NULL, 0))
+ && (error != ENOSPC || (ioflag & IO_UNIT)))
+ goto errexit;
+ lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
+ } else
+ lastcn = de_clcount(pmp, osize) - 1;
+
+ do {
+ bn = de_blk(pmp, uio->uio_offset);
+ if (isadir) {
+ if (error = pcbmap(dep, bn, &bn, 0))
+ break;
+ } else if (bn > lastcn) {
+ error = ENOSPC;
+ break;
+ }
+
+ if ((uio->uio_offset & pmp->pm_crbomask) == 0
+ && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset)
+ || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
+ /*
+ * If either the whole cluster gets written,
+ * or we write the cluster from its start beyond EOF,
+ * then no need to read data from disk.
+ */
+ bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0);
+ clrbuf(bp);
+ /*
+ * Do the bmap now, since pcbmap needs buffers
+ * for the fat table. (see msdosfs_strategy)
+ */
+ if (!isadir) {
+ if (bp->b_blkno == bp->b_lblkno) {
+ if (error = pcbmap(dep, bp->b_lblkno,
+ &bp->b_blkno, 0))
+ bp->b_blkno = -1;
+ }
+ if (bp->b_blkno == -1) {
+ brelse(bp);
+ if (!error)
+ error = EIO; /* XXX */
+ break;
+ }
+ }
+ } else {
+ /*
+ * The block we need to write into exists, so read it in.
+ */
+ if (error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp))
+ break;
+ }
+
+ croffset = uio->uio_offset & pmp->pm_crbomask;
+ n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
+ if (uio->uio_offset + n > dep->de_FileSize) {
+ dep->de_FileSize = uio->uio_offset + n;
+ vnode_pager_setsize(vp, dep->de_FileSize); /* why? */
+ }
+ (void) vnode_pager_uncache(vp); /* why not? */
+ /*
+ * Should these vnode_pager_* functions be done on dir
+ * files?
+ */
+
+ /*
+ * Copy the data from user space into the buf header.
+ */
+ error = uiomove(bp->b_data + croffset, n, uio);
+
+ /*
+ * If they want this synchronous then write it and wait for
+ * it. Otherwise, if on a cluster boundary write it
+ * asynchronously so we can move on to the next block
+ * without delay. Otherwise do a delayed write because we
+ * may want to write somemore into the block later.
+ */
+ if (ioflag & IO_SYNC)
+ (void) bwrite(bp);
+ else if (n + croffset == pmp->pm_bpcluster) {
+ bp->b_flags |= B_AGE;
+ bawrite(bp);
+ } else
+ bdwrite(bp);
+ dep->de_flag |= DE_UPDATE;
+ } while (error == 0 && uio->uio_resid > 0);
+
+ /*
+ * If the write failed and they want us to, truncate the file back
+ * to the size it was before the write was attempted.
+ */
+errexit:
+ if (error) {
+ if (ioflag & IO_UNIT) {
+ detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL);
+ uio->uio_offset -= resid - uio->uio_resid;
+ uio->uio_resid = resid;
+ } else {
+ detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL);
+ if (uio->uio_resid != resid)
+ error = 0;
+ }
+ } else {
+ error = deupdat(dep, &time, 1);
+ }
+ return error;
+}
+
+int
+msdosfs_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return ENOTTY;
+}
+
+int
+msdosfs_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return 1; /* DOS filesystems never block? */
+}
+
+int
+msdosfs_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return EINVAL;
+}
+
+/*
+ * Flush the blocks of a file to disk.
+ *
+ * This function is worthless for vnodes that represent directories. Maybe we
+ * could just do a sync if they try an fsync on a directory file.
+ */
+int
+msdosfs_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ int wait = ap->a_waitfor == MNT_WAIT;
+ int error;
+
+#if 0
+ /*
+ * Does this call to vflushbuf() do anything? I can find no code
+ * anywhere that sets v_dirtyblkhd in the vnode, which vflushbuf()
+ * seems to depend upon.
+ */
+ vflushbuf(vp, wait ? B_SYNC : 0);
+#endif
+ return deupdat(VTODE(vp), &time, wait);
+}
+
+/*
+ * Now the whole work of extending a file is done in the write function.
+ * So nothing to do here.
+ */
+int
+msdosfs_seek(ap)
+ struct vop_seek_args /* {
+ struct vnode *a_vp;
+ off_t a_oldoff;
+ off_t a_newoff;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ return 0;
+}
+
+int
+msdosfs_remove(ap)
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct denode *ddep = VTODE(ap->a_dvp);
+
+ error = removede(ddep,dep);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount);
+#endif
+ if (ddep == dep)
+ vrele(ap->a_vp);
+ else
+ vput(ap->a_vp); /* causes msdosfs_inactive() to be called
+ * via vrele() */
+ vput(ap->a_dvp);
+ return error;
+}
+
+/*
+ * DOS filesystems don't know what links are. But since we already called
+ * msdosfs_lookup() with create and lockparent, the parent is locked so we
+ * have to free it before we return the error.
+ */
+int
+msdosfs_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ return VOP_ABORTOP(ap->a_vp, ap->a_cnp);
+}
+
+/*
+ * Renames on files require moving the denode to a new hash queue since the
+ * denode's location is used to compute which hash queue to put the file
+ * in. Unless it is a rename in place. For example "mv a b".
+ *
+ * What follows is the basic algorithm:
+ *
+ * if (file move) {
+ * if (dest file exists) {
+ * remove dest file
+ * }
+ * if (dest and src in same directory) {
+ * rewrite name in existing directory slot
+ * } else {
+ * write new entry in dest directory
+ * update offset and dirclust in denode
+ * move denode to new hash chain
+ * clear old directory entry
+ * }
+ * } else {
+ * directory move
+ * if (dest directory exists) {
+ * if (dest is not empty) {
+ * return ENOTEMPTY
+ * }
+ * remove dest directory
+ * }
+ * if (dest and src in same directory) {
+ * rewrite name in existing entry
+ * } else {
+ * be sure dest is not a child of src directory
+ * write entry in dest directory
+ * update "." and ".." in moved directory
+ * update offset and dirclust in denode
+ * move denode to new hash chain
+ * clear old directory entry for moved directory
+ * }
+ * }
+ *
+ * On entry:
+ * source's parent directory is unlocked
+ * source file or directory is unlocked
+ * destination's parent directory is locked
+ * destination file or directory is locked if it exists
+ *
+ * On exit:
+ * all denodes should be released
+ *
+ * Notes:
+ * I'm not sure how the memory containing the pathnames pointed at by the
+ * componentname structures is freed, there may be some memory bleeding
+ * for each rename done.
+ */
+int
+msdosfs_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ u_char toname[11];
+ int error;
+ int newparent = 0;
+ int sourceisadirectory = 0;
+ u_long to_dirclust;
+ u_long to_diroffset;
+ u_long cn;
+ daddr_t bn;
+ struct vnode *tvp = ap->a_tvp;
+ struct denode *fddep; /* from file's parent directory */
+ struct denode *fdep; /* from file or directory */
+ struct denode *tddep; /* to file's parent directory */
+ struct denode *tdep; /* to file or directory */
+ struct msdosfsmount *pmp;
+ struct direntry *dotdotp;
+ struct direntry *ep;
+ struct buf *bp;
+
+ fddep = VTODE(ap->a_fdvp);
+ fdep = VTODE(ap->a_fvp);
+ tddep = VTODE(ap->a_tdvp);
+ tdep = tvp ? VTODE(tvp) : NULL;
+ pmp = fddep->de_pmp;
+
+#ifdef __NetBSD__
+ /* Check for cross-device rename */
+ if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) ||
+ (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) {
+ error = EXDEV;
+ goto bad;
+ }
+#endif
+
+ /*
+ * Convert the filename in tcnp into a dos filename. We copy this
+ * into the denode and directory entry for the destination
+ * file/directory.
+ */
+ unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr,
+ toname, ap->a_tcnp->cn_namelen);
+
+ /*
+ * At this point this is the lock state of the denodes:
+ * fddep referenced
+ * fdep referenced
+ * tddep locked
+ * tdep locked if it exists
+ */
+
+ /*
+ * Be sure we are not renaming ".", "..", or an alias of ".". This
+ * leads to a crippled directory tree. It's pretty tough to do a
+ * "ls" or "pwd" with the "." directory entry missing, and "cd .."
+ * doesn't work if the ".." entry is missing.
+ */
+ if (fdep->de_Attributes & ATTR_DIRECTORY) {
+ if ((ap->a_fcnp->cn_namelen == 1
+ && ap->a_fcnp->cn_nameptr[0] == '.')
+ || fddep == fdep
+ || (ap->a_fcnp->cn_flags & ISDOTDOT)) {
+ VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp);
+ vput(ap->a_tdvp);
+ if (tvp)
+ vput(tvp);
+ VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp);
+ vrele(ap->a_fdvp);
+ vrele(ap->a_fvp);
+ return EINVAL;
+ }
+ sourceisadirectory = 1;
+ }
+
+ /*
+ * If we are renaming a directory, and the directory is being moved
+ * to another directory, then we must be sure the destination
+ * directory is not in the subtree of the source directory. This
+ * could orphan everything under the source directory.
+ * doscheckpath() unlocks the destination's parent directory so we
+ * must look it up again to relock it.
+ */
+ if (fddep->de_StartCluster != tddep->de_StartCluster)
+ newparent = 1;
+ if (sourceisadirectory && newparent) {
+ if (tdep) {
+ vput(ap->a_tvp);
+ tdep = NULL;
+ }
+ /* doscheckpath() vput()'s tddep */
+ error = doscheckpath(fdep, tddep);
+ tddep = NULL;
+ if (error) {
+ goto bad;
+ }
+ if ((ap->a_tcnp->cn_flags & SAVESTART) == 0)
+ panic("msdosfs_rename(): lost to startdir");
+ if (error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp)) {
+ goto bad;
+ }
+ tddep = VTODE(ap->a_tdvp);
+ tdep = tvp ? VTODE(tvp) : NULL;
+ }
+
+ /*
+ * If the destination exists, then be sure its type (file or dir)
+ * matches that of the source. And, if it is a directory make sure
+ * it is empty. Then delete the destination.
+ */
+ if (tdep) {
+ if (tdep->de_Attributes & ATTR_DIRECTORY) {
+ if (!sourceisadirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ if (!dosdirempty(tdep)) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ } else { /* destination is file */
+ if (sourceisadirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+ }
+ to_dirclust = tdep->de_dirclust;
+ to_diroffset = tdep->de_diroffset;
+ if (error = removede(tddep,tdep)) {
+ goto bad;
+ }
+ vput(ap->a_tvp);
+ tdep = NULL;
+
+ /*
+ * Remember where the slot was for createde().
+ */
+ tddep->de_fndclust = to_dirclust;
+ tddep->de_fndoffset = to_diroffset;
+ }
+
+ /*
+ * If the source and destination are in the same directory then
+ * just read in the directory entry, change the name in the
+ * directory entry and write it back to disk.
+ */
+ if (newparent == 0) {
+ /* tddep and fddep point to the same denode here */
+ VOP_LOCK(ap->a_fvp); /* ap->a_fdvp is already locked */
+ if (error = readep(fddep->de_pmp,
+ fdep->de_dirclust,
+ fdep->de_diroffset,
+ &bp, &ep)) {
+ VOP_UNLOCK(ap->a_fvp);
+ goto bad;
+ }
+ bcopy(toname, ep->deName, 11);
+ if (error = bwrite(bp)) {
+ VOP_UNLOCK(ap->a_fvp);
+ goto bad;
+ }
+ bcopy(toname, fdep->de_Name, 11); /* update denode */
+ /*
+ * fdep locked fddep and tddep point to the same denode
+ * which is locked tdep is NULL
+ */
+ } else {
+ u_long dirsize = 0L;
+
+ /*
+ * If the source and destination are in different
+ * directories, then mark the entry in the source directory
+ * as deleted and write a new entry in the destination
+ * directory. Then move the denode to the correct hash
+ * chain for its new location in the filesystem. And, if
+ * we moved a directory, then update its .. entry to point
+ * to the new parent directory. If we moved a directory
+ * will also insure that the directory entry on disk has a
+ * filesize of zero.
+ */
+ VOP_LOCK(ap->a_fvp);
+ bcopy(toname, fdep->de_Name, 11); /* update denode */
+ if (fdep->de_Attributes & ATTR_DIRECTORY) {
+ dirsize = fdep->de_FileSize;
+ fdep->de_FileSize = 0;
+ }
+ error = createde(fdep, tddep, (struct denode **) 0);
+ if (fdep->de_Attributes & ATTR_DIRECTORY) {
+ fdep->de_FileSize = dirsize;
+ }
+ if (error) {
+ /* should put back filename */
+ VOP_UNLOCK(ap->a_fvp);
+ goto bad;
+ }
+ VOP_LOCK(ap->a_fdvp);
+ if (error = readep(fddep->de_pmp,
+ fddep->de_fndclust,
+ fddep->de_fndoffset,
+ &bp, &ep)) {
+ VOP_UNLOCK(ap->a_fvp);
+ VOP_UNLOCK(ap->a_fdvp);
+ goto bad;
+ }
+ ep->deName[0] = SLOT_DELETED;
+ if (error = bwrite(bp)) {
+ VOP_UNLOCK(ap->a_fvp);
+ VOP_UNLOCK(ap->a_fdvp);
+ goto bad;
+ }
+ fdep->de_dirclust = tddep->de_fndclust;
+ fdep->de_diroffset = tddep->de_fndoffset;
+ reinsert(fdep);
+ VOP_UNLOCK(ap->a_fdvp);
+ }
+ /* fdep is still locked here */
+
+ /*
+ * If we moved a directory to a new parent directory, then we must
+ * fixup the ".." entry in the moved directory.
+ */
+ if (sourceisadirectory && newparent) {
+ cn = fdep->de_StartCluster;
+ if (cn == MSDOSFSROOT) {
+ /* this should never happen */
+ panic("msdosfs_rename(): updating .. in root directory?\n");
+ } else {
+ bn = cntobn(pmp, cn);
+ }
+ error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
+ NOCRED, &bp);
+ if (error) {
+ /* should really panic here, fs is corrupt */
+ VOP_UNLOCK(ap->a_fvp);
+ goto bad;
+ }
+ dotdotp = (struct direntry *) bp->b_data + 1;
+ putushort(dotdotp->deStartCluster, tddep->de_StartCluster);
+ error = bwrite(bp);
+ VOP_UNLOCK(ap->a_fvp);
+ if (error) {
+ /* should really panic here, fs is corrupt */
+ goto bad;
+ }
+ } else
+ VOP_UNLOCK(ap->a_fvp);
+bad: ;
+ vrele(DETOV(fdep));
+ vrele(DETOV(fddep));
+ if (tdep)
+ vput(DETOV(tdep));
+ if (tddep)
+ vput(DETOV(tddep));
+ return error;
+}
+
+struct {
+ struct direntry dot;
+ struct direntry dotdot;
+} dosdirtemplate = {
+
+ ". ", " ", /* the . entry */
+ ATTR_DIRECTORY, /* file attribute */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */
+ 210, 4, 210, 4, /* time and date */
+ 0, 0, /* startcluster */
+ 0, 0, 0, 0, /* filesize */
+ ".. ", " ", /* the .. entry */
+ ATTR_DIRECTORY, /* file attribute */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */
+ 210, 4, 210, 4, /* time and date */
+ 0, 0, /* startcluster */
+ 0, 0, 0, 0, /* filesize */
+};
+
+int
+msdosfs_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struvt vnode **a_vpp;
+ struvt componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int bn;
+ int error;
+ u_long newcluster;
+ struct denode *pdep;
+ struct denode *ndep;
+ struct direntry *denp;
+ struct denode ndirent;
+ struct msdosfsmount *pmp;
+ struct buf *bp;
+ struct timespec ts;
+ u_short dDate, dTime;
+
+ pdep = VTODE(ap->a_dvp);
+
+ /*
+ * 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_fndclust == (u_long)-1) {
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ return ENOSPC;
+ }
+
+ pmp = pdep->de_pmp;
+
+ /*
+ * Allocate a cluster to hold the about to be created directory.
+ */
+ if (error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL)) {
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ return error;
+ }
+
+ /*
+ * 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);
+ /* always succeeds */
+ bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0);
+ bzero(bp->b_data, pmp->pm_bpcluster);
+ bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
+ denp = (struct direntry *) bp->b_data;
+ putushort(denp->deStartCluster, newcluster);
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ unix2dostime(&ts, &dDate, &dTime);
+ putushort(denp->deDate, dDate);
+ putushort(denp->deTime, dTime);
+ denp++;
+ putushort(denp->deStartCluster, pdep->de_StartCluster);
+ putushort(denp->deDate, dDate);
+ putushort(denp->deTime, dTime);
+ if (error = bwrite(bp)) {
+ clusterfree(pmp, newcluster, NULL);
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ return error;
+ }
+
+ /*
+ * Now build up a directory entry pointing to the newly allocated
+ * cluster. This will be written to an empty slot in the parent
+ * directory.
+ */
+ ndep = &ndirent;
+ bzero(ndep, sizeof(*ndep));
+ unix2dosfn((u_char *)ap->a_cnp->cn_nameptr,
+ ndep->de_Name, ap->a_cnp->cn_namelen);
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time);
+ ndep->de_StartCluster = newcluster;
+ ndep->de_Attributes = ATTR_DIRECTORY;
+
+ error = createde(ndep, pdep, &ndep);
+ if (error) {
+ clusterfree(pmp, newcluster, NULL);
+ } else {
+ *ap->a_vpp = DETOV(ndep);
+ }
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp);
+#endif
+ vput(ap->a_dvp);
+ return error;
+}
+
+int
+msdosfs_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ struct denode *ddep;
+ struct denode *dep;
+ int error = 0;
+
+ ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */
+ dep = VTODE(ap->a_vp);/* directory to delete */
+
+ /*
+ * Don't let "rmdir ." go thru.
+ */
+ if (ddep == dep) {
+ vrele(ap->a_vp);
+ vput(ap->a_vp);
+ return EINVAL;
+ }
+
+ /*
+ * Be sure the directory being deleted is empty.
+ */
+ if (dosdirempty(dep) == 0) {
+ error = ENOTEMPTY;
+ goto out;
+ }
+
+ /*
+ * Delete the entry from the directory. For dos filesystems this
+ * gets rid of the directory entry on disk, the in memory copy
+ * still exists but the de_refcnt is <= 0. This prevents it from
+ * being found by deget(). When the vput() on dep is done we give
+ * up access and eventually msdosfs_reclaim() will be called which
+ * will remove it from the denode cache.
+ */
+ if (error = removede(ddep,dep))
+ goto out;
+
+ /*
+ * This is where we decrement the link count in the parent
+ * directory. Since dos filesystems don't do this we just purge
+ * the name cache and let go of the parent directory denode.
+ */
+ cache_purge(DETOV(ddep));
+ vput(ap->a_dvp);
+ ap->a_dvp = NULL;
+
+ /*
+ * Truncate the directory that is being deleted.
+ */
+ error = detrunc(dep, (u_long) 0, IO_SYNC);
+ cache_purge(DETOV(dep));
+
+out: ;
+ if (ap->a_dvp)
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ return error;
+}
+
+/*
+ * DOS filesystems don't know what symlinks are.
+ */
+int
+msdosfs_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ struct denode *pdep = VTODE(ap->a_dvp);
+
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ return EINVAL;
+}
+
+/*
+ * Dummy dirents to simulate the "." and ".." entries of the root directory
+ * in a dos filesystem. Dos doesn't provide these. Note that each entry
+ * must be the same size as a dos directory entry (32 bytes).
+ */
+struct dos_dirent {
+ u_long d_fileno;
+ u_short d_reclen;
+ u_char d_type;
+ u_char d_namlen;
+ u_char d_name[24];
+} rootdots[2] = {
+
+ {
+ 1, /* d_fileno */
+ sizeof(struct direntry), /* d_reclen */
+ DT_DIR, /* d_type */
+ 1, /* d_namlen */
+ "." /* d_name */
+ },
+ {
+ 1, /* d_fileno */
+ sizeof(struct direntry), /* d_reclen */
+ DT_DIR, /* d_type */
+ 2, /* d_namlen */
+ ".." /* d_name */
+ }
+};
+
+int
+msdosfs_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ int *a_eofflag;
+ u_int *a_cookies;
+ int a_ncookies;
+ } */ *ap;
+{
+ int error = 0;
+ int diff;
+ char pushout;
+ long n;
+ long on;
+ long lost;
+ long count;
+ u_long cn;
+ u_long fileno;
+ long bias = 0;
+ daddr_t bn;
+ daddr_t lbn;
+ struct buf *bp;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct direntry *dentp;
+ struct dirent *prev;
+ struct dirent *crnt;
+ u_char dirbuf[512]; /* holds converted dos directories */
+ int i = 0;
+ struct uio *uio = ap->a_uio;
+ int ncookies = 1;
+ u_int* cookies = NULL;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n",
+ ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
+#endif
+
+#if 0
+ if (!ap->a_cookies)
+ ncookies = 1;
+#endif
+
+ /*
+ * msdosfs_readdir() won't operate properly on regular files since
+ * it does i/o only with the the filesystem vnode, and hence can
+ * retrieve the wrong block from the buffer cache for a plain file.
+ * So, fail attempts to readdir() on a plain file.
+ */
+ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
+ return ENOTDIR;
+
+ /*
+ * If the user buffer is smaller than the size of one dos directory
+ * entry or the file offset is not a multiple of the size of a
+ * directory entry, then we fail the read.
+ */
+ count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
+ lost = uio->uio_resid - count;
+ if (count < sizeof(struct direntry) ||
+ (uio->uio_offset & (sizeof(struct direntry) - 1)))
+ return EINVAL;
+ uio->uio_resid = count;
+ uio->uio_iov->iov_len = count;
+
+ /*
+ * If they are reading from the root directory then, we simulate
+ * the . and .. entries since these don't exist in the root
+ * directory. We also set the offset bias to make up for having to
+ * simulate these entries. By this I mean that at file offset 64 we
+ * read the first entry in the root directory that lives on disk.
+ */
+ if (dep->de_StartCluster == MSDOSFSROOT) {
+ /*
+ * printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
+ * uio->uio_offset);
+ */
+ bias = 2 * sizeof(struct direntry);
+ if (uio->uio_offset < 2 * sizeof(struct direntry)) {
+ if (uio->uio_offset
+ && uio->uio_offset != sizeof(struct direntry)) {
+ error = EINVAL;
+ goto out;
+ }
+ n = 1;
+ if (!uio->uio_offset) {
+ n = 2;
+ if (cookies) {
+ *cookies++ = sizeof(struct direntry);
+ ncookies--;
+ }
+ }
+ if (cookies) {
+ if (ncookies-- <= 0)
+ n--;
+ else
+ *cookies++ = 2 * sizeof(struct direntry);
+ }
+
+ error = uiomove((char *) rootdots + uio->uio_offset,
+ n * sizeof(struct direntry), uio);
+ }
+ }
+ while (!error && uio->uio_resid > 0 && ncookies > 0) {
+ lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift;
+ on = (uio->uio_offset - bias) & pmp->pm_crbomask;
+ n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
+ diff = dep->de_FileSize - (uio->uio_offset - bias);
+ if (diff <= 0)
+ return 0;
+ if (diff < n)
+ n = diff;
+ error = pcbmap(dep, lbn, &bn, &cn);
+ if (error)
+ break;
+ error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp);
+ n = min(n, pmp->pm_bpcluster - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+
+ /*
+ * code to convert from dos directory entries to ufs
+ * directory entries
+ */
+ pushout = 0;
+ dentp = (struct direntry *)(bp->b_data + on);
+ prev = 0;
+ crnt = (struct dirent *) dirbuf;
+ while ((char *) dentp < bp->b_data + on + n) {
+ /*
+ * printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
+ * dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
+ */
+ /*
+ * If we have an empty entry or a slot from a
+ * deleted file, or a volume label entry just
+ * concatenate its space onto the end of the
+ * previous entry or, manufacture an empty entry if
+ * there is no previous entry.
+ */
+ if (dentp->deName[0] == SLOT_EMPTY ||
+ dentp->deName[0] == SLOT_DELETED ||
+ (dentp->deAttributes & ATTR_VOLUME)) {
+ if (prev) {
+ prev->d_reclen += sizeof(struct direntry);
+ if (cookies) {
+ ncookies++;
+ cookies--;
+ }
+ } else {
+ prev = crnt;
+ prev->d_fileno = 0;
+ prev->d_reclen = sizeof(struct direntry);
+ prev->d_type = DT_UNKNOWN;
+ prev->d_namlen = 0;
+ prev->d_name[0] = 0;
+ }
+ } else {
+ /*
+ * this computation of d_fileno must match
+ * the computation of va_fileid in
+ * msdosfs_getattr
+ */
+ if (dentp->deAttributes & ATTR_DIRECTORY) {
+ /* if this is the root directory */
+ fileno = getushort(dentp->deStartCluster);
+ if (fileno == MSDOSFSROOT)
+ fileno = 1;
+ } else {
+ /*
+ * if the file's dirent lives in
+ * root dir
+ */
+ if ((fileno = cn) == MSDOSFSROOT)
+ fileno = 1;
+ fileno = (fileno << 16) |
+ ((dentp - (struct direntry *) bp->b_data) & 0xffff);
+ }
+ crnt->d_fileno = fileno;
+ crnt->d_reclen = sizeof(struct direntry);
+ crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY)
+ ? DT_DIR : DT_REG;
+ crnt->d_namlen = dos2unixfn(dentp->deName,
+ (u_char *)crnt->d_name);
+ /*
+ * printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n",
+ * crnt->d_name, crnt->d_fileno, dentp->deAttributes,
+ * dentp->deStartCluster);
+ */
+ prev = crnt;
+ }
+ dentp++;
+ if (cookies) {
+ *cookies++ = (u_int)((char *)dentp - bp->b_data - on)
+ + uio->uio_offset;
+ ncookies--;
+ }
+
+ crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry));
+ pushout = 1;
+
+ /*
+ * If our intermediate buffer is full then copy its
+ * contents to user space. I would just use the
+ * buffer the buf header points to but, I'm afraid
+ * that when we brelse() it someone else might find
+ * it in the cache and think its contents are
+ * valid. Maybe there is a way to invalidate the
+ * buffer before brelse()'ing it.
+ */
+ if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) {
+ pushout = 0;
+ error = uiomove(dirbuf, sizeof(dirbuf), uio);
+ if (error)
+ break;
+ prev = 0;
+ crnt = (struct dirent *) dirbuf;
+ }
+ if (ncookies <= 0)
+ break;
+ }
+ if (pushout) {
+ pushout = 0;
+ error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf,
+ uio);
+ }
+
+#if 0
+ /*
+ * If we have read everything from this block or have read
+ * to end of file then we are done with this block. Mark
+ * it to say the buffer can be reused if need be.
+ */
+ if (n + on == pmp->pm_bpcluster ||
+ (uio->uio_offset - bias) == dep->de_FileSize)
+ bp->b_flags |= B_AGE;
+#endif /* if 0 */
+ brelse(bp);
+ if (n == 0)
+ break;
+ }
+out: ;
+ uio->uio_resid += lost;
+
+#if 0
+ /*
+ * I don't know why we bother setting this eofflag, getdirentries()
+ * in vfs_syscalls.c doesn't bother to look at it when we return.
+ * (because NFS uses it in nfs_serv.c -- JMP)
+ */
+ if (dep->de_FileSize - uio->uio_offset - bias <= 0)
+ *ap->a_eofflag = 1;
+ else
+ *ap->a_eofflag = 0;
+#endif
+
+ return error;
+}
+
+/*
+ * DOS filesystems don't know what symlinks are.
+ */
+int
+msdosfs_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ return EINVAL;
+}
+
+int
+msdosfs_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return 0;
+}
+
+int
+msdosfs_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct denode *dep = VTODE(ap->a_vp);
+
+ while (dep->de_flag & DE_LOCKED) {
+ dep->de_flag |= DE_WANTED;
+ if (dep->de_lockholder == curproc->p_pid)
+ panic("msdosfs_lock: locking against myself");
+ dep->de_lockwaiter = curproc->p_pid;
+ (void) sleep((caddr_t) dep, PINOD);
+ }
+ dep->de_lockwaiter = 0;
+ dep->de_lockholder = curproc->p_pid;
+ dep->de_flag |= DE_LOCKED;
+ return 0;
+}
+
+int
+msdosfs_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *vp;
+ } */ *ap;
+{
+ struct denode *dep = VTODE(ap->a_vp);
+
+ if (!(dep->de_flag & DE_LOCKED))
+ panic("msdosfs_unlock: denode not locked");
+ dep->de_lockholder = 0;
+ dep->de_flag &= ~DE_LOCKED;
+ if (dep->de_flag & DE_WANTED) {
+ dep->de_flag &= ~DE_WANTED;
+ wakeup((caddr_t) dep);
+ }
+ return 0;
+}
+
+int
+msdosfs_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ return VTODE(ap->a_vp)->de_flag & DE_LOCKED ? 1 : 0;
+}
+
+/*
+ * vp - address of vnode file the file
+ * bn - which cluster we are interested in mapping to a filesystem block number.
+ * vpp - returns the vnode for the block special file holding the filesystem
+ * containing the file of interest
+ * bnp - address of where to return the filesystem relative block number
+ */
+int
+msdosfs_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ struct denode *dep = VTODE(ap->a_vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = dep->de_devvp;
+ if (ap->a_bnp == NULL)
+ return 0;
+ if (ap->a_runp) {
+ /*
+ * Sequential clusters should be counted here.
+ */
+ *ap->a_runp = 0;
+ }
+ return pcbmap(dep, ap->a_bn, ap->a_bnp, 0);
+}
+
+int msdosfs_reallocblks(ap)
+ struct vop_reallocblks_args /* {
+ struct vnode *a_vp;
+ struct cluster_save *a_buflist;
+ } */ *ap;
+{
+ /* Currently no support for clustering */ /* XXX */
+ return ENOSPC;
+}
+
+int
+msdosfs_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ struct denode *dep = VTODE(bp->b_vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct vnode *vp;
+ int error = 0;
+
+ if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR)
+ panic("msdosfs_strategy: spec");
+ /*
+ * If we don't already know the filesystem relative block number
+ * then get it using pcbmap(). If pcbmap() returns the block
+ * number as -1 then we've got a hole in the file. DOS filesystems
+ * don't allow files with holes, so we shouldn't ever see this.
+ */
+ if (bp->b_blkno == bp->b_lblkno) {
+ if (error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0))
+ bp->b_blkno = -1;
+ if (bp->b_blkno == -1)
+ clrbuf(bp);
+ }
+ if (bp->b_blkno == -1) {
+ biodone(bp);
+ return error;
+ }
+#ifdef DIAGNOSTIC
+#endif
+ /*
+ * Read/write the block from/to the disk that contains the desired
+ * file block.
+ */
+ vp = dep->de_devvp;
+ bp->b_dev = vp->v_rdev;
+ VOCALL(vp->v_op, VOFFSET(vop_strategy), ap);
+ return 0;
+}
+
+int
+msdosfs_print(ap)
+ struct vop_print_args /* {
+ struct vnode *vp;
+ } */ *ap;
+{
+ struct denode *dep = VTODE(ap->a_vp);
+
+ printf("tag VT_MSDOSFS, startcluster %d, dircluster %d, diroffset %d ",
+ dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
+ printf(" dev %d, %d, %s\n",
+ major(dep->de_dev), minor(dep->de_dev),
+ dep->de_flag & DE_LOCKED ? "(LOCKED)" : "");
+ if (dep->de_lockholder) {
+ printf(" owner pid %d", dep->de_lockholder);
+ if (dep->de_lockwaiter)
+ printf(" waiting pid %d", dep->de_lockwaiter);
+ printf("\n");
+ }
+ return 0;
+}
+
+int
+msdosfs_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+ return EINVAL; /* we don't do locking yet */
+}
+
+int
+msdosfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = 1;
+ return 0;
+ case _PC_NAME_MAX:
+ *ap->a_retval = 12;
+ return 0;
+ case _PC_PATH_MAX:
+ *ap->a_retval = PATH_MAX; /* 255? */
+ return 0;
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return 0;
+ case _PC_NO_TRUNC:
+ *ap->a_retval = 0;
+ return 0;
+ default:
+ return EINVAL;
+ }
+}
+
+/* Global vfs data structures for msdosfs */
+int (**msdosfs_vnodeop_p)();
+struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, msdosfs_lookup }, /* lookup */
+ { &vop_create_desc, msdosfs_create }, /* create */
+ { &vop_mknod_desc, msdosfs_mknod }, /* mknod */
+ { &vop_open_desc, msdosfs_open }, /* open */
+ { &vop_close_desc, msdosfs_close }, /* close */
+ { &vop_access_desc, msdosfs_access }, /* access */
+ { &vop_getattr_desc, msdosfs_getattr }, /* getattr */
+ { &vop_setattr_desc, msdosfs_setattr }, /* setattr */
+ { &vop_read_desc, msdosfs_read }, /* read */
+ { &vop_write_desc, msdosfs_write }, /* write */
+ { &vop_ioctl_desc, msdosfs_ioctl }, /* ioctl */
+ { &vop_select_desc, msdosfs_select }, /* select */
+ { &vop_mmap_desc, msdosfs_mmap }, /* mmap */
+ { &vop_fsync_desc, msdosfs_fsync }, /* fsync */
+ { &vop_seek_desc, msdosfs_seek }, /* seek */
+ { &vop_remove_desc, msdosfs_remove }, /* remove */
+ { &vop_link_desc, msdosfs_link }, /* link */
+ { &vop_rename_desc, msdosfs_rename }, /* rename */
+ { &vop_mkdir_desc, msdosfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, msdosfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, msdosfs_symlink }, /* symlink */
+ { &vop_readdir_desc, msdosfs_readdir }, /* readdir */
+ { &vop_readlink_desc, msdosfs_readlink }, /* readlink */
+ { &vop_abortop_desc, msdosfs_abortop }, /* abortop */
+ { &vop_inactive_desc, msdosfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, msdosfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, msdosfs_lock }, /* lock */
+ { &vop_unlock_desc, msdosfs_unlock }, /* unlock */
+ { &vop_bmap_desc, msdosfs_bmap }, /* bmap */
+ { &vop_strategy_desc, msdosfs_strategy }, /* strategy */
+ { &vop_print_desc, msdosfs_print }, /* print */
+ { &vop_islocked_desc, msdosfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, msdosfs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, msdosfs_advlock }, /* advlock */
+ { &vop_reallocblks_desc, msdosfs_reallocblks }, /* reallocblks */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc *)NULL, (int (*)())NULL }
+};
+struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
+ { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h
new file mode 100644
index 0000000..62853c5
--- /dev/null
+++ b/sys/fs/msdosfs/msdosfsmount.h
@@ -0,0 +1,183 @@
+/* $Id$ */
+/* $NetBSD: msdosfsmount.h,v 1.7 1994/08/21 18:44:17 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * Layout of the mount control block for a msdos file system.
+ */
+struct msdosfsmount {
+ struct mount *pm_mountp;/* vfs mount struct for this fs */
+ dev_t pm_dev; /* block special device mounted */
+ uid_t pm_mounter; /* uid of the user who mounted the FS */
+ uid_t pm_uid; /* uid to set as owner of the files */
+ gid_t pm_gid; /* gid to set as owner of the files */
+ mode_t pm_mask; /* mask to and with file protection bits */
+ struct vnode *pm_devvp; /* vnode for block device mntd */
+ struct bpb50 pm_bpb; /* BIOS parameter blk for this fs */
+ u_long pm_fatblk; /* block # of first FAT */
+ u_long pm_rootdirblk; /* block # of root directory */
+ u_long pm_rootdirsize; /* size in blocks (not clusters) */
+ u_long pm_firstcluster; /* block number of first cluster */
+ u_long pm_nmbrofclusters; /* # of clusters in filesystem */
+ u_long pm_maxcluster; /* maximum cluster number */
+ u_long pm_freeclustercount; /* number of free clusters */
+ u_long pm_bnshift; /* shift file offset right this amount to get a block number */
+ u_long pm_brbomask; /* and a file offset with this mask to get block rel offset */
+ u_long pm_cnshift; /* shift file offset right this amount to get a cluster number */
+ u_long pm_crbomask; /* and a file offset with this mask to get cluster rel offset */
+ u_long pm_bpcluster; /* bytes per cluster */
+ u_long pm_depclust; /* directory entries per cluster */
+ u_long pm_fmod; /* ~0 if fs is modified, this can rollover to 0 */
+ u_long pm_fatblocksize; /* size of fat blocks in bytes */
+ u_long pm_fatblocksec; /* size of fat blocks in sectors */
+ u_long pm_fatsize; /* size of fat in bytes */
+ u_int *pm_inusemap; /* ptr to bitmap of in-use clusters */
+ char pm_ronly; /* read only if non-zero */
+ char pm_waitonfat; /* wait for writes of the fat to complt, when 0 use bdwrite, else use bwrite */
+ struct netexport pm_export; /* export information */
+};
+
+/* Number of bits in one pm_inusemap item: */
+#define N_INUSEBITS (8 * sizeof(u_int))
+
+/*
+ * How to compute pm_cnshift and pm_crbomask.
+ *
+ * pm_crbomask = (pm_SectPerClust * pm_BytesPerSect) - 1
+ * if (bytesperclust == * 0)
+ * return EBADBLKSZ;
+ * bit = 1;
+ * for (i = 0; i < 32; i++) {
+ * if (bit & bytesperclust) {
+ * if (bit ^ bytesperclust)
+ * return EBADBLKSZ;
+ * pm_cnshift = * i;
+ * break;
+ * }
+ * bit <<= 1;
+ * }
+ */
+
+/*
+ * Shorthand for fields in the bpb contained in the msdosfsmount structure.
+ */
+#define pm_BytesPerSec pm_bpb.bpbBytesPerSec
+#define pm_SectPerClust pm_bpb.bpbSecPerClust
+#define pm_ResSectors pm_bpb.bpbResSectors
+#define pm_FATs pm_bpb.bpbFATs
+#define pm_RootDirEnts pm_bpb.bpbRootDirEnts
+#define pm_Sectors pm_bpb.bpbSectors
+#define pm_Media pm_bpb.bpbMedia
+#define pm_FATsecs pm_bpb.bpbFATsecs
+#define pm_SecPerTrack pm_bpb.bpbSecPerTrack
+#define pm_Heads pm_bpb.bpbHeads
+#define pm_HiddenSects pm_bpb.bpbHiddenSecs
+#define pm_HugeSectors pm_bpb.bpbHugeSectors
+
+/*
+ * Map a cluster number into a filesystem relative block number.
+ */
+#define cntobn(pmp, cn) \
+ ((((cn)-CLUST_FIRST) * (pmp)->pm_SectPerClust) + (pmp)->pm_firstcluster)
+
+/*
+ * Map a filesystem relative block number back into a cluster number.
+ */
+#define bntocn(pmp, bn) \
+ ((((bn) - pmp->pm_firstcluster)/ (pmp)->pm_SectPerClust) + CLUST_FIRST)
+
+/*
+ * Calculate block number for directory entry in root dir, offset dirofs
+ */
+#define roottobn(pmp, dirofs) \
+ (((dirofs) / (pmp)->pm_depclust) * (pmp)->pm_SectPerClust \
+ + (pmp)->pm_rootdirblk)
+
+/*
+ * Calculate block number for directory entry at cluster dirclu, offset
+ * dirofs
+ */
+#define detobn(pmp, dirclu, dirofs) \
+ ((dirclu) == MSDOSFSROOT \
+ ? roottobn((pmp), (dirofs)) \
+ : cntobn((pmp), (dirclu)))
+
+/*
+ * Convert pointer to buffer -> pointer to direntry
+ */
+#define bptoep(pmp, bp, dirofs) \
+ ((struct direntry *)((bp)->b_data) \
+ + (dirofs) % (pmp)->pm_depclust)
+
+
+/*
+ * Convert filesize to block number
+ */
+#define de_blk(pmp, off) \
+ ((off) >> (pmp)->pm_cnshift)
+
+/*
+ * Clusters required to hold size bytes
+ */
+#define de_clcount(pmp, size) \
+ (((size) + (pmp)->pm_bpcluster - 1) >> (pmp)->pm_cnshift)
+
+/*
+ * Prototypes for MSDOSFS virtual filesystem operations
+ */
+int msdosfs_mount __P((struct mount *, char *, caddr_t, struct nameidata *, struct proc *));
+int msdosfs_start __P((struct mount *, int, struct proc *));
+int msdosfs_unmount __P((struct mount *, int, struct proc *));
+int msdosfs_root __P((struct mount *, struct vnode **));
+int msdosfs_quotactl __P((struct mount *, int, uid_t, caddr_t, struct proc *));
+int msdosfs_statfs __P((struct mount *, struct statfs *, struct proc *));
+int msdosfs_sync __P((struct mount *, int, struct ucred *, struct proc *));
+int msdosfs_fhtovp __P((struct mount *, struct fid *, struct mbuf *, struct vnode **, int *, struct ucred **));
+int msdosfs_vptofh __P((struct vnode *, struct fid *));
+int msdosfs_init __P(());
diff --git a/sys/kern/kern_ntptime.c b/sys/kern/kern_ntptime.c
index 9d4c1b9..e11628b 100644
--- a/sys/kern/kern_ntptime.c
+++ b/sys/kern/kern_ntptime.c
@@ -48,6 +48,7 @@
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/timex.h>
+#include <vm/vm.h>
#include <sys/sysctl.h>
/*
diff --git a/sys/kern/vfs_conf.c b/sys/kern/vfs_conf.c
index 70377ed..3480333 100644
--- a/sys/kern/vfs_conf.c
+++ b/sys/kern/vfs_conf.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)vfs_conf.c 8.8 (Berkeley) 3/31/94
- * $Id: vfs_conf.c,v 1.2 1994/08/02 07:43:19 davidg Exp $
+ * $Id: vfs_conf.c,v 1.3 1994/08/20 16:03:12 davidg Exp $
*/
#include <sys/param.h>
@@ -138,6 +138,13 @@ extern struct vfsops cd9660_vfsops;
#define CD9660_VFSOPS NULL
#endif
+#ifdef MSDOSFS
+extern struct vfsops msdosfs_vfsops;
+#define MSDOSFS_VFSOPS &msdosfs_vfsops
+#else
+#define MSDOSFS_VFSOPS NULL
+#endif
+
#ifdef UNION
extern struct vfsops union_vfsops;
#define UNION_VFSOPS &union_vfsops
@@ -150,7 +157,7 @@ struct vfsops *vfssw[] = {
UFS_VFSOPS, /* 1 = MOUNT_UFS */
NFS_VFSOPS, /* 2 = MOUNT_NFS */
MFS_VFSOPS, /* 3 = MOUNT_MFS */
- NULL, /* 4 = MOUNT_PC */
+ MSDOSFS_VFSOPS, /* 4 = MOUNT_MSDOS */
LFS_VFSOPS, /* 5 = MOUNT_LFS */
NULL, /* 6 = MOUNT_LOFS */
FDESC_VFSOPS, /* 7 = MOUNT_FDESC */
@@ -195,6 +202,7 @@ extern struct vnodeopv_desc procfs_vnodeop_opv_desc;
extern struct vnodeopv_desc cd9660_vnodeop_opv_desc;
extern struct vnodeopv_desc cd9660_specop_opv_desc;
extern struct vnodeopv_desc cd9660_fifoop_opv_desc;
+extern struct vnodeopv_desc msdosfs_vnodeop_opv_desc;
extern struct vnodeopv_desc union_vnodeop_opv_desc;
struct vnodeopv_desc *vfs_opv_descs[] = {
@@ -250,6 +258,9 @@ struct vnodeopv_desc *vfs_opv_descs[] = {
&cd9660_fifoop_opv_desc,
#endif
#endif
+#ifdef MSDOSFS
+ &msdosfs_vnodeop_opv_desc,
+#endif
#ifdef UNION
&union_vnodeop_opv_desc,
#endif
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 70377ed..3480333 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)vfs_conf.c 8.8 (Berkeley) 3/31/94
- * $Id: vfs_conf.c,v 1.2 1994/08/02 07:43:19 davidg Exp $
+ * $Id: vfs_conf.c,v 1.3 1994/08/20 16:03:12 davidg Exp $
*/
#include <sys/param.h>
@@ -138,6 +138,13 @@ extern struct vfsops cd9660_vfsops;
#define CD9660_VFSOPS NULL
#endif
+#ifdef MSDOSFS
+extern struct vfsops msdosfs_vfsops;
+#define MSDOSFS_VFSOPS &msdosfs_vfsops
+#else
+#define MSDOSFS_VFSOPS NULL
+#endif
+
#ifdef UNION
extern struct vfsops union_vfsops;
#define UNION_VFSOPS &union_vfsops
@@ -150,7 +157,7 @@ struct vfsops *vfssw[] = {
UFS_VFSOPS, /* 1 = MOUNT_UFS */
NFS_VFSOPS, /* 2 = MOUNT_NFS */
MFS_VFSOPS, /* 3 = MOUNT_MFS */
- NULL, /* 4 = MOUNT_PC */
+ MSDOSFS_VFSOPS, /* 4 = MOUNT_MSDOS */
LFS_VFSOPS, /* 5 = MOUNT_LFS */
NULL, /* 6 = MOUNT_LOFS */
FDESC_VFSOPS, /* 7 = MOUNT_FDESC */
@@ -195,6 +202,7 @@ extern struct vnodeopv_desc procfs_vnodeop_opv_desc;
extern struct vnodeopv_desc cd9660_vnodeop_opv_desc;
extern struct vnodeopv_desc cd9660_specop_opv_desc;
extern struct vnodeopv_desc cd9660_fifoop_opv_desc;
+extern struct vnodeopv_desc msdosfs_vnodeop_opv_desc;
extern struct vnodeopv_desc union_vnodeop_opv_desc;
struct vnodeopv_desc *vfs_opv_descs[] = {
@@ -250,6 +258,9 @@ struct vnodeopv_desc *vfs_opv_descs[] = {
&cd9660_fifoop_opv_desc,
#endif
#endif
+#ifdef MSDOSFS
+ &msdosfs_vnodeop_opv_desc,
+#endif
#ifdef UNION
&union_vnodeop_opv_desc,
#endif
diff --git a/sys/msdosfs/bootsect.h b/sys/msdosfs/bootsect.h
new file mode 100644
index 0000000..099b15f
--- /dev/null
+++ b/sys/msdosfs/bootsect.h
@@ -0,0 +1,70 @@
+/* $Id$ */
+/* $NetBSD: bootsect.h,v 1.4 1994/06/29 06:35:28 cgd Exp $ */
+
+/*
+ * 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
+ */
+
+/*
+ * Format of a boot sector. This is the first sector on a DOS floppy disk
+ * or the fist sector of a partition on a hard disk. But, it is not the
+ * first sector of a partitioned hard disk.
+ */
+struct bootsector33 {
+ u_char bsJump[3]; /* jump instruction E9xxxx or EBxx90 */
+ char bsOemName[8]; /* OEM name and version */
+ char bsBPB[19]; /* BIOS parameter block */
+ char bsDriveNumber; /* drive number (0x80) */
+ char bsBootCode[474]; /* pad so structure is 512 bytes long */
+ u_short bsBootSectSig;
+#define BOOTSIG 0xaa55
+};
+
+struct bootsector50 {
+ u_char bsJump[3]; /* jump instruction E9xxxx or EBxx90 */
+ char bsOemName[8]; /* OEM name and version */
+ char bsBPB[25]; /* BIOS parameter block */
+ char bsDriveNumber; /* drive number (0x80) */
+ char bsReserved1; /* reserved */
+ char bsBootSignature; /* extended boot signature (0x29) */
+#define EXBOOTSIG 0x29
+ char bsVolumeID[4]; /* volume ID number */
+ char bsVolumeLabel[11]; /* volume label */
+ char bsFileSysType[8]; /* file system type (FAT12 or FAT16) */
+ char bsBootCode[448]; /* pad so structure is 512 bytes long */
+ u_short bsBootSectSig;
+#define BOOTSIG 0xaa55
+};
+
+union bootsector {
+ struct bootsector33 bs33;
+ struct bootsector50 bs50;
+};
+
+/*
+ * Shorthand for fields in the bpb.
+ */
+#define bsBytesPerSec bsBPB.bpbBytesPerSec
+#define bsSectPerClust bsBPB.bpbSectPerClust
+#define bsResSectors bsBPB.bpbResSectors
+#define bsFATS bsBPB.bpbFATS
+#define bsRootDirEnts bsBPB.bpbRootDirEnts
+#define bsSectors bsBPB.bpbSectors
+#define bsMedia bsBPB.bpbMedia
+#define bsFATsecs bsBPB.bpbFATsecs
+#define bsSectPerTrack bsBPB.bpbSectPerTrack
+#define bsHeads bsBPB.bpbHeads
+#define bsHiddenSecs bsBPB.bpbHiddenSecs
+#define bsHugeSectors bsBPB.bpbHugeSectors
diff --git a/sys/msdosfs/bpb.h b/sys/msdosfs/bpb.h
new file mode 100644
index 0000000..70e4de2
--- /dev/null
+++ b/sys/msdosfs/bpb.h
@@ -0,0 +1,120 @@
+/* $Id$ */
+/* $NetBSD: bpb.h,v 1.3 1994/06/29 06:35:29 cgd Exp $ */
+
+/*
+ * 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
+ */
+
+/*
+ * BIOS Parameter Block (BPB) for DOS 3.3
+ */
+struct bpb33 {
+ u_short bpbBytesPerSec; /* bytes per sector */
+ u_char bpbSecPerClust; /* sectors per cluster */
+ u_short bpbResSectors; /* number of reserved sectors */
+ u_char bpbFATs; /* number of FATs */
+ u_short bpbRootDirEnts; /* number of root directory entries */
+ u_short bpbSectors; /* total number of sectors */
+ u_char bpbMedia; /* media descriptor */
+ u_short bpbFATsecs; /* number of sectors per FAT */
+ u_short bpbSecPerTrack; /* sectors per track */
+ u_short bpbHeads; /* number of heads */
+ u_short bpbHiddenSecs; /* number of hidden sectors */
+};
+
+/*
+ * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3,
+ * and bpbHugeSectors is not in the 3.3 bpb.
+ */
+struct bpb50 {
+ u_short bpbBytesPerSec; /* bytes per sector */
+ u_char bpbSecPerClust; /* sectors per cluster */
+ u_short bpbResSectors; /* number of reserved sectors */
+ u_char bpbFATs; /* number of FATs */
+ u_short bpbRootDirEnts; /* number of root directory entries */
+ u_short bpbSectors; /* total number of sectors */
+ u_char bpbMedia; /* media descriptor */
+ u_short bpbFATsecs; /* number of sectors per FAT */
+ u_short bpbSecPerTrack; /* sectors per track */
+ u_short bpbHeads; /* number of heads */
+ u_long bpbHiddenSecs; /* number of hidden sectors */
+ u_long bpbHugeSectors; /* number of sectrs if bpbSectors == 0 */
+};
+
+/*
+ * The following structures represent how the bpb's look on disk. shorts
+ * and longs are just character arrays of the appropriate length. This is
+ * because the compiler forces shorts and longs to align on word or
+ * halfword boundaries.
+ *
+ * XXX The little-endian code here assumes that the processor can access
+ * 16-bit and 32-bit quantities on byte boundaries. If this is not true,
+ * use the macros for the big-endian case.
+ */
+#include <machine/endian.h>
+#if BYTE_ORDER == LITTLE_ENDIAN /* && can do unaligned accesses */
+#define getushort(x) *((u_short *)(x))
+#define getulong(x) *((u_long *)(x))
+#define putushort(p, v) (*((u_short *)(p)) = (v))
+#define putulong(p, v) (*((u_long *)(p)) = (v))
+
+#else
+#define getushort(x) (((u_char *)(x))[0] + (((u_char *)(x))[1] << 8))
+#define getulong(x) (((u_char *)(x))[0] + (((u_char *)(x))[1] << 8) \
+ + (((u_char *)(x))[2] << 16) \
+ + (((u_char *)(x))[3] << 24))
+#define putushort(p, v) (((u_char *)(p))[0] = (v), \
+ ((u_char *)(p))[1] = (v) >> 8)
+#define putulong(p, v) (((u_char *)(p))[0] = (v), \
+ ((u_char *)(p))[1] = (v) >> 8, \
+ ((u_char *)(p))[2] = (v) >> 16,\
+ ((u_char *)(p))[3] = (v) >> 24)
+#endif
+
+/*
+ * BIOS Parameter Block (BPB) for DOS 3.3
+ */
+struct byte_bpb33 {
+ char bpbBytesPerSec[2]; /* bytes per sector */
+ char bpbSecPerClust; /* sectors per cluster */
+ char bpbResSectors[2]; /* number of reserved sectors */
+ char bpbFATs; /* number of FATs */
+ char bpbRootDirEnts[2]; /* number of root directory entries */
+ char bpbSectors[2]; /* total number of sectors */
+ char bpbMedia; /* media descriptor */
+ char bpbFATsecs[2]; /* number of sectors per FAT */
+ char bpbSecPerTrack[2]; /* sectors per track */
+ char bpbHeads[2]; /* number of heads */
+ char bpbHiddenSecs[2]; /* number of hidden sectors */
+};
+
+/*
+ * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3,
+ * and bpbHugeSectors is not in the 3.3 bpb.
+ */
+struct byte_bpb50 {
+ char bpbBytesPerSec[2]; /* bytes per sector */
+ char bpbSecPerClust; /* sectors per cluster */
+ char bpbResSectors[2]; /* number of reserved sectors */
+ char bpbFATs; /* number of FATs */
+ char bpbRootDirEnts[2]; /* number of root directory entries */
+ char bpbSectors[2]; /* total number of sectors */
+ char bpbMedia; /* media descriptor */
+ char bpbFATsecs[2]; /* number of sectors per FAT */
+ char bpbSecPerTrack[2]; /* sectors per track */
+ char bpbHeads[2]; /* number of heads */
+ char bpbHiddenSecs[4]; /* number of hidden sectors */
+ char bpbHugeSectors[4]; /* number of sectrs if bpbSectors == 0 */
+};
diff --git a/sys/msdosfs/denode.h b/sys/msdosfs/denode.h
new file mode 100644
index 0000000..8df3e4c
--- /dev/null
+++ b/sys/msdosfs/denode.h
@@ -0,0 +1,267 @@
+/* $Id$ */
+/* $NetBSD: denode.h,v 1.8 1994/08/21 18:43:49 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * This is the pc filesystem specific portion of the vnode structure.
+ *
+ * To describe a file uniquely the de_dirclust, de_diroffset, and
+ * de_StartCluster fields are used.
+ *
+ * de_dirclust contains the cluster number of the directory cluster
+ * containing the entry for a file or directory.
+ * de_diroffset is the index into the cluster for the entry describing
+ * a file or directory.
+ * de_StartCluster is the number of the first cluster of the file or directory.
+ *
+ * Now to describe the quirks of the pc filesystem.
+ * - Clusters 0 and 1 are reserved.
+ * - The first allocatable cluster is 2.
+ * - The root directory is of fixed size and all blocks that make it up
+ * are contiguous.
+ * - Cluster 0 refers to the root directory when it is found in the
+ * startcluster field of a directory entry that points to another directory.
+ * - Cluster 0 implies a 0 length file when found in the start cluster field
+ * of a directory entry that points to a file.
+ * - You can't use the cluster number 0 to derive the address of the root
+ * directory.
+ * - Multiple directory entries can point to a directory. The entry in the
+ * parent directory points to a child directory. Any directories in the
+ * child directory contain a ".." entry that points back to the parent.
+ * The child directory itself contains a "." entry that points to itself.
+ * - The root directory does not contain a "." or ".." entry.
+ * - Directory entries for directories are never changed once they are created
+ * (except when removed). The size stays 0, and the last modification time
+ * is never changed. This is because so many directory entries can point to
+ * the physical clusters that make up a directory. It would lead to an
+ * update nightmare.
+ * - The length field in a directory entry pointing to a directory contains 0
+ * (always). The only way to find the end of a directory is to follow the
+ * cluster chain until the "last cluster" marker is found.
+ *
+ * My extensions to make this house of cards work. These apply only to the in
+ * memory copy of the directory entry.
+ * - A reference count for each denode will be kept since dos doesn't keep such
+ * things.
+ */
+
+/*
+ * Internal pseudo-offset for (nonexistent) directory entry for the root
+ * dir in the root dir
+ */
+#define MSDOSFSROOT_OFS 0x1fffffff
+
+/*
+ * The fat cache structure. fc_fsrcn is the filesystem relative cluster
+ * number that corresponds to the file relative cluster number in this
+ * structure (fc_frcn).
+ */
+struct fatcache {
+ u_short fc_frcn; /* file relative cluster number */
+ u_short fc_fsrcn; /* filesystem relative cluster number */
+};
+
+/*
+ * The fat entry cache as it stands helps make extending files a "quick"
+ * operation by avoiding having to scan the fat to discover the last
+ * cluster of the file. The cache also helps sequential reads by
+ * remembering the last cluster read from the file. This also prevents us
+ * from having to rescan the fat to find the next cluster to read. This
+ * cache is probably pretty worthless if a file is opened by multiple
+ * processes.
+ */
+#define FC_SIZE 2 /* number of entries in the cache */
+#define FC_LASTMAP 0 /* entry the last call to pcbmap() resolved
+ * to */
+#define FC_LASTFC 1 /* entry for the last cluster in the file */
+
+#define FCE_EMPTY 0xffff /* doesn't represent an actual cluster # */
+
+/*
+ * Set a slot in the fat cache.
+ */
+#define fc_setcache(dep, slot, frcn, fsrcn) \
+ (dep)->de_fc[slot].fc_frcn = frcn; \
+ (dep)->de_fc[slot].fc_fsrcn = fsrcn;
+
+/*
+ * This is the in memory variant of a dos directory entry. It is usually
+ * contained within a vnode.
+ */
+struct denode {
+ struct denode *de_next; /* Hash chain forward */
+ struct denode **de_prev; /* Hash chain back */
+ struct vnode *de_vnode; /* addr of vnode we are part of */
+ struct vnode *de_devvp; /* vnode of blk dev we live on */
+ u_long de_flag; /* flag bits */
+ dev_t de_dev; /* device where direntry lives */
+ u_long de_dirclust; /* cluster of the directory file containing this entry */
+ u_long de_diroffset; /* ordinal of this entry in the directory */
+ u_long de_fndclust; /* cluster of found dir entry */
+ u_long de_fndoffset; /* offset of found dir entry */
+ long de_refcnt; /* reference count */
+ struct msdosfsmount *de_pmp; /* addr of our mount struct */
+ struct lockf *de_lockf; /* byte level lock list */
+ pid_t de_lockholder; /* current lock holder */
+ pid_t de_lockwaiter; /* lock wanter */
+ /* the next two fields must be contiguous in memory... */
+ u_char de_Name[8]; /* name, from directory entry */
+ u_char de_Extension[3]; /* extension, from directory entry */
+ u_char de_Attributes; /* attributes, from directory entry */
+ u_short de_Time; /* creation time */
+ u_short de_Date; /* creation date */
+ u_short de_StartCluster; /* starting cluster of file */
+ u_long de_FileSize; /* size of file in bytes */
+ struct fatcache de_fc[FC_SIZE]; /* fat cache */
+};
+
+/*
+ * Values for the de_flag field of the denode.
+ */
+#define DE_LOCKED 0x0001 /* directory entry is locked */
+#define DE_WANTED 0x0002 /* someone wants this de */
+#define DE_UPDATE 0x0004 /* file has been modified */
+#define DE_MODIFIED 0x0080 /* denode wants to be written back to disk */
+
+/*
+ * Transfer directory entries between internal and external form.
+ * dep is a struct denode * (internal form),
+ * dp is a struct direntry * (external form).
+ */
+#define DE_INTERNALIZE(dep, dp) \
+ (bcopy((dp)->deName, (dep)->de_Name, 11), \
+ (dep)->de_Attributes = (dp)->deAttributes, \
+ (dep)->de_Time = getushort((dp)->deTime), \
+ (dep)->de_Date = getushort((dp)->deDate), \
+ (dep)->de_StartCluster = getushort((dp)->deStartCluster), \
+ (dep)->de_FileSize = getulong((dp)->deFileSize))
+
+#define DE_EXTERNALIZE(dp, dep) \
+ (bcopy((dep)->de_Name, (dp)->deName, 11), \
+ (dp)->deAttributes = (dep)->de_Attributes, \
+ putushort((dp)->deTime, (dep)->de_Time), \
+ putushort((dp)->deDate, (dep)->de_Date), \
+ putushort((dp)->deStartCluster, (dep)->de_StartCluster), \
+ putulong((dp)->deFileSize, (dep)->de_FileSize))
+
+#define de_forw de_chain[0]
+#define de_back de_chain[1]
+
+#ifdef KERNEL
+
+#define VTODE(vp) ((struct denode *)(vp)->v_data)
+#define DETOV(de) ((de)->de_vnode)
+
+#define DE_UPDAT(dep, t, waitfor) \
+ if (dep->de_flag & DE_UPDATE) \
+ (void) deupdat(dep, t, waitfor);
+
+#define DE_TIMES(dep, t) \
+ if (dep->de_flag & DE_UPDATE) { \
+ (dep)->de_flag |= DE_MODIFIED; \
+ unix2dostime(t, &dep->de_Date, &dep->de_Time); \
+ (dep)->de_flag &= ~DE_UPDATE; \
+ }
+
+/*
+ * This overlays the fid sturcture (see mount.h)
+ */
+struct defid {
+ u_short defid_len; /* length of structure */
+ u_short defid_pad; /* force long alignment */
+
+ u_long defid_dirclust; /* cluster this dir entry came from */
+ u_long defid_dirofs; /* index of entry within the cluster */
+
+ /* u_long defid_gen; generation number */
+};
+
+/*
+ * Prototypes for MSDOSFS vnode operations
+ */
+int msdosfs_lookup __P((struct vop_lookup_args *));
+int msdosfs_create __P((struct vop_create_args *));
+int msdosfs_mknod __P((struct vop_mknod_args *));
+int msdosfs_open __P((struct vop_open_args *));
+int msdosfs_close __P((struct vop_close_args *));
+int msdosfs_access __P((struct vop_access_args *));
+int msdosfs_getattr __P((struct vop_getattr_args *));
+int msdosfs_setattr __P((struct vop_setattr_args *));
+int msdosfs_read __P((struct vop_read_args *));
+int msdosfs_write __P((struct vop_write_args *));
+int msdosfs_ioctl __P((struct vop_ioctl_args *));
+int msdosfs_select __P((struct vop_select_args *));
+int msdosfs_mmap __P((struct vop_mmap_args *));
+int msdosfs_fsync __P((struct vop_fsync_args *));
+int msdosfs_seek __P((struct vop_seek_args *));
+int msdosfs_remove __P((struct vop_remove_args *));
+int msdosfs_link __P((struct vop_link_args *));
+int msdosfs_rename __P((struct vop_rename_args *));
+int msdosfs_mkdir __P((struct vop_mkdir_args *));
+int msdosfs_rmdir __P((struct vop_rmdir_args *));
+int msdosfs_symlink __P((struct vop_symlink_args *));
+int msdosfs_readdir __P((struct vop_readdir_args *));
+int msdosfs_readlink __P((struct vop_readlink_args *));
+int msdosfs_abortop __P((struct vop_abortop_args *));
+int msdosfs_inactive __P((struct vop_inactive_args *));
+int msdosfs_reclaim __P((struct vop_reclaim_args *));
+int msdosfs_lock __P((struct vop_lock_args *));
+int msdosfs_unlock __P((struct vop_unlock_args *));
+int msdosfs_bmap __P((struct vop_bmap_args *));
+int msdosfs_strategy __P((struct vop_strategy_args *));
+int msdosfs_print __P((struct vop_print_args *));
+int msdosfs_islocked __P((struct vop_islocked_args *));
+int msdosfs_advlock __P((struct vop_advlock_args *));
+int msdosfs_reallocblks __P((struct vop_reallocblks_args *));
+
+/*
+ * Internal service routine prototypes.
+ */
+int deget __P((struct msdosfsmount * pmp, u_long dirclust, u_long diroffset, struct direntry * direntptr, struct denode ** depp));
+#endif /* KERNEL */
diff --git a/sys/msdosfs/direntry.h b/sys/msdosfs/direntry.h
new file mode 100644
index 0000000..d2ec019
--- /dev/null
+++ b/sys/msdosfs/direntry.h
@@ -0,0 +1,104 @@
+/* $Id$ */
+/* $NetBSD: direntry.h,v 1.7 1994/08/21 18:43:54 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * Structure of a dos directory entry.
+ */
+struct direntry {
+ u_char deName[8]; /* filename, blank filled */
+#define SLOT_EMPTY 0x00 /* slot has never been used */
+#define SLOT_E5 0x05 /* the real value is 0xe5 */
+#define SLOT_DELETED 0xe5 /* file in this slot deleted */
+ u_char deExtension[3]; /* extension, blank filled */
+ u_char deAttributes; /* file attributes */
+#define ATTR_NORMAL 0x00 /* normal file */
+#define ATTR_READONLY 0x01 /* file is readonly */
+#define ATTR_HIDDEN 0x02 /* file is hidden */
+#define ATTR_SYSTEM 0x04 /* file is a system file */
+#define ATTR_VOLUME 0x08 /* entry is a volume label */
+#define ATTR_DIRECTORY 0x10 /* entry is a directory name */
+#define ATTR_ARCHIVE 0x20 /* file is new or modified */
+ u_char deReserved[10]; /* reserved */
+ u_char deTime[2]; /* create/last update time */
+ u_char deDate[2]; /* create/last update date */
+ u_char deStartCluster[2]; /* starting cluster of file */
+ u_char deFileSize[4]; /* size of file in bytes */
+};
+
+/*
+ * This is the format of the contents of the deTime field in the direntry
+ * structure.
+ * We don't use bitfields because we don't know how compilers for
+ * arbitrary machines will lay them out.
+ */
+#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
+#define DT_2SECONDS_SHIFT 0
+#define DT_MINUTES_MASK 0x7E0 /* minutes */
+#define DT_MINUTES_SHIFT 5
+#define DT_HOURS_MASK 0xF800 /* hours */
+#define DT_HOURS_SHIFT 11
+
+/*
+ * This is the format of the contents of the deDate field in the direntry
+ * structure.
+ */
+#define DD_DAY_MASK 0x1F /* day of month */
+#define DD_DAY_SHIFT 0
+#define DD_MONTH_MASK 0x1E0 /* month */
+#define DD_MONTH_SHIFT 5
+#define DD_YEAR_MASK 0xFE00 /* year - 1980 */
+#define DD_YEAR_SHIFT 9
+
+#ifdef KERNEL
+void unix2dostime __P((struct timespec * tsp, u_short * ddp, u_short * dtp));
+void dos2unixtime __P((u_short dd, u_short dt, struct timespec * tsp));
+int dos2unixfn __P((u_char dn[11], u_char * un));
+void unix2dosfn __P((u_char * un, u_char dn[11], int unlen));
+#endif /* KERNEL */
diff --git a/sys/msdosfs/fat.h b/sys/msdosfs/fat.h
new file mode 100644
index 0000000..3867c87
--- /dev/null
+++ b/sys/msdosfs/fat.h
@@ -0,0 +1,97 @@
+/* $Id$ */
+/* $NetBSD: fat.h,v 1.4 1994/08/21 18:43:57 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * Some useful cluster numbers.
+ */
+#define MSDOSFSROOT 0 /* cluster 0 means the root dir */
+#define CLUST_FREE 0 /* cluster 0 also means a free cluster */
+#define MSDOSFSFREE CLUST_FREE
+#define CLUST_FIRST 2 /* first legal cluster number */
+#define CLUST_RSRVS 0xfff0 /* start of reserved cluster range */
+#define CLUST_RSRVE 0xfff6 /* end of reserved cluster range */
+#define CLUST_BAD 0xfff7 /* a cluster with a defect */
+#define CLUST_EOFS 0xfff8 /* start of eof cluster range */
+#define CLUST_EOFE 0xffff /* end of eof cluster range */
+
+#define FAT12_MASK 0x0fff /* mask for 12 bit cluster numbers */
+#define FAT16_MASK 0xffff /* mask for 16 bit cluster numbers */
+
+/*
+ * Return true if filesystem uses 12 bit fats. Microsoft Programmer's
+ * Reference says if the maximum cluster number in a filesystem is greater
+ * than 4086 then we've got a 16 bit fat filesystem.
+ */
+#define FAT12(pmp) (pmp->pm_maxcluster <= 4086)
+#define FAT16(pmp) (pmp->pm_maxcluster > 4086)
+
+#define MSDOSFSEOF(cn) (((cn) & 0xfff8) == 0xfff8)
+
+#ifdef KERNEL
+/*
+ * These are the values for the function argument to the function
+ * fatentry().
+ */
+#define FAT_GET 0x0001 /* get a fat entry */
+#define FAT_SET 0x0002 /* set a fat entry */
+#define FAT_GET_AND_SET (FAT_GET | FAT_SET)
+
+/*
+ * Flags to extendfile:
+ */
+#define DE_CLEAR 1 /* Zero out the blocks allocated */
+
+int pcbmap __P((struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp));
+int clusterfree __P((struct msdosfsmount *pmp, u_long cn, u_long *oldcnp));
+int clusteralloc __P((struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith, u_long *retcluster, u_long *got));
+int fatentry __P((int function, struct msdosfsmount *pmp, u_long cluster, u_long *oldcontents, u_long newcontents));
+int freeclusterchain __P((struct msdosfsmount *pmp, u_long startchain));
+int extendfile __P((struct denode *dep, u_long count, struct buf **bpp, u_long *ncp, int flags));
+#endif /* KERNEL */
diff --git a/sys/msdosfs/msdosfs_conv.c b/sys/msdosfs/msdosfs_conv.c
new file mode 100644
index 0000000..da8ce97
--- /dev/null
+++ b/sys/msdosfs/msdosfs_conv.c
@@ -0,0 +1,358 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_conv.c,v 1.6.2.1 1994/08/30 02:27:57 cgd Exp $ */
+
+/*
+ * 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
+ */
+
+/*
+ * System include files.
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/kernel.h> /* defines tz */
+
+/*
+ * MSDOSFS include files.
+ */
+#include <msdosfs/direntry.h>
+
+/*
+ * Days in each month in a regular year.
+ */
+u_short regyear[] = {
+ 31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * Days in each month in a leap year.
+ */
+u_short leapyear[] = {
+ 31, 29, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * Variables used to remember parts of the last time conversion. Maybe we
+ * can avoid a full conversion.
+ */
+u_long lasttime;
+u_long lastday;
+u_short lastddate;
+u_short lastdtime;
+
+/*
+ * Convert the unix version of time to dos's idea of time to be used in
+ * file timestamps. The passed in unix time is assumed to be in GMT.
+ */
+void
+unix2dostime(tsp, ddp, dtp)
+ struct timespec *tsp;
+ u_short *ddp;
+ u_short *dtp;
+{
+ u_long t;
+ u_long days;
+ u_long inc;
+ u_long year;
+ u_long month;
+ u_short *months;
+
+ /*
+ * If the time from the last conversion is the same as now, then
+ * skip the computations and use the saved result.
+ */
+ t = tsp->ts_sec - (tz.tz_minuteswest * 60)
+ /* +- daylight savings time correction */ ;
+ if (lasttime != t) {
+ lasttime = t;
+ lastdtime = (((t % 60) >> 1) << DT_2SECONDS_SHIFT)
+ + (((t / 60) % 60) << DT_MINUTES_SHIFT)
+ + (((t / 3600) % 24) << DT_HOURS_SHIFT);
+
+ /*
+ * If the number of days since 1970 is the same as the last
+ * time we did the computation then skip all this leap year
+ * and month stuff.
+ */
+ days = t / (24 * 60 * 60);
+ if (days != lastday) {
+ lastday = days;
+ for (year = 1970;; year++) {
+ inc = year & 0x03 ? 365 : 366;
+ if (days < inc)
+ break;
+ days -= inc;
+ }
+ months = year & 0x03 ? regyear : leapyear;
+ for (month = 0; month < 12; month++) {
+ if (days < months[month])
+ break;
+ days -= months[month];
+ }
+ lastddate = ((days + 1) << DD_DAY_SHIFT)
+ + ((month + 1) << DD_MONTH_SHIFT);
+ /*
+ * Remember dos's idea of time is relative to 1980.
+ * unix's is relative to 1970. If somehow we get a
+ * time before 1980 then don't give totally crazy
+ * results.
+ */
+ if (year > 1980)
+ lastddate += (year - 1980) << DD_YEAR_SHIFT;
+ }
+ }
+ *dtp = lastdtime;
+ *ddp = lastddate;
+}
+
+/*
+ * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
+ * interval there were 8 regular years and 2 leap years.
+ */
+#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
+
+u_short lastdosdate;
+u_long lastseconds;
+
+/*
+ * Convert from dos' idea of time to unix'. This will probably only be
+ * called from the stat(), and fstat() system calls and so probably need
+ * not be too efficient.
+ */
+void
+dos2unixtime(dd, dt, tsp)
+ u_short dd;
+ u_short dt;
+ struct timespec *tsp;
+{
+ u_long seconds;
+ u_long m, month;
+ u_long y, year;
+ u_long days;
+ u_short *months;
+
+ seconds = ((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT)
+ + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60
+ + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600;
+ /*
+ * If the year, month, and day from the last conversion are the
+ * same then use the saved value.
+ */
+ if (lastdosdate != dd) {
+ lastdosdate = dd;
+ days = 0;
+ year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT;
+ for (y = 0; y < year; y++) {
+ days += y & 0x03 ? 365 : 366;
+ }
+ months = year & 0x03 ? regyear : leapyear;
+ /*
+ * Prevent going from 0 to 0xffffffff in the following
+ * loop.
+ */
+ month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT;
+ if (month == 0) {
+ printf("dos2unixtime(): month value out of range (%d)\n",
+ month);
+ month = 1;
+ }
+ for (m = 0; m < month - 1; m++) {
+ days += months[m];
+ }
+ days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
+ lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
+ }
+ tsp->ts_sec = seconds + lastseconds + (tz.tz_minuteswest * 60)
+ /* -+ daylight savings time correction */ ;
+ tsp->ts_nsec = 0;
+}
+
+/*
+ * Cheezy macros to do case detection and conversion for the ascii
+ * character set. DOESN'T work for ebcdic.
+ */
+#define isupper(c) (c >= 'A' && c <= 'Z')
+#define islower(c) (c >= 'a' && c <= 'z')
+#define toupper(c) (c & ~' ')
+#define tolower(c) (c | ' ')
+
+/*
+ * DOS filenames are made of 2 parts, the name part and the extension part.
+ * The name part is 8 characters long and the extension part is 3
+ * characters long. They may contain trailing blanks if the name or
+ * extension are not long enough to fill their respective fields.
+ */
+
+/*
+ * Convert a DOS filename to a unix filename. And, return the number of
+ * characters in the resulting unix filename excluding the terminating
+ * null.
+ */
+int
+dos2unixfn(dn, un)
+ u_char dn[11];
+ u_char *un;
+{
+ int i;
+ int ni;
+ int ei;
+ int thislong = 0;
+ u_char c;
+ u_char *origun = un;
+
+ /*
+ * Find the last character in the name portion of the dos filename.
+ */
+ for (ni = 7; ni >= 0; ni--)
+ if (dn[ni] != ' ')
+ break;
+
+ /*
+ * Find the last character in the extension portion of the
+ * filename.
+ */
+ for (ei = 10; ei >= 8; ei--)
+ if (dn[ei] != ' ')
+ break;
+
+ /*
+ * Copy the name portion into the unix filename string. NOTE: DOS
+ * filenames are usually kept in upper case. To make it more unixy
+ * we convert all DOS filenames to lower case. Some may like this,
+ * some may not.
+ */
+ for (i = 0; i <= ni; i++) {
+ c = dn[i];
+ *un++ = isupper(c) ? tolower(c) : c;
+ thislong++;
+ }
+
+ /*
+ * Now, if there is an extension then put in a period and copy in
+ * the extension.
+ */
+ if (ei >= 8) {
+ *un++ = '.';
+ thislong++;
+ for (i = 8; i <= ei; i++) {
+ c = dn[i];
+ *un++ = isupper(c) ? tolower(c) : c;
+ thislong++;
+ }
+ }
+ *un++ = 0;
+
+ /*
+ * If first char of the filename is SLOT_E5 (0x05), then the real
+ * first char of the filename should be 0xe5. But, they couldn't
+ * just have a 0xe5 mean 0xe5 because that is used to mean a freed
+ * directory slot. Another dos quirk.
+ */
+ if (*origun == SLOT_E5)
+ *origun = 0xe5;
+
+ return thislong;
+}
+
+/*
+ * Convert a unix filename to a DOS filename. This function does not ensure
+ * that valid characters for a dos filename are supplied.
+ */
+void
+unix2dosfn(un, dn, unlen)
+ u_char *un;
+ u_char dn[11];
+ int unlen;
+{
+ int i;
+ u_char c;
+
+ /*
+ * Fill the dos filename string with blanks. These are DOS's pad
+ * characters.
+ */
+ for (i = 0; i <= 10; i++)
+ dn[i] = ' ';
+
+ /*
+ * The filenames "." and ".." are handled specially, since they
+ * don't follow dos filename rules.
+ */
+ if (un[0] == '.' && unlen == 1) {
+ dn[0] = '.';
+ return;
+ }
+ if (un[0] == '.' && un[1] == '.' && unlen == 2) {
+ dn[0] = '.';
+ dn[1] = '.';
+ return;
+ }
+
+ /*
+ * Copy the unix filename into the dos filename string upto the end
+ * of string, a '.', or 8 characters. Whichever happens first stops
+ * us. This forms the name portion of the dos filename. Fold to
+ * upper case.
+ */
+ for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) {
+ dn[i] = islower(c) ? toupper(c) : c;
+ un++;
+ unlen--;
+ }
+
+ /*
+ * If the first char of the filename is 0xe5, then translate it to
+ * 0x05. This is because 0xe5 is the marker for a deleted
+ * directory slot. I guess this means you can't have filenames
+ * that start with 0x05. I suppose we should check for this and
+ * doing something about it.
+ */
+ if (dn[0] == SLOT_DELETED)
+ dn[0] = SLOT_E5;
+
+ /*
+ * Strip any further characters up to a '.' or the end of the
+ * string.
+ */
+ while (unlen && (c = *un)) {
+ un++;
+ unlen--;
+ /* Make sure we've skipped over the dot before stopping. */
+ if (c == '.')
+ break;
+ }
+
+ /*
+ * Copy in the extension part of the name, if any. Force to upper
+ * case. Note that the extension is allowed to contain '.'s.
+ * Filenames in this form are probably inaccessable under dos.
+ */
+ for (i = 8; i <= 10 && unlen && (c = *un); i++) {
+ dn[i] = islower(c) ? toupper(c) : c;
+ un++;
+ unlen--;
+ }
+}
+
+/*
+ * Get rid of these macros before someone discovers we are using such
+ * hideous things.
+ */
+#undef isupper
+#undef islower
+#undef toupper
+#undef tolower
diff --git a/sys/msdosfs/msdosfs_denode.c b/sys/msdosfs/msdosfs_denode.c
new file mode 100644
index 0000000..6812863
--- /dev/null
+++ b/sys/msdosfs/msdosfs_denode.c
@@ -0,0 +1,689 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_denode.c,v 1.9 1994/08/21 18:44:00 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h> /* defines "time" */
+
+#include <msdosfs/bpb.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/fat.h>
+
+struct denode **dehashtbl;
+u_long dehash; /* size of hash table - 1 */
+#define DEHASH(dev, deno) (((dev) + (deno)) & dehash)
+
+int msdosfs_init()
+{
+ dehashtbl = hashinit(desiredvnodes/2, M_MSDOSFSMNT, &dehash);
+ return 0;
+}
+
+static struct denode *
+msdosfs_hashget(dev, dirclust, diroff)
+ dev_t dev;
+ u_long dirclust;
+ u_long diroff;
+{
+ struct denode *dep;
+
+ for (;;)
+ for (dep = dehashtbl[DEHASH(dev, dirclust + diroff)];;
+ dep = dep->de_next) {
+ if (dep == NULL)
+ return NULL;
+ if (dirclust != dep->de_dirclust
+ || diroff != dep->de_diroffset
+ || dev != dep->de_dev
+ || dep->de_refcnt == 0)
+ continue;
+ if (dep->de_flag & DE_LOCKED) {
+ dep->de_flag |= DE_WANTED;
+ sleep((caddr_t)dep, PINOD);
+ break;
+ }
+ if (!vget(DETOV(dep), 1))
+ return dep;
+ break;
+ }
+ /* NOTREACHED */
+}
+
+static void
+msdosfs_hashins(dep)
+ struct denode *dep;
+{
+ struct denode **depp, *deq;
+
+ depp = &dehashtbl[DEHASH(dep->de_dev, dep->de_dirclust + dep->de_diroffset)];
+ if (deq = *depp)
+ deq->de_prev = &dep->de_next;
+ dep->de_next = deq;
+ dep->de_prev = depp;
+ *depp = dep;
+ if (dep->de_flag & DE_LOCKED)
+ panic("msdosfs_hashins: already locked");
+ if (curproc)
+ dep->de_lockholder = curproc->p_pid;
+ else
+ dep->de_lockholder = -1;
+ dep->de_flag |= DE_LOCKED;
+}
+
+static void
+msdosfs_hashrem(dep)
+ struct denode *dep;
+{
+ struct denode *deq;
+ if (deq = dep->de_next)
+ deq->de_prev = dep->de_prev;
+ *dep->de_prev = deq;
+#ifdef DIAGNOSTIC
+ dep->de_next = NULL;
+ dep->de_prev = NULL;
+#endif
+}
+
+/*
+ * 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
+ * direntptr - address of the direntry structure of interest. If direntptr is
+ * NULL, the block is read if necessary.
+ * depp - returns the address of the gotten denode.
+ */
+int
+deget(pmp, dirclust, diroffset, direntptr, depp)
+ struct msdosfsmount *pmp; /* so we know the maj/min number */
+ u_long dirclust; /* cluster this dir entry came from */
+ u_long diroffset; /* index of entry within the cluster */
+ struct direntry *direntptr;
+ struct denode **depp; /* returns the addr of the gotten denode */
+{
+ int error;
+ dev_t dev = pmp->pm_dev;
+ struct mount *mntp = pmp->pm_mountp;
+ extern int (**msdosfs_vnodeop_p)();
+ struct denode *ldep;
+ struct vnode *nvp;
+ struct buf *bp;
+
+#ifdef MSDOSFS_DEBUG
+ printf("deget(pmp %08x, dirclust %d, diroffset %x, direntptr %x, depp %08x)\n",
+ pmp, dirclust, diroffset, direntptr, depp);
+#endif
+
+ /*
+ * If dir entry is given and refers to a directory, convert to
+ * canonical form
+ */
+ if (direntptr && (direntptr->deAttributes & ATTR_DIRECTORY)) {
+ dirclust = getushort(direntptr->deStartCluster);
+ if (dirclust == MSDOSFSROOT)
+ diroffset = MSDOSFSROOT_OFS;
+ else
+ diroffset = 0;
+ }
+
+ /*
+ * See if the denode is in the denode cache. Use the location of
+ * the directory entry to compute the hash value. For subdir use
+ * address of "." entry. for root dir use cluster MSDOSFSROOT,
+ * offset MSDOSFSROOT_OFS
+ *
+ * NOTE: The check for de_refcnt > 0 below insures the denode being
+ * examined does not represent an unlinked but still open file.
+ * These files are not to be accessible even when the directory
+ * entry that represented the file happens to be reused while the
+ * deleted file is still open.
+ */
+ if (ldep = msdosfs_hashget(dev, dirclust, diroffset)) {
+ *depp = ldep;
+ return 0;
+ }
+
+
+ /*
+ * Directory entry was not in cache, have to create a vnode and
+ * copy it from the passed disk buffer.
+ */
+ /* getnewvnode() does a VREF() on the vnode */
+ if (error = getnewvnode(VT_MSDOSFS, mntp, msdosfs_vnodeop_p, &nvp)) {
+ *depp = 0;
+ return error;
+ }
+ MALLOC(ldep, struct denode *, sizeof(struct denode), M_MSDOSFSNODE, M_WAITOK);
+ bzero((caddr_t)ldep, sizeof *ldep);
+ nvp->v_data = ldep;
+ ldep->de_vnode = nvp;
+ ldep->de_flag = 0;
+ ldep->de_devvp = 0;
+ ldep->de_lockf = 0;
+ ldep->de_dev = dev;
+ ldep->de_dirclust = dirclust;
+ ldep->de_diroffset = diroffset;
+ fc_purge(ldep, 0); /* init the fat cache for this denode */
+
+ /*
+ * Insert the denode into the hash queue and lock the denode so it
+ * can't be accessed until we've read it in and have done what we
+ * need to it.
+ */
+ msdosfs_hashins(ldep);
+
+ /*
+ * Copy the directory entry into the denode area of the vnode.
+ */
+ if (dirclust == MSDOSFSROOT && 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_Attributes = ATTR_DIRECTORY;
+ 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_Time = 0x0000; /* 00:00:00 */
+ ldep->de_Date = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT)
+ | (1 << DD_DAY_SHIFT);
+ /* Jan 1, 1980 */
+ /* leave the other fields as garbage */
+ } else {
+ bp = NULL;
+ if (!direntptr) {
+ error = readep(pmp, dirclust, diroffset, &bp,
+ &direntptr);
+ if (error)
+ return error;
+ }
+ DE_INTERNALIZE(ldep, direntptr);
+ if (bp)
+ brelse(bp);
+ }
+
+ /*
+ * Fill in a few fields of the vnode and finish filling in the
+ * denode. Then return the address of the found denode.
+ */
+ ldep->de_pmp = pmp;
+ ldep->de_devvp = pmp->pm_devvp;
+ ldep->de_refcnt = 1;
+ 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;
+
+ nvp->v_type = VDIR;
+ if (ldep->de_StartCluster == MSDOSFSROOT)
+ nvp->v_flag |= VROOT;
+ else {
+ error = pcbmap(ldep, 0xffff, 0, &size);
+ if (error == E2BIG) {
+ ldep->de_FileSize = size << pmp->pm_cnshift;
+ error = 0;
+ } else
+ printf("deget(): pcbmap returned %d\n", error);
+ }
+ } else
+ nvp->v_type = VREG;
+ VREF(ldep->de_devvp);
+ *depp = ldep;
+ return 0;
+}
+
+int
+deupdat(dep, tp, waitfor)
+ struct denode *dep;
+ struct timespec *tp;
+ int waitfor;
+{
+ int error;
+ daddr_t bn;
+ int diro;
+ struct buf *bp;
+ struct direntry *dirp;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct timespec ts;
+ struct vnode *vp = DETOV(dep);
+
+#ifdef MSDOSFS_DEBUG
+ printf("deupdat(): dep %08x\n", dep);
+#endif
+
+ /*
+ * If the update bit is off, or this denode is from a readonly
+ * filesystem, or this denode is for a directory, or the denode
+ * represents an open but unlinked file then don't do anything. DOS
+ * directory entries that describe a directory do not ever get
+ * updated. This is the way dos treats them.
+ */
+ if ((dep->de_flag & DE_UPDATE) == 0 ||
+ vp->v_mount->mnt_flag & MNT_RDONLY ||
+ dep->de_Attributes & ATTR_DIRECTORY ||
+ dep->de_refcnt <= 0)
+ return 0;
+
+ /*
+ * Read in the cluster containing the directory entry we want to
+ * update.
+ */
+ if (error = readde(dep, &bp, &dirp))
+ return error;
+
+ /*
+ * Put the passed in time into the directory entry.
+ */
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ unix2dostime(&ts, &dep->de_Date, &dep->de_Time);
+ dep->de_flag &= ~DE_UPDATE;
+
+ /*
+ * Copy the directory entry out of the denode into the cluster it
+ * came from.
+ */
+ DE_EXTERNALIZE(dirp, dep);
+
+ /*
+ * Write the cluster back to disk. If they asked for us to wait
+ * for the write to complete, then use bwrite() otherwise use
+ * bdwrite().
+ */
+ error = 0; /* note that error is 0 from above, but ... */
+ if (waitfor)
+ error = bwrite(bp);
+ else
+ bdwrite(bp);
+ return error;
+}
+
+/*
+ * Truncate the file described by dep to the length specified by length.
+ */
+int
+detrunc(dep, length, flags, cred, p)
+ struct denode *dep;
+ u_long length;
+ int flags;
+ struct ucred *cred;
+ struct proc *p;
+{
+ int error;
+ int allerror;
+ int vflags;
+ u_long eofentry;
+ u_long chaintofree;
+ daddr_t bn;
+ 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 %d, flags %d\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 (DETOV(dep)->v_flag & VROOT) {
+ printf("detrunc(): can't truncate root directory, clust %d, offset %d\n",
+ dep->de_dirclust, dep->de_diroffset);
+ return EINVAL;
+ }
+
+ vnode_pager_setsize(DETOV(dep), length);
+
+ if (dep->de_FileSize < length)
+ return deextend(dep, length, cred);
+
+ /*
+ * 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 {
+ if (error = pcbmap(dep, de_clcount(pmp, length) - 1, 0, &eofentry)) {
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): pcbmap fails %d\n", error);
+#endif
+ return error;
+ }
+ }
+
+ fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift);
+
+ /*
+ * 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) {
+ /*
+ * should read from file vnode or filesystem vnode
+ * depending on if file or dir
+ */
+ if (isadir) {
+ bn = cntobn(pmp, eofentry);
+ error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
+ NOCRED, &bp);
+ } else {
+ bn = de_blk(pmp, length);
+ error = bread(DETOV(dep), bn, pmp->pm_bpcluster,
+ NOCRED, &bp);
+ }
+ if (error) {
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): bread fails %d\n", error);
+#endif
+ return error;
+ }
+ vnode_pager_uncache(DETOV(dep)); /* what's this for? */
+ /*
+ * is this the right place for it?
+ */
+ bzero(bp->b_data + boff, 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;
+ dep->de_flag |= DE_UPDATE;
+ vflags = (length > 0 ? V_SAVE : 0) | V_SAVEMETA;
+ vinvalbuf(DETOV(dep), vflags, cred, p, 0, 0);
+ allerror = deupdat(dep, &time, 1);
+#ifdef MSDOSFS_DEBUG
+ printf("detrunc(): allerror %d, eofentry %d\n",
+ allerror, eofentry);
+#endif
+
+ /*
+ * If we need to break the cluster chain for the file then do it
+ * now.
+ */
+ if (eofentry != ~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;
+ }
+ fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift,
+ eofentry);
+ }
+
+ /*
+ * Now free the clusters removed from the file because of the
+ * truncation.
+ */
+ if (chaintofree != 0 && !MSDOSFSEOF(chaintofree))
+ freeclusterchain(pmp, chaintofree);
+
+ return allerror;
+}
+
+/*
+ * Extend the file described by dep to length specified by length.
+ */
+int
+deextend(dep, length, cred)
+ struct denode *dep;
+ off_t length;
+ struct ucred *cred;
+{
+ struct msdosfsmount *pmp = dep->de_pmp;
+ u_long count;
+ int error;
+
+ /*
+ * The root of a DOS filesystem cannot be extended.
+ */
+ if (DETOV(dep)->v_flag & VROOT)
+ return EINVAL;
+
+ /*
+ * Directories can only be extended by the superuser.
+ * Is this really important?
+ */
+ if (dep->de_Attributes & ATTR_DIRECTORY) {
+ if (error = suser(cred, NULL))
+ return error;
+ }
+
+ if (length <= dep->de_FileSize)
+ panic("deextend: file too large");
+
+ /*
+ * 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;
+ if (error = extendfile(dep, count, NULL, NULL, DE_CLEAR)) {
+ /* truncate the added clusters away again */
+ (void) detrunc(dep, dep->de_FileSize, 0, cred, NULL);
+ return error;
+ }
+ }
+
+ dep->de_flag |= DE_UPDATE;
+ dep->de_FileSize = length;
+ return deupdat(dep, &time, 1);
+}
+
+/*
+ * Move a denode to its correct hash queue after the file it represents has
+ * been moved to a new directory.
+ */
+int reinsert(dep)
+ struct denode *dep;
+{
+ union dehead *deh;
+
+ /*
+ * Fix up the denode cache. If the denode is for a directory,
+ * there is nothing to do since the hash is based on the starting
+ * cluster of the directory file and that hasn't changed. If for a
+ * file the hash is based on the location of the directory entry,
+ * so we must remove it from the cache and re-enter it with the
+ * hash based on the new location of the directory entry.
+ */
+ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
+ msdosfs_hashrem(dep);
+ msdosfs_hashins(dep);
+ }
+ return 0;
+}
+
+int
+msdosfs_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct denode *dep = VTODE(vp);
+ int i;
+ extern int prtactive;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_reclaim(): dep %08x, file %s, refcnt %d\n",
+ dep, dep->de_Name, dep->de_refcnt);
+#endif
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("msdosfs_reclaim(): pushing active", vp);
+
+ /*
+ * Remove the denode from the denode hash chain we are in.
+ */
+ msdosfs_hashrem(dep);
+
+ cache_purge(vp);
+ /*
+ * Indicate that one less file on the filesystem is open.
+ */
+ if (dep->de_devvp) {
+ vrele(dep->de_devvp);
+ dep->de_devvp = 0;
+ }
+
+ dep->de_flag = 0;
+
+ FREE(dep, M_MSDOSFSNODE);
+ vp->v_data = NULL;
+
+ return 0;
+}
+
+int
+msdosfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct denode *dep = VTODE(vp);
+ int error = 0;
+ extern int prtactive;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_inactive(): dep %08x, de_Name[0] %x\n", dep, dep->de_Name[0]);
+#endif
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("msdosfs_inactive(): pushing active", vp);
+
+ /*
+ * Get rid of denodes related to stale file handles. Hmmm, what
+ * does this really do?
+ */
+ if (dep->de_Name[0] == SLOT_DELETED) {
+ if ((vp->v_flag & VXLOCK) == 0)
+ vgone(vp);
+ return 0;
+ }
+
+ /*
+ * If the file has been deleted and it is on a read/write
+ * filesystem, then truncate the file, and mark the directory slot
+ * as empty. (This may not be necessary for the dos filesystem.)
+ */
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_inactive(): dep %08x, refcnt %d, mntflag %x, MNT_RDONLY %x\n",
+ dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY);
+#endif
+ VOP_LOCK(vp);
+ if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+ error = detrunc(dep, (u_long) 0, 0, NOCRED, NULL);
+ dep->de_flag |= DE_UPDATE;
+ dep->de_Name[0] = SLOT_DELETED;
+ }
+ DE_UPDAT(dep, &time, 0);
+ VOP_UNLOCK(vp);
+ dep->de_flag = 0;
+
+ /*
+ * If we are done with the denode, then reclaim it so that it can
+ * be reused now.
+ */
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount,
+ dep->de_Name[0]);
+#endif
+ if (vp->v_usecount == 0 && dep->de_Name[0] == SLOT_DELETED)
+ vgone(vp);
+ return error;
+}
diff --git a/sys/msdosfs/msdosfs_fat.c b/sys/msdosfs/msdosfs_fat.c
new file mode 100644
index 0000000..9a2f117
--- /dev/null
+++ b/sys/msdosfs/msdosfs_fat.c
@@ -0,0 +1,984 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_fat.c,v 1.12 1994/08/21 18:44:04 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * kernel include files.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/namei.h>
+#include <sys/mount.h> /* to define statfs structure */
+#include <sys/vnode.h> /* to define vattr structure */
+#include <sys/errno.h>
+
+/*
+ * msdosfs include files.
+ */
+#include <msdosfs/bpb.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/fat.h>
+
+/*
+ * Fat cache stats.
+ */
+int fc_fileextends; /* # of file extends */
+int fc_lfcempty; /* # of time last file cluster cache entry
+ * was empty */
+int fc_bmapcalls; /* # of times pcbmap was called */
+
+#define LMMAX 20
+int fc_lmdistance[LMMAX]; /* counters for how far off the last
+ * cluster mapped entry was. */
+int fc_largedistance; /* off by more than LMMAX */
+
+/* Byte offset in FAT on filesystem pmp, cluster cn */
+#define FATOFS(pmp, cn) (FAT12(pmp) ? (cn) * 3 / 2 : (cn) * 2)
+
+void fc_lookup(struct denode *dep, u_long findcn,
+ u_long *frcnp, u_long *fsrcnp);
+void fc_purge(struct denode *dep, u_int frcn);
+
+static void
+fatblock(pmp, ofs, bnp, sizep, bop)
+ struct msdosfsmount *pmp;
+ u_long ofs;
+ u_long *bnp;
+ u_long *sizep;
+ u_long *bop;
+{
+ u_long bn, size;
+
+ bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
+ size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn)
+ * pmp->pm_BytesPerSec;
+ bn += pmp->pm_fatblk;
+ if (bnp)
+ *bnp = bn;
+ if (sizep)
+ *sizep = size;
+ if (bop)
+ *bop = ofs % pmp->pm_fatblocksize;
+}
+
+/*
+ * Map the logical cluster number of a file into a physical disk sector
+ * that is filesystem relative.
+ *
+ * dep - address of denode representing the file of interest
+ * findcn - file relative cluster whose filesystem relative cluster number
+ * and/or block number are/is to be found
+ * bnp - address of where to place the file system relative block number.
+ * If this pointer is null then don't return this quantity.
+ * cnp - address of where to place the file system relative cluster number.
+ * If this pointer is null then don't return this quantity.
+ *
+ * NOTE: Either bnp or cnp must be non-null.
+ * This function has one side effect. If the requested file relative cluster
+ * is beyond the end of file, then the actual number of clusters in the file
+ * is returned in *cnp. This is useful for determining how long a directory is.
+ * If cnp is null, nothing is returned.
+ */
+int
+pcbmap(dep, findcn, bnp, cnp)
+ struct denode *dep;
+ u_long findcn; /* file relative cluster to get */
+ daddr_t *bnp; /* returned filesys relative blk number */
+ u_long *cnp; /* returned cluster number */
+{
+ int error;
+ u_long i;
+ u_long cn;
+ u_long prevcn;
+ u_long byteoffset;
+ u_long bn;
+ u_long bo;
+ struct buf *bp = NULL;
+ u_long bp_bn = -1;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ u_long bsize;
+ int fat12 = FAT12(pmp); /* 12 bit fat */
+
+ fc_bmapcalls++;
+
+ /*
+ * If they don't give us someplace to return a value then don't
+ * bother doing anything.
+ */
+ if (bnp == NULL && cnp == NULL)
+ return 0;
+
+ cn = dep->de_StartCluster;
+ /*
+ * The "file" that makes up the root directory is contiguous,
+ * permanently allocated, of fixed size, and is not made up of
+ * clusters. If the cluster number is beyond the end of the root
+ * directory, then return the number of clusters in the file.
+ */
+ if (cn == MSDOSFSROOT) {
+ if (dep->de_Attributes & ATTR_DIRECTORY) {
+ if (findcn * pmp->pm_SectPerClust > pmp->pm_rootdirsize) {
+ if (cnp)
+ *cnp = pmp->pm_rootdirsize / pmp->pm_SectPerClust;
+ return E2BIG;
+ }
+ if (bnp)
+ *bnp = pmp->pm_rootdirblk + (findcn * pmp->pm_SectPerClust);
+ if (cnp)
+ *cnp = MSDOSFSROOT;
+ return 0;
+ } else { /* just an empty file */
+ if (cnp)
+ *cnp = 0;
+ return E2BIG;
+ }
+ }
+
+ /*
+ * Rummage around in the fat cache, maybe we can avoid tromping
+ * thru every fat entry for the file. And, keep track of how far
+ * off the cache was from where we wanted to be.
+ */
+ i = 0;
+ fc_lookup(dep, findcn, &i, &cn);
+ if ((bn = findcn - i) >= LMMAX)
+ fc_largedistance++;
+ else
+ fc_lmdistance[bn]++;
+
+ /*
+ * Handle all other files or directories the normal way.
+ */
+ prevcn = 0;
+ for (; i < findcn; i++) {
+ if (MSDOSFSEOF(cn))
+ goto hiteof;
+ byteoffset = FATOFS(pmp, cn);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (bn != bp_bn) {
+ if (bp)
+ brelse(bp);
+ error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
+ if (error)
+ return error;
+ bp_bn = bn;
+ }
+ prevcn = cn;
+ cn = getushort(&bp->b_data[bo]);
+ if (fat12) {
+ if (prevcn & 1)
+ cn >>= 4;
+ cn &= 0x0fff;
+ /*
+ * Force the special cluster numbers in the range
+ * 0x0ff0-0x0fff to be the same as for 16 bit
+ * cluster numbers to let the rest of msdosfs think
+ * it is always dealing with 16 bit fats.
+ */
+ if ((cn & 0x0ff0) == 0x0ff0)
+ cn |= 0xf000;
+ }
+ }
+
+ if (!MSDOSFSEOF(cn)) {
+ if (bp)
+ brelse(bp);
+ if (bnp)
+ *bnp = cntobn(pmp, cn);
+ if (cnp)
+ *cnp = cn;
+ fc_setcache(dep, FC_LASTMAP, i, cn);
+ return 0;
+ }
+
+hiteof:;
+ if (cnp)
+ *cnp = i;
+ if (bp)
+ brelse(bp);
+ /* update last file cluster entry in the fat cache */
+ fc_setcache(dep, FC_LASTFC, i - 1, prevcn);
+ return E2BIG;
+}
+
+/*
+ * Find the closest entry in the fat cache to the cluster we are looking
+ * for.
+ */
+void fc_lookup(dep, findcn, frcnp, fsrcnp)
+ struct denode *dep;
+ u_long findcn;
+ u_long *frcnp;
+ u_long *fsrcnp;
+{
+ int i;
+ u_long cn;
+ struct fatcache *closest = 0;
+
+ for (i = 0; i < FC_SIZE; i++) {
+ cn = dep->de_fc[i].fc_frcn;
+ if (cn != FCE_EMPTY && cn <= findcn) {
+ if (closest == 0 || cn > closest->fc_frcn)
+ closest = &dep->de_fc[i];
+ }
+ }
+ if (closest) {
+ *frcnp = closest->fc_frcn;
+ *fsrcnp = closest->fc_fsrcn;
+ }
+}
+
+/*
+ * Purge the fat cache in denode dep of all entries relating to file
+ * relative cluster frcn and beyond.
+ */
+void fc_purge(dep, frcn)
+ struct denode *dep;
+ u_int frcn;
+{
+ int i;
+ struct fatcache *fcp;
+
+ fcp = dep->de_fc;
+ for (i = 0; i < FC_SIZE; i++, fcp++) {
+ if (fcp->fc_frcn >= frcn)
+ fcp->fc_frcn = FCE_EMPTY;
+ }
+}
+
+/*
+ * Update all copies of the fat. The first copy is updated last.
+ *
+ * pmp - msdosfsmount structure for filesystem to update
+ * bp - addr of modified fat block
+ * fatbn - block number relative to begin of filesystem of the modified fat block.
+ */
+void
+updatefats(pmp, bp, fatbn)
+ struct msdosfsmount *pmp;
+ struct buf *bp;
+ u_long fatbn;
+{
+ int i;
+ struct buf *bpn;
+
+#ifdef MSDOSFS_DEBUG
+ printf("updatefats(pmp %08x, bp %08x, fatbn %d)\n",
+ pmp, bp, fatbn);
+#endif
+
+ /*
+ * Now copy the block(s) of the modified fat to the other copies of
+ * the fat and write them out. This is faster than reading in the
+ * other fats and then writing them back out. This could tie up
+ * the fat for quite a while. Preventing others from accessing it.
+ * To prevent us from going after the fat quite so much we use
+ * delayed writes, unless they specfied "synchronous" when the
+ * filesystem was mounted. If synch is asked for then use
+ * bwrite()'s and really slow things down.
+ */
+ for (i = 1; i < pmp->pm_FATs; i++) {
+ fatbn += pmp->pm_FATsecs;
+ /* getblk() never fails */
+ bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount, 0, 0);
+ bcopy(bp->b_data, bpn->b_data, bp->b_bcount);
+ if (pmp->pm_waitonfat)
+ bwrite(bpn);
+ else
+ bdwrite(bpn);
+ }
+ /*
+ * Write out the first fat last.
+ */
+ if (pmp->pm_waitonfat)
+ bwrite(bp);
+ else
+ bdwrite(bp);
+}
+
+/*
+ * Updating entries in 12 bit fats is a pain in the butt.
+ *
+ * The following picture shows where nibbles go when moving from a 12 bit
+ * cluster number into the appropriate bytes in the FAT.
+ *
+ * byte m byte m+1 byte m+2
+ * +----+----+ +----+----+ +----+----+
+ * | 0 1 | | 2 3 | | 4 5 | FAT bytes
+ * +----+----+ +----+----+ +----+----+
+ *
+ * +----+----+----+ +----+----+----+
+ * | 3 0 1 | | 4 5 2 |
+ * +----+----+----+ +----+----+----+
+ * cluster n cluster n+1
+ *
+ * Where n is even. m = n + (n >> 2)
+ *
+ */
+extern __inline void
+usemap_alloc(pmp, cn)
+ struct msdosfsmount *pmp;
+ u_long cn;
+{
+ pmp->pm_inusemap[cn / N_INUSEBITS]
+ |= 1 << (cn % N_INUSEBITS);
+ pmp->pm_freeclustercount--;
+}
+
+extern __inline void
+usemap_free(pmp, cn)
+ struct msdosfsmount *pmp;
+ u_long cn;
+{
+ pmp->pm_freeclustercount++;
+ pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS));
+}
+
+int
+clusterfree(pmp, cluster, oldcnp)
+ struct msdosfsmount *pmp;
+ u_long cluster;
+ u_long *oldcnp;
+{
+ int error;
+ u_long oldcn;
+
+ error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE);
+ if (error == 0) {
+ /*
+ * If the cluster was successfully marked free, then update
+ * the count of free clusters, and turn off the "allocated"
+ * bit in the "in use" cluster bit map.
+ */
+ usemap_free(pmp, cluster);
+ if (oldcnp)
+ *oldcnp = oldcn;
+ }
+ return error;
+}
+
+/*
+ * Get or Set or 'Get and Set' the cluster'th entry in the fat.
+ *
+ * function - whether to get or set a fat entry
+ * pmp - address of the msdosfsmount structure for the filesystem
+ * whose fat is to be manipulated.
+ * cn - which cluster is of interest
+ * oldcontents - address of a word that is to receive the contents of the
+ * cluster'th entry if this is a get function
+ * newcontents - the new value to be written into the cluster'th element of
+ * the fat if this is a set function.
+ *
+ * This function can also be used to free a cluster by setting the fat entry
+ * for a cluster to 0.
+ *
+ * All copies of the fat are updated if this is a set function. NOTE: If
+ * fatentry() marks a cluster as free it does not update the inusemap in
+ * the msdosfsmount structure. This is left to the caller.
+ */
+int
+fatentry(function, pmp, cn, oldcontents, newcontents)
+ int function;
+ struct msdosfsmount *pmp;
+ u_long cn;
+ u_long *oldcontents;
+ u_long newcontents;
+{
+ int error;
+ u_long readcn;
+ u_long bn, bo, bsize, byteoffset;
+ struct buf *bp;
+
+ /*
+ * printf("fatentry(func %d, pmp %08x, clust %d, oldcon %08x, newcon %d)\n",
+ * function, pmp, cluster, oldcontents, newcontents);
+ */
+
+#ifdef DIAGNOSTIC
+ /*
+ * Be sure they asked us to do something.
+ */
+ if ((function & (FAT_SET | FAT_GET)) == 0) {
+ printf("fatentry(): function code doesn't specify get or set\n");
+ return EINVAL;
+ }
+
+ /*
+ * If they asked us to return a cluster number but didn't tell us
+ * where to put it, give them an error.
+ */
+ if ((function & FAT_GET) && oldcontents == NULL) {
+ printf("fatentry(): get function with no place to put result\n");
+ return EINVAL;
+ }
+#endif
+
+ /*
+ * Be sure the requested cluster is in the filesystem.
+ */
+ if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster)
+ return EINVAL;
+
+ byteoffset = FATOFS(pmp, cn);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp))
+ return error;
+
+ if (function & FAT_GET) {
+ readcn = getushort(&bp->b_data[bo]);
+ if (FAT12(pmp)) {
+ if (cn & 1)
+ readcn >>= 4;
+ readcn &= 0x0fff;
+ /* map certain 12 bit fat entries to 16 bit */
+ if ((readcn & 0x0ff0) == 0x0ff0)
+ readcn |= 0xf000;
+ }
+ *oldcontents = readcn;
+ }
+ if (function & FAT_SET) {
+ if (FAT12(pmp)) {
+ readcn = getushort(&bp->b_data[bo]);
+ if (cn & 1) {
+ readcn &= 0x000f;
+ readcn |= newcontents << 4;
+ } else {
+ readcn &= 0xf000;
+ readcn |= newcontents & 0xfff;
+ }
+ putushort(&bp->b_data[bo], readcn);
+ } else
+ putushort(&bp->b_data[bo], newcontents);
+ updatefats(pmp, bp, bn);
+ bp = NULL;
+ pmp->pm_fmod = 1;
+ }
+ if (bp)
+ brelse(bp);
+ return 0;
+}
+
+/*
+ * Update a contiguous cluster chain
+ *
+ * pmp - mount point
+ * start - first cluster of chain
+ * count - number of clusters in chain
+ * fillwith - what to write into fat entry of last cluster
+ */
+static int
+fatchain(pmp, start, count, fillwith)
+ struct msdosfsmount *pmp;
+ u_long start;
+ u_long count;
+ u_long fillwith;
+{
+ int error;
+ u_long bn, bo, bsize, byteoffset, readcn, newc;
+ struct buf *bp;
+
+#ifdef MSDOSFS_DEBUG
+ printf("fatchain(pmp %08x, start %d, count %d, fillwith %d)\n",
+ pmp, start, count, fillwith);
+#endif
+ /*
+ * Be sure the clusters are in the filesystem.
+ */
+ if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster)
+ return EINVAL;
+
+ while (count > 0) {
+ byteoffset = FATOFS(pmp, start);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp))
+ return error;
+ while (count > 0) {
+ start++;
+ newc = --count > 0 ? start : fillwith;
+ if (FAT12(pmp)) {
+ readcn = getushort(&bp->b_data[bo]);
+ if (start & 1) {
+ readcn &= 0xf000;
+ readcn |= newc & 0xfff;
+ } else {
+ readcn &= 0x000f;
+ readcn |= newc << 4;
+ }
+ putushort(&bp->b_data[bo], readcn);
+ bo++;
+ if (!(start & 1))
+ bo++;
+ } else {
+ putushort(&bp->b_data[bo], newc);
+ bo += 2;
+ }
+ if (bo >= bsize)
+ break;
+ }
+ updatefats(pmp, bp, bn);
+ }
+ pmp->pm_fmod = 1;
+ return 0;
+}
+
+/*
+ * Check the length of a free cluster chain starting at start.
+ *
+ * pmp - mount point
+ * start - start of chain
+ * count - maximum interesting length
+ */
+int
+chainlength(pmp, start, count)
+ struct msdosfsmount *pmp;
+ u_long start;
+ u_long count;
+{
+ u_long idx, max_idx;
+ u_int map;
+ u_long len;
+
+ max_idx = pmp->pm_maxcluster / N_INUSEBITS;
+ idx = start / N_INUSEBITS;
+ start %= N_INUSEBITS;
+ map = pmp->pm_inusemap[idx];
+ map &= ~((1 << start) - 1);
+ if (map) {
+ len = ffs(map) - 1 - start;
+ return len > count ? count : len;
+ }
+ len = N_INUSEBITS - start;
+ if (len >= count)
+ return count;
+ while (++idx <= max_idx) {
+ if (len >= count)
+ break;
+ if (map = pmp->pm_inusemap[idx]) {
+ len += ffs(map) - 1;
+ break;
+ }
+ len += N_INUSEBITS;
+ }
+ return len > count ? count : len;
+}
+
+/*
+ * Allocate contigous free clusters.
+ *
+ * pmp - mount point.
+ * start - start of cluster chain.
+ * count - number of clusters to allocate.
+ * fillwith - put this value into the fat entry for the
+ * last allocated cluster.
+ * retcluster - put the first allocated cluster's number here.
+ * got - how many clusters were actually allocated.
+ */
+int
+chainalloc(pmp, start, count, fillwith, retcluster, got)
+ struct msdosfsmount *pmp;
+ u_long start;
+ u_long count;
+ u_long fillwith;
+ u_long *retcluster;
+ u_long *got;
+{
+ int error;
+
+ error = fatchain(pmp, start, count, fillwith);
+ if (error == 0) {
+#ifdef MSDOSFS_DEBUG
+ printf("clusteralloc(): allocated cluster chain at %d (%d clusters)\n",
+ start, count);
+#endif
+ if (retcluster)
+ *retcluster = start;
+ if (got)
+ *got = count;
+ while (count-- > 0)
+ usemap_alloc(pmp, start++);
+ }
+ return error;
+}
+
+/*
+ * Allocate contiguous free clusters.
+ *
+ * pmp - mount point.
+ * start - preferred start of cluster chain.
+ * count - number of clusters requested.
+ * fillwith - put this value into the fat entry for the
+ * last allocated cluster.
+ * retcluster - put the first allocated cluster's number here.
+ * got - how many clusters were actually allocated.
+ */
+int
+clusteralloc(pmp, start, count, fillwith, retcluster, got)
+ struct msdosfsmount *pmp;
+ u_long start;
+ u_long count;
+ u_long fillwith;
+ u_long *retcluster;
+ u_long *got;
+{
+ int error;
+ u_long idx;
+ u_long len, newst, foundcn, foundl, cn, l;
+ u_int map;
+
+#ifdef MSDOSFS_DEBUG
+ printf("clusteralloc(): find %d clusters\n",count);
+#endif
+ if (start) {
+ if ((len = chainlength(pmp, start, count)) >= count)
+ return chainalloc(pmp, start, count, fillwith, retcluster, got);
+ } else {
+ /*
+ * This is a new file, initialize start
+ */
+ struct timeval tv;
+
+ microtime(&tv);
+ start = (tv.tv_usec >> 10)|tv.tv_usec;
+ len = 0;
+ }
+
+ /*
+ * Start at a (pseudo) random place to maximize cluster runs
+ * under multiple writers.
+ */
+ foundcn = newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1);
+ foundl = 0;
+
+ for (cn = newst; cn <= pmp->pm_maxcluster;) {
+ idx = cn / N_INUSEBITS;
+ map = pmp->pm_inusemap[idx];
+ map |= (1 << (cn % N_INUSEBITS)) - 1;
+ if (map != (u_int)-1) {
+ cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1;
+ if ((l = chainlength(pmp, cn, count)) >= count)
+ return chainalloc(pmp, cn, count, fillwith, retcluster, got);
+ if (l > foundl) {
+ foundcn = cn;
+ foundl = l;
+ }
+ cn += l + 1;
+ continue;
+ }
+ cn += N_INUSEBITS - cn % N_INUSEBITS;
+ }
+ for (cn = 0; cn < newst;) {
+ idx = cn / N_INUSEBITS;
+ map = pmp->pm_inusemap[idx];
+ map |= (1 << (cn % N_INUSEBITS)) - 1;
+ if (map != (u_int)-1) {
+ cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1;
+ if ((l = chainlength(pmp, cn, count)) >= count)
+ return chainalloc(pmp, cn, count, fillwith, retcluster, got);
+ if (l > foundl) {
+ foundcn = cn;
+ foundl = l;
+ }
+ cn += l + 1;
+ continue;
+ }
+ cn += N_INUSEBITS - cn % N_INUSEBITS;
+ }
+
+ if (!foundl)
+ return ENOSPC;
+
+ if (len)
+ return chainalloc(pmp, start, len, fillwith, retcluster, got);
+ else
+ return chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got);
+}
+
+
+/*
+ * Free a chain of clusters.
+ *
+ * pmp - address of the msdosfs mount structure for the filesystem
+ * containing the cluster chain to be freed.
+ * startcluster - number of the 1st cluster in the chain of clusters to be
+ * freed.
+ */
+int
+freeclusterchain(pmp, cluster)
+ struct msdosfsmount *pmp;
+ u_long cluster;
+{
+ int error = 0;
+ struct buf *bp = NULL;
+ u_long bn, bo, bsize, byteoffset;
+ u_long readcn, lbn = -1;
+
+ while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) {
+ byteoffset = FATOFS(pmp, cluster);
+ fatblock(pmp, byteoffset, &bn, &bsize, &bo);
+ if (lbn != bn) {
+ if (bp)
+ updatefats(pmp, bp, bn);
+ if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp))
+ return error;
+ lbn = bn;
+ }
+ usemap_free(pmp, cluster);
+ readcn = getushort(&bp->b_data[bo]);
+ if (FAT12(pmp)) {
+ if (cluster & 1) {
+ cluster = readcn >> 4;
+ readcn &= 0x000f;
+ readcn |= MSDOSFSFREE << 4;
+ } else {
+ cluster = readcn;
+ readcn &= 0xf000;
+ readcn |= MSDOSFSFREE & 0xfff;
+ }
+ putushort(&bp->b_data[bo], readcn);
+ cluster &= 0x0fff;
+ if ((cluster&0x0ff0) == 0x0ff0)
+ cluster |= 0xf000;
+ } else {
+ cluster = readcn;
+ putushort(&bp->b_data[bo], MSDOSFSFREE);
+ }
+ }
+ if (bp)
+ updatefats(pmp, bp, bn);
+ return error;
+}
+
+/*
+ * Read in fat blocks looking for free clusters. For every free cluster
+ * found turn off its corresponding bit in the pm_inusemap.
+ */
+int
+fillinusemap(pmp)
+ struct msdosfsmount *pmp;
+{
+ struct buf *bp = NULL;
+ u_long cn, readcn;
+ int error;
+ int fat12 = FAT12(pmp);
+ u_long bn, bo, bsize, byteoffset;
+
+ /*
+ * Mark all clusters in use, we mark the free ones in the fat scan
+ * loop further down.
+ */
+ for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++)
+ pmp->pm_inusemap[cn] = (u_int)-1;
+
+ /*
+ * Figure how many free clusters are in the filesystem by ripping
+ * through the fat counting the number of entries whose content is
+ * zero. These represent free clusters.
+ */
+ pmp->pm_freeclustercount = 0;
+ for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) {
+ byteoffset = FATOFS(pmp, cn);
+ bo = byteoffset % pmp->pm_fatblocksize;
+ if (!bo || !bp) {
+ /* Read new FAT block */
+ if (bp)
+ brelse(bp);
+ fatblock(pmp, byteoffset, &bn, &bsize, NULL);
+ error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
+ if (error)
+ return error;
+ }
+ readcn = getushort(&bp->b_data[bo]);
+ if (fat12) {
+ if (cn & 1)
+ readcn >>= 4;
+ readcn &= 0x0fff;
+ }
+
+ if (readcn == 0)
+ usemap_free(pmp, cn);
+ }
+ brelse(bp);
+ return 0;
+}
+
+/*
+ * Allocate a new cluster and chain it onto the end of the file.
+ *
+ * dep - the file to extend
+ * count - number of clusters to allocate
+ * bpp - where to return the address of the buf header for the first new
+ * file block
+ * ncp - where to put cluster number of the first newly allocated cluster
+ * If this pointer is 0, do not return the cluster number.
+ * flags - see fat.h
+ *
+ * NOTE: This function is not responsible for turning on the DE_UPDATE bit of
+ * the de_flag field of the denode and it does not change the de_FileSize
+ * field. This is left for the caller to do.
+ */
+int
+extendfile(dep, count, bpp, ncp, flags)
+ struct denode *dep;
+ u_long count;
+ struct buf **bpp;
+ u_long *ncp;
+ int flags;
+{
+ int error = 0;
+ u_long frcn;
+ u_long cn, got;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct buf *bp;
+
+ /*
+ * Don't try to extend the root directory
+ */
+ if (DETOV(dep)->v_flag & VROOT) {
+ printf("extendfile(): attempt to extend root directory\n");
+ return ENOSPC;
+ }
+
+ /*
+ * If the "file's last cluster" cache entry is empty, and the file
+ * is not empty, then fill the cache entry by calling pcbmap().
+ */
+ fc_fileextends++;
+ if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY &&
+ dep->de_StartCluster != 0) {
+ fc_lfcempty++;
+ error = pcbmap(dep, 0xffff, 0, &cn);
+ /* we expect it to return E2BIG */
+ if (error != E2BIG)
+ return error;
+ error = 0;
+ }
+
+ while (count > 0) {
+ /*
+ * Allocate a new cluster chain and cat onto the end of the file.
+ * If the file is empty we make de_StartCluster point to the new
+ * block. Note that de_StartCluster being 0 is sufficient to be
+ * sure the file is empty since we exclude attempts to extend the
+ * root directory above, and the root dir is the only file with a
+ * startcluster of 0 that has blocks allocated (sort of).
+ */
+ if (dep->de_StartCluster == 0)
+ cn = 0;
+ else
+ cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1;
+ if (error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got))
+ return error;
+
+ count -= got;
+
+ /*
+ * Give them the filesystem relative cluster number if they want
+ * it.
+ */
+ if (ncp) {
+ *ncp = cn;
+ ncp = NULL;
+ }
+
+ if (dep->de_StartCluster == 0) {
+ dep->de_StartCluster = cn;
+ frcn = 0;
+ } else {
+ error = fatentry(FAT_SET, pmp, dep->de_fc[FC_LASTFC].fc_fsrcn,
+ 0, cn);
+ if (error) {
+ clusterfree(pmp, cn, NULL);
+ return error;
+ }
+
+ frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1;
+ }
+
+ /*
+ * Update the "last cluster of the file" entry in the denode's fat
+ * cache.
+ */
+ fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1);
+
+ if (flags & DE_CLEAR) {
+ while (got-- > 0) {
+ /*
+ * Get the buf header for the new block of the file.
+ */
+ if (dep->de_Attributes & ATTR_DIRECTORY)
+ bp = getblk(pmp->pm_devvp, cntobn(pmp, cn++),
+ pmp->pm_bpcluster, 0, 0);
+ else {
+ bp = getblk(DETOV(dep), frcn++, pmp->pm_bpcluster, 0, 0);
+ /*
+ * Do the bmap now, as in msdosfs_write
+ */
+ if (pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0))
+ bp->b_blkno = -1;
+ if (bp->b_blkno == -1)
+ panic("extendfile: pcbmap");
+ }
+ clrbuf(bp);
+ if (bpp) {
+ *bpp = bp;
+ bpp = NULL;
+ } else {
+ bp->b_flags |= B_AGE;
+ bawrite(bp);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/sys/msdosfs/msdosfs_lookup.c b/sys/msdosfs/msdosfs_lookup.c
new file mode 100644
index 0000000..c67f888
--- /dev/null
+++ b/sys/msdosfs/msdosfs_lookup.c
@@ -0,0 +1,763 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_lookup.c,v 1.14 1994/08/21 18:44:07 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <msdosfs/bpb.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/fat.h>
+
+/*
+ * 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
+ * soul 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.
+ */
+int
+msdosfs_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ struct vnode *vdp = ap->a_dvp;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ daddr_t bn;
+ int error;
+ int lockparent;
+ int wantparent;
+ int slotstatus;
+
+#define NONE 0
+#define FOUND 1
+ int slotoffset = -1;
+ int slotcluster = -1;
+ int frcn;
+ u_long cluster;
+ int rootreloff;
+ int diroff;
+ int isadir; /* ~0 if found direntry is a directory */
+ u_long scn; /* starting cluster number */
+ struct vnode *pdp;
+ struct denode *dp;
+ struct denode *tdp;
+ struct msdosfsmount *pmp;
+ struct buf *bp = 0;
+ struct direntry *dep = NULL;
+ u_char dosfilename[12];
+ int flags = cnp->cn_flags;
+ int nameiop = cnp->cn_nameiop;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr);
+#endif
+ dp = VTODE(vdp);
+ pmp = dp->de_pmp;
+ *vpp = NULL;
+ lockparent = flags & LOCKPARENT;
+ wantparent = flags & (LOCKPARENT | WANTPARENT);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): vdp %08x, dp %08x, Attr %02x\n",
+ vdp, dp, dp->de_Attributes);
+#endif
+
+ /*
+ * Be sure vdp is a directory. Since dos filesystems don't have
+ * the concept of execute permission anybody can search a
+ * directory.
+ */
+ if ((dp->de_Attributes & ATTR_DIRECTORY) == 0)
+ return ENOTDIR;
+
+ /*
+ * See if the component of the pathname we are looking for is in
+ * the directory cache. If so then do a few things and return.
+ */
+ if (error = cache_lookup(vdp, vpp, cnp)) {
+ int vpid;
+
+ if (error == ENOENT)
+ return error;
+ pdp = vdp;
+ vdp = *vpp;
+ dp = VTODE(vdp);
+ vpid = vdp->v_id;
+ if (pdp == vdp) {
+ VREF(vdp);
+ error = 0;
+ } else if (flags & ISDOTDOT) {
+ VOP_UNLOCK(pdp);
+ error = vget(vdp, 1);
+ if (!error && lockparent && (flags & ISLASTCN))
+ error = VOP_LOCK(pdp);
+ } else {
+ error = vget(vdp, 1);
+ if (!lockparent || error || !(flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ }
+
+ if (!error) {
+ if (vpid == vdp->v_id) {
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): cache hit, vnode %08x, file %s\n",
+ vdp, dp->de_Name);
+#endif
+ return 0;
+ }
+ vput(vdp);
+ if (lockparent && pdp != vdp && (flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ }
+ if (error = VOP_LOCK(pdp))
+ return error;
+ vdp = pdp;
+ dp = VTODE(vdp);
+ *vpp = NULL;
+ }
+
+ /*
+ * If they are going after the . or .. entry in the root directory,
+ * they won't find it. DOS filesystems don't have them in the root
+ * directory. So, we fake it. deget() is in on this scam too.
+ */
+ if ((vdp->v_flag & VROOT) && cnp->cn_nameptr[0] == '.' &&
+ (cnp->cn_namelen == 1 ||
+ (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {
+ isadir = ATTR_DIRECTORY;
+ scn = MSDOSFSROOT;
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): looking for . or .. in root directory\n");
+#endif
+ cluster = MSDOSFSROOT;
+ diroff = MSDOSFSROOT_OFS;
+ goto foundroot;
+ }
+
+ /*
+ * Don't search for free slots unless we are creating a filename
+ * and we are at the end of the pathname.
+ */
+ slotstatus = FOUND;
+ if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) {
+ slotstatus = NONE;
+ slotoffset = -1;
+ }
+
+ unix2dosfn((u_char *) cnp->cn_nameptr, dosfilename, cnp->cn_namelen);
+ dosfilename[11] = 0;
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): dos version of filename %s, length %d\n",
+ dosfilename, cnp->cn_namelen);
+#endif
+ /*
+ * Search the directory pointed at by vdp for the name pointed at
+ * by cnp->cn_nameptr.
+ */
+ tdp = NULL;
+ /*
+ * 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.
+ */
+ rootreloff = 0;
+ for (frcn = 0;; frcn++) {
+ if (error = pcbmap(dp, frcn, &bn, &cluster)) {
+ if (error == E2BIG)
+ break;
+ return error;
+ }
+ if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp))
+ return error;
+ for (diroff = 0; diroff < pmp->pm_depclust; diroff++) {
+ dep = (struct direntry *) bp->b_data + diroff;
+
+ /*
+ * 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) {
+ if (slotstatus != FOUND) {
+ slotstatus = FOUND;
+ if (cluster == MSDOSFSROOT)
+ slotoffset = rootreloff;
+ else
+ slotoffset = diroff;
+ slotcluster = cluster;
+ }
+ if (dep->deName[0] == SLOT_EMPTY) {
+ brelse(bp);
+ goto notfound;
+ }
+ } else {
+ /*
+ * Ignore volume labels (anywhere, not just
+ * the root directory).
+ */
+ if ((dep->deAttributes & ATTR_VOLUME) == 0 &&
+ bcmp(dosfilename, dep->deName, 11) == 0) {
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): match diroff %d, rootreloff %d\n",
+ diroff, rootreloff);
+#endif
+ /*
+ * Remember where this directory
+ * entry came from for whoever did
+ * this lookup. If this is the root
+ * directory we are interested in
+ * the offset relative to the
+ * beginning of the directory (not
+ * the beginning of the cluster).
+ */
+ if (cluster == MSDOSFSROOT)
+ diroff = rootreloff;
+ dp->de_fndoffset = diroff;
+ dp->de_fndclust = cluster;
+ goto found;
+ }
+ }
+ rootreloff++;
+ } /* for (diroff = 0; .... */
+ /*
+ * Release the buffer holding the directory cluster just
+ * searched.
+ */
+ brelse(bp);
+ } /* 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.
+ */
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): op %d, refcnt %d, slotstatus %d\n",
+ nameiop, dp->de_refcnt, slotstatus);
+ printf(" slotoffset %d, slotcluster %d\n",
+ slotoffset, slotcluster);
+#endif
+ if ((nameiop == CREATE || nameiop == RENAME) &&
+ (flags & ISLASTCN) && dp->de_refcnt != 0) {
+ if (slotstatus == NONE) {
+ dp->de_fndoffset = (u_long)-1;
+ dp->de_fndclust = (u_long)-1;
+ } else {
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_lookup(): saving empty slot location\n");
+#endif
+ dp->de_fndoffset = slotoffset;
+ dp->de_fndclust = slotcluster;
+ }
+ /* dp->de_flag |= DE_UPDATE; never update dos directories */
+ cnp->cn_flags |= SAVENAME;
+ if (!lockparent)/* leave searched dir locked? */
+ VOP_UNLOCK(vdp);
+ return EJUSTRETURN;
+ }
+ /*
+ * Insert name in cache as non-existant if not trying to create it.
+ */
+ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
+ cache_enter(vdp, *vpp, cnp);
+ return ENOENT;
+
+found: ;
+ /*
+ * NOTE: We still have the buffer with matched directory entry at
+ * this point.
+ */
+ isadir = dep->deAttributes & ATTR_DIRECTORY;
+ scn = getushort(dep->deStartCluster);
+
+foundroot:;
+ /*
+ * If we entered at foundroot, then we are looking for the . or ..
+ * entry of the filesystems root directory. isadir and scn were
+ * setup before jumping here. And, bp is null. There is no buf
+ * header.
+ */
+
+ /*
+ * If deleting and at the end of the path, then if we matched on
+ * "." then don't deget() we would probably panic(). Otherwise
+ * deget() the directory entry.
+ */
+ if (nameiop == DELETE && (flags & ISLASTCN)) {
+ if (dp->de_StartCluster == scn && isadir) { /* "." */
+ VREF(vdp);
+ *vpp = vdp;
+ if (bp)
+ brelse(bp);
+ return 0;
+ }
+ error = deget(pmp, cluster, diroff, dep, &tdp);
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return error;
+ }
+ *vpp = DETOV(tdp);
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ if (bp)
+ brelse(bp);
+ return 0;
+ }
+
+ /*
+ * If renaming.
+ */
+ if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
+ if (dp->de_StartCluster == scn && isadir) {
+ if (bp)
+ brelse(bp);
+ return EISDIR;
+ }
+ error = deget(pmp, cluster, diroff, dep, &tdp);
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return error;
+ }
+ *vpp = DETOV(tdp);
+ cnp->cn_flags |= SAVENAME;
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ if (bp)
+ brelse(bp);
+ return 0;
+ }
+
+ /*
+ * ?
+ */
+ pdp = vdp;
+ if (flags & ISDOTDOT) {
+ VOP_UNLOCK(pdp);
+ error = deget(pmp, cluster, diroff, dep, &tdp);
+ if (error) {
+ VOP_LOCK(pdp);
+ if (bp)
+ brelse(bp);
+ return error;
+ }
+ if (lockparent && (flags & ISLASTCN)
+ && (error = VOP_LOCK(pdp))) {
+ vput(DETOV(tdp));
+ return error;
+ }
+ *vpp = DETOV(tdp);
+ } else if (dp->de_StartCluster == scn && isadir) { /* "." */
+ VREF(vdp);
+ *vpp = vdp;
+ } else {
+ error = deget(pmp, cluster, diroff, dep, &tdp);
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return error;
+ }
+ if (!lockparent || !(flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ *vpp = DETOV(tdp);
+ }
+ if (bp)
+ brelse(bp);
+
+ /*
+ * Insert name in cache if wanted.
+ */
+ if (cnp->cn_flags & MAKEENTRY)
+ cache_enter(vdp, *vpp, cnp);
+ return 0;
+}
+
+/*
+ * dep - directory entry to copy into the directory
+ * ddep - directory to add to
+ * depp - return the address of the denode for the created directory entry
+ * if depp != 0
+ */
+int
+createde(dep, ddep, depp)
+ struct denode *dep;
+ struct denode *ddep;
+ struct denode **depp;
+{
+ int bn;
+ int error;
+ u_long dirclust, diroffset;
+ struct direntry *ndep;
+ struct msdosfsmount *pmp = ddep->de_pmp;
+ struct buf *bp;
+
+#ifdef MSDOSFS_DEBUG
+ printf("createde(dep %08x, ddep %08x, depp %08x)\n", dep, ddep, depp);
+#endif
+
+ /*
+ * If no space left in the directory then allocate another cluster
+ * and chain it onto the end of the file. There is one exception
+ * to this. That is, if the root directory has no more space it
+ * can NOT be expanded. extendfile() checks for and fails attempts
+ * to extend the root directory. We just return an error in that
+ * case.
+ */
+ if (ddep->de_fndclust == (u_long)-1) {
+ if (error = extendfile(ddep, 1, &bp, &dirclust, DE_CLEAR))
+ return error;
+ ndep = (struct direntry *) bp->b_data;
+ /*
+ * Let caller know where we put the directory entry.
+ */
+ ddep->de_fndclust = dirclust;
+ ddep->de_fndoffset = diroffset = 0;
+ /*
+ * Update the size of the directory
+ */
+ ddep->de_FileSize += pmp->pm_bpcluster;
+ } else {
+ /*
+ * There is space in the existing directory. So, we just
+ * read in the cluster with space. Copy the new directory
+ * entry in. Then write it to disk. NOTE: DOS directories
+ * do not get smaller as clusters are emptied.
+ */
+ dirclust = ddep->de_fndclust;
+ diroffset = ddep->de_fndoffset;
+
+ error = readep(pmp, dirclust, diroffset, &bp, &ndep);
+ if (error)
+ return error;
+ }
+ DE_EXTERNALIZE(ndep, dep);
+
+ /*
+ * If they want us to return with the denode gotten.
+ */
+ if (depp) {
+ error = deget(pmp, dirclust, diroffset, ndep, depp);
+ if (error)
+ return error;
+ }
+ if (error = bwrite(bp)) {
+ vput(DETOV(*depp)); /* free the vnode we got on error */
+ return error;
+ }
+ return 0;
+}
+
+/*
+ * Read in a directory entry and mark it as being deleted.
+ */
+int
+markdeleted(pmp, dirclust, diroffset)
+ struct msdosfsmount *pmp;
+ u_long dirclust;
+ u_long diroffset;
+{
+ int error;
+ struct direntry *ep;
+ struct buf *bp;
+
+ error = readep(pmp, dirclust, diroffset, &bp, &ep);
+ if (error)
+ return error;
+ ep->deName[0] = SLOT_DELETED;
+ return bwrite(bp);
+}
+
+/*
+ * Remove a directory entry. At this point the file represented by the
+ * directory entry to be removed is still full length until no one has it
+ * open. When the file no longer being used msdosfs_inactive() is called
+ * and will truncate the file to 0 length. When the vnode containing the
+ * denode is needed for some other purpose by VFS it will call
+ * msdosfs_reclaim() which will remove the denode from the denode cache.
+ */
+int
+removede(pdep,dep)
+ struct denode *pdep; /* directory where the entry is removed */
+ struct denode *dep; /* file to be removed */
+{
+ struct msdosfsmount *pmp = pdep->de_pmp;
+ int error;
+
+#ifdef MSDOSFS_DEBUG
+ printf("removede(): filename %s\n", dep->de_Name);
+ printf("removede(): dep %08x, ndpcluster %d, ndpoffset %d\n",
+ dep, pdep->de_fndclust, pdep->de_fndoffset);
+#endif
+
+ /*
+ * Read the directory block containing the directory entry we are
+ * to make free. The nameidata structure holds the cluster number
+ * and directory entry index number of the entry to free.
+ */
+ error = markdeleted(pmp, pdep->de_fndclust, pdep->de_fndoffset);
+
+ if (error == 0)
+ dep->de_refcnt--;
+ return error;
+}
+
+/*
+ * Be sure a directory is empty except for "." and "..". Return 1 if empty,
+ * return 0 if not empty or error.
+ */
+int
+dosdirempty(dep)
+ struct denode *dep;
+{
+ int dei;
+ int error;
+ u_long cn;
+ daddr_t bn;
+ struct buf *bp;
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct direntry *dentp;
+
+ /*
+ * Since the filesize field in directory entries for a directory is
+ * zero, we just have to feel our way through the directory until
+ * we hit end of file.
+ */
+ for (cn = 0;; cn++) {
+ error = pcbmap(dep, cn, &bn, 0);
+ if (error == E2BIG)
+ return 1; /* it's empty */
+ error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED,
+ &bp);
+ if (error)
+ return error;
+ dentp = (struct direntry *) bp->b_data;
+ for (dei = 0; dei < pmp->pm_depclust; dei++) {
+ if (dentp->deName[0] != SLOT_DELETED) {
+ /*
+ * In dos directories an entry whose name
+ * starts with SLOT_EMPTY (0) starts the
+ * beginning of the unused part of the
+ * directory, so we can just return that it
+ * is empty.
+ */
+ if (dentp->deName[0] == SLOT_EMPTY) {
+ brelse(bp);
+ return 1;
+ }
+ /*
+ * Any names other than "." and ".." in a
+ * directory mean it is not empty.
+ */
+ if (bcmp(dentp->deName, ". ", 11) &&
+ bcmp(dentp->deName, ".. ", 11)) {
+ brelse(bp);
+#ifdef MSDOSFS_DEBUG
+ printf("dosdirempty(): entry %d found %02x, %02x\n",
+ dei, dentp->deName[0], dentp->deName[1]);
+#endif
+ return 0; /* not empty */
+ }
+ }
+ dentp++;
+ }
+ brelse(bp);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Check to see if the directory described by target is in some
+ * subdirectory of source. This prevents something like the following from
+ * succeeding and leaving a bunch or files and directories orphaned. mv
+ * /a/b/c /a/b/c/d/e/f Where c and f are directories.
+ *
+ * source - the inode for /a/b/c
+ * target - the inode for /a/b/c/d/e/f
+ *
+ * Returns 0 if target is NOT a subdirectory of source.
+ * Otherwise returns a non-zero error number.
+ * The target inode is always unlocked on return.
+ */
+int
+doscheckpath(source, target)
+ struct denode *source;
+ struct denode *target;
+{
+ daddr_t scn;
+ struct denode dummy;
+ struct msdosfsmount *pmp;
+ struct direntry *ep;
+ struct denode *dep;
+ struct buf *bp = NULL;
+ int error = 0;
+
+ dep = target;
+ if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
+ (source->de_Attributes & ATTR_DIRECTORY) == 0) {
+ error = ENOTDIR;
+ goto out;
+ }
+ if (dep->de_StartCluster == source->de_StartCluster) {
+ error = EEXIST;
+ goto out;
+ }
+ if (dep->de_StartCluster == MSDOSFSROOT)
+ goto out;
+ for (;;) {
+ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
+ error = ENOTDIR;
+ goto out;
+ }
+ pmp = dep->de_pmp;
+ scn = dep->de_StartCluster;
+ error = bread(pmp->pm_devvp, cntobn(pmp, scn),
+ pmp->pm_bpcluster, NOCRED, &bp);
+ if (error) {
+ break;
+ }
+ ep = (struct direntry *) bp->b_data + 1;
+ if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
+ bcmp(ep->deName, ".. ", 11) != 0) {
+ error = ENOTDIR;
+ break;
+ }
+ scn = getushort(ep->deStartCluster);
+ if (scn == source->de_StartCluster) {
+ error = EINVAL;
+ break;
+ }
+ if (scn == MSDOSFSROOT)
+ break;
+ vput(DETOV(dep));
+ /* NOTE: deget() clears dep on error */
+ error = deget(pmp, scn, 0, ep, &dep);
+ brelse(bp);
+ bp = NULL;
+ if (error)
+ break;
+ }
+out: ;
+ if (bp)
+ brelse(bp);
+ if (error == ENOTDIR)
+ printf("doscheckpath(): .. not a directory?\n");
+ if (dep != NULL)
+ vput(DETOV(dep));
+ return error;
+}
+
+/*
+ * Read in the disk block containing the directory entry (dirclu, dirofs)
+ * and return the address of the buf header, and the address of the
+ * directory entry within the block.
+ */
+int
+readep(pmp, dirclu, dirofs, bpp, epp)
+ struct msdosfsmount *pmp;
+ u_long dirclu, dirofs;
+ struct buf **bpp;
+ struct direntry **epp;
+{
+ int error;
+ daddr_t bn;
+
+ bn = detobn(pmp, dirclu, dirofs);
+ if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp)) {
+ *bpp = NULL;
+ return error;
+ }
+ if (epp)
+ *epp = bptoep(pmp, *bpp, dirofs);
+ return 0;
+}
+
+
+/*
+ * Read in the disk block containing the directory entry dep came from and
+ * return the address of the buf header, and the address of the directory
+ * entry within the block.
+ */
+int
+readde(dep, bpp, epp)
+ struct denode *dep;
+ struct buf **bpp;
+ struct direntry **epp;
+{
+ return readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
+ bpp, epp);
+}
diff --git a/sys/msdosfs/msdosfs_vfsops.c b/sys/msdosfs/msdosfs_vfsops.c
new file mode 100644
index 0000000..ac9c284
--- /dev/null
+++ b/sys/msdosfs/msdosfs_vfsops.c
@@ -0,0 +1,733 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/malloc.h>
+
+#include <msdosfs/bpb.h>
+#include <msdosfs/bootsect.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/fat.h>
+
+int msdosfsdoforce = 1; /* 1 = force unmount */
+
+/*
+ * mp - path - addr in user space of mount point (ie /usr or whatever)
+ * data - addr in user space of mount params including the name of the block
+ * special file to treat as a filesystem.
+ */
+int
+msdosfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *devvp; /* vnode for blk device to mount */
+ struct msdosfs_args args; /* will hold data from mount request */
+ struct msdosfsmount *pmp; /* msdosfs specific mount control block */
+ int error, flags;
+ u_int size;
+ struct ucred *cred, *scred;
+ struct vattr va;
+
+ /*
+ * Copy in the args for the mount request.
+ */
+ if (error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args)))
+ return error;
+
+ /*
+ * If they just want to update then be sure we can do what is
+ * asked. Can't change a filesystem from read/write to read only.
+ * Why? And if they've supplied a new device file name then we
+ * continue, otherwise return.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ pmp = (struct msdosfsmount *) mp->mnt_data;
+ error = 0;
+ if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
+ flags = WRITECLOSE;
+ if (mp->mnt_flag & MNT_FORCE)
+ flags |= FORCECLOSE;
+ if (vfs_busy(mp))
+ return EBUSY;
+ error = vflush(mp, NULLVP, flags);
+ vfs_unbusy(mp);
+ }
+ if (!error && (mp->mnt_flag & MNT_RELOAD))
+ /* not yet implemented */
+ error = EINVAL;
+ if (error)
+ return error;
+ if (pmp->pm_ronly && (mp->mnt_flag & MNT_RDONLY) == 0)
+ pmp->pm_ronly = 0;
+ if (args.fspec == 0) {
+ /*
+ * Process export requests.
+ */
+ return vfs_export(mp, &pmp->pm_export, &args.export);
+ }
+ } else
+ pmp = NULL;
+
+ /*
+ * check to see that the user in owns the target directory.
+ * Note the very XXX trick to make sure we're checking as the
+ * real user -- were mount() executable by anyone, this wouldn't
+ * be a problem.
+ *
+ * XXX there should be one consistent error out.
+ */
+ cred = crdup(p->p_ucred); /* XXX */
+ cred->cr_uid = p->p_cred->p_ruid; /* XXX */
+ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
+ if (error) {
+ crfree(cred); /* XXX */
+ return error;
+ }
+ if (cred->cr_uid != 0) {
+ if (va.va_uid != cred->cr_uid) {
+ error = EACCES;
+ crfree(cred); /* XXX */
+ return error;
+ }
+
+ /* a user mounted it; we'll verify permissions when unmounting */
+ mp->mnt_flag |= MNT_USER;
+ }
+
+ /*
+ * Now, lookup the name of the block device this mount or name
+ * update request is to apply to.
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+ scred = p->p_ucred; /* XXX */
+ p->p_ucred = cred; /* XXX */
+ error = namei(ndp);
+ p->p_ucred = scred; /* XXX */
+ crfree(cred); /* XXX */
+ if (error != 0)
+ return error;
+
+ /*
+ * Be sure they've given us a block device to treat as a
+ * filesystem. And, that its major number is within the bdevsw
+ * table.
+ */
+ devvp = ndp->ni_vp;
+ if (devvp->v_type != VBLK) {
+ vrele(devvp);
+ return ENOTBLK;
+ }
+ if (major(devvp->v_rdev) >= nblkdev) {
+ vrele(devvp);
+ return ENXIO;
+ }
+
+ /*
+ * If this is an update, then make sure the vnode for the block
+ * special device is the same as the one our filesystem is in.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ if (devvp != pmp->pm_devvp)
+ error = EINVAL;
+ else
+ vrele(devvp);
+ } else {
+
+ /*
+ * Well, it's not an update, it's a real mount request.
+ * Time to get dirty.
+ */
+ error = mountmsdosfs(devvp, mp, p);
+ }
+ if (error) {
+ vrele(devvp);
+ return error;
+ }
+
+ /*
+ * Copy in the name of the directory the filesystem is to be
+ * mounted on. Then copy in the name of the block special file
+ * representing the filesystem being mounted. And we clear the
+ * remainder of the character strings to be tidy. Set up the
+ * user id/group id/mask as specified by the user. Then, we try to
+ * fill in the filesystem stats structure as best we can with
+ * whatever applies from a dos file system.
+ */
+ pmp = (struct msdosfsmount *) mp->mnt_data;
+ copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname,
+ sizeof(mp->mnt_stat.f_mntonname) - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size,
+ sizeof(mp->mnt_stat.f_mntonname) - size);
+ copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntfromname + size,
+ MNAMELEN - size);
+ pmp->pm_mounter = p->p_cred->p_ruid;
+ pmp->pm_gid = args.gid;
+ pmp->pm_uid = args.uid;
+ pmp->pm_mask = args.mask;
+ (void) msdosfs_statfs(mp, &mp->mnt_stat, p);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_mount(): mp %x, pmp %x, inusemap %x\n", mp, pmp, pmp->pm_inusemap);
+#endif
+ return 0;
+}
+
+int
+mountmsdosfs(devvp, mp, p)
+ struct vnode *devvp;
+ struct mount *mp;
+ struct proc *p;
+{
+ int i;
+ int bpc;
+ int bit;
+ int error;
+ int needclose;
+ int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+ dev_t dev = devvp->v_rdev;
+ union bootsector *bsp;
+ struct msdosfsmount *pmp = NULL;
+ struct buf *bp0 = NULL;
+ struct byte_bpb33 *b33;
+ struct byte_bpb50 *b50;
+
+ /*
+ * Multiple mounts of the same block special file aren't allowed.
+ * Make sure no one else has the special file open. And flush any
+ * old buffers from this filesystem. Presumably this prevents us
+ * from running into buffers that are the wrong blocksize.
+ */
+ if (error = vfs_mountedon(devvp))
+ return error;
+ if (vcount(devvp) > 1)
+ return EBUSY;
+ if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0))
+ return error;
+
+ /*
+ * Now open the block special file.
+ */
+ if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p))
+ return error;
+ needclose = 1;
+#ifdef HDSUPPORT
+ /*
+ * Put this in when we support reading dos filesystems from
+ * partitioned harddisks.
+ */
+ if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) {
+ }
+#endif
+
+ /*
+ * Read the boot sector of the filesystem, and then check the boot
+ * signature. If not a dos boot sector then error out. We could
+ * also add some checking on the bsOemName field. So far I've seen
+ * the following values: "IBM 3.3" "MSDOS3.3" "MSDOS5.0"
+ */
+ if (error = bread(devvp, 0, 512, NOCRED, &bp0))
+ goto error_exit;
+ bp0->b_flags |= B_AGE;
+ bsp = (union bootsector *) bp0->b_data;
+ b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB;
+ b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB;
+#ifdef MSDOSFS_CHECKSIG
+ if (bsp->bs50.bsBootSectSig != BOOTSIG) {
+ error = EINVAL;
+ goto error_exit;
+ }
+#endif
+ if ( bsp->bs50.bsJump[0] != 0xe9 &&
+ (bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) {
+ error = EINVAL;
+ goto error_exit;
+ }
+
+ pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK);
+ bzero((caddr_t)pmp, sizeof *pmp);
+ pmp->pm_mountp = mp;
+
+ /*
+ * 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.
+ */
+ pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
+ pmp->pm_SectPerClust = b50->bpbSecPerClust;
+ 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_Media = b50->bpbMedia;
+ pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
+ pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
+ pmp->pm_Heads = getushort(b50->bpbHeads);
+
+ /* XXX - We should probably check more values here */
+ if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust ||
+ !pmp->pm_Heads || pmp->pm_Heads > 255 ||
+ !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) {
+ error = EINVAL;
+ goto error_exit;
+ }
+
+ 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;
+ }
+ pmp->pm_fatblk = pmp->pm_ResSectors;
+ pmp->pm_rootdirblk = pmp->pm_fatblk +
+ (pmp->pm_FATs * pmp->pm_FATsecs);
+ pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry))
+ /
+ pmp->pm_BytesPerSec;/* in sectors */
+ pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
+ pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
+ pmp->pm_SectPerClust;
+ pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
+ pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
+ if (FAT12(pmp))
+ /*
+ * This will usually be a floppy disk. This size makes sure
+ * that one fat entry will not be split across multiple
+ * blocks.
+ */
+ pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
+ else
+ /*
+ * This will usually be a hard disk. Reading or writing one
+ * block should be quite fast.
+ */
+ pmp->pm_fatblocksize = MAXBSIZE;
+ pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
+
+
+ if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0)
+ printf("mountmsdosfs(): root directory is not a multiple of the clustersize in length\n");
+
+ /*
+ * Compute mask and shift value for isolating cluster relative byte
+ * offsets and cluster numbers from a file offset.
+ */
+ bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec;
+ pmp->pm_bpcluster = bpc;
+ pmp->pm_depclust = bpc / sizeof(struct direntry);
+ pmp->pm_crbomask = bpc - 1;
+ if (bpc == 0) {
+ error = EINVAL;
+ goto error_exit;
+ }
+ bit = 1;
+ for (i = 0; i < 32; i++) {
+ if (bit & bpc) {
+ if (bit ^ bpc) {
+ error = EINVAL;
+ goto error_exit;
+ }
+ pmp->pm_cnshift = i;
+ break;
+ }
+ bit <<= 1;
+ }
+
+ pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */
+ pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */
+
+ /*
+ * Release the bootsector buffer.
+ */
+ brelse(bp0);
+ bp0 = NULL;
+
+ /*
+ * Allocate memory for the bitmap of allocated clusters, and then
+ * fill it in.
+ */
+ pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1)
+ / N_INUSEBITS)
+ * sizeof(*pmp->pm_inusemap),
+ M_MSDOSFSFAT, M_WAITOK);
+
+ /*
+ * fillinusemap() needs pm_devvp.
+ */
+ pmp->pm_dev = dev;
+ pmp->pm_devvp = devvp;
+
+ /*
+ * Have the inuse map filled in.
+ */
+ error = fillinusemap(pmp);
+ if (error)
+ goto error_exit;
+
+ /*
+ * If they want fat updates to be synchronous then let them suffer
+ * the performance degradation in exchange for the on disk copy of
+ * the fat being correct just about all the time. I suppose this
+ * would be a good thing to turn on if the kernel is still flakey.
+ */
+ pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS;
+
+ /*
+ * Finish up.
+ */
+ pmp->pm_ronly = ronly;
+ if (ronly == 0)
+ pmp->pm_fmod = 1;
+ mp->mnt_data = (qaddr_t) pmp;
+ mp->mnt_stat.f_fsid.val[0] = (long)dev;
+ mp->mnt_stat.f_fsid.val[1] = MOUNT_MSDOS;
+ mp->mnt_flag |= MNT_LOCAL;
+#ifdef QUOTA
+ /*
+ * 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.
+ */
+#endif
+ devvp->v_specflags |= SI_MOUNTEDON;
+
+ return 0;
+
+error_exit:;
+ if (bp0)
+ brelse(bp0);
+ if (needclose)
+ (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE,
+ NOCRED, p);
+ if (pmp) {
+ if (pmp->pm_inusemap)
+ free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
+ free((caddr_t) pmp, M_MSDOSFSMNT);
+ mp->mnt_data = (qaddr_t) 0;
+ }
+ return error;
+}
+
+int
+msdosfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return 0;
+}
+
+/*
+ * Unmount the filesystem described by mp.
+ */
+int
+msdosfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int flags = 0;
+ int error;
+ struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
+ struct vnode *vp = pmp->pm_devvp;
+
+ /* only the mounter, or superuser can unmount */
+ if ((p->p_cred->p_ruid != pmp->pm_mounter) &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ return error;
+
+ if (mntflags & MNT_FORCE) {
+ if (!msdosfsdoforce)
+ return EINVAL;
+ flags |= FORCECLOSE;
+ }
+#ifdef QUOTA
+#endif
+ if (error = vflush(mp, NULLVP, flags))
+ return error;
+ pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON;
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_umount(): just before calling VOP_CLOSE()\n");
+ printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n",
+ vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt);
+ printf("lastr %d, id %d, mount %08x, op %08x\n",
+ vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op);
+ printf("freef %08x, freeb %08x, mountf %08x, mountb %08x\n",
+ vp->v_freef, vp->v_freeb, vp->v_mountf, vp->v_mountb);
+ printf("cleanblkhd %08x, dirtyblkhd %08x, numoutput %d, type %d\n",
+ vp->v_cleanblkhd, vp->v_dirtyblkhd, vp->v_numoutput, vp->v_type);
+ printf("union %08x, tag %d, data[0] %08x, data[1] %08x\n",
+ vp->v_socket, vp->v_tag, vp->v_data[0], vp->v_data[1]);
+#endif
+ error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE,
+ NOCRED, p);
+ vrele(pmp->pm_devvp);
+ free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
+ free((caddr_t) pmp, M_MSDOSFSMNT);
+ mp->mnt_data = (qaddr_t) 0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ return error;
+}
+
+int
+msdosfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct denode *ndep;
+ struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data);
+ int error;
+
+ error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_root(); mp %08x, pmp %08x, ndep %08x, vp %08x\n",
+ mp, pmp, ndep, DETOV(ndep));
+#endif
+ if (error == 0)
+ *vpp = DETOV(ndep);
+ return error;
+}
+
+int
+msdosfs_quotactl(mp, cmds, uid, arg, p)
+ struct mount *mp;
+ int cmds;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+#ifdef QUOTA
+#else
+ return EOPNOTSUPP;
+#endif
+}
+
+int
+msdosfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
+
+ /*
+ * Fill in the stat block.
+ */
+#ifdef COMPAT_09
+ sbp->f_type = 4;
+#else
+ sbp->f_type = 0;
+#endif
+ sbp->f_bsize = pmp->pm_bpcluster;
+ sbp->f_iosize = pmp->pm_bpcluster;
+ sbp->f_blocks = pmp->pm_nmbrofclusters;
+ sbp->f_bfree = pmp->pm_freeclustercount;
+ sbp->f_bavail = pmp->pm_freeclustercount;
+ sbp->f_files = pmp->pm_RootDirEnts; /* XXX */
+ sbp->f_ffree = 0; /* what to put in here? */
+
+ /*
+ * Copy the mounted on and mounted from names into the passed in
+ * stat block, if it is not the one in the mount structure.
+ */
+ if (sbp != &mp->mnt_stat) {
+ bcopy((caddr_t) mp->mnt_stat.f_mntonname,
+ (caddr_t) & sbp->f_mntonname[0], MNAMELEN);
+ bcopy((caddr_t) mp->mnt_stat.f_mntfromname,
+ (caddr_t) & sbp->f_mntfromname[0], MNAMELEN);
+ }
+#if 0
+ strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN);
+ sbp->f_fstypename[MFSNAMELEN] = '\0';
+#endif
+ return 0;
+}
+
+int
+msdosfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ struct vnode *vp;
+ struct denode *dep;
+ struct msdosfsmount *pmp;
+ int error;
+ int allerror = 0;
+
+ pmp = (struct msdosfsmount *) mp->mnt_data;
+
+ /*
+ * If we ever switch to not updating all of the fats all the time,
+ * this would be the place to update them from the first one.
+ */
+ if (pmp->pm_fmod)
+ if (pmp->pm_ronly)
+ panic("msdosfs_sync: rofs mod");
+ else {
+ /* update fats here */
+ }
+
+ /*
+ * Go thru in memory denodes and write them out along with
+ * unwritten file blocks.
+ */
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first; vp;
+ vp = vp->v_mntvnodes.le_next) {
+ if (vp->v_mount != mp) /* not ours anymore */
+ goto loop;
+ if (VOP_ISLOCKED(vp)) /* file is busy */
+ continue;
+ dep = VTODE(vp);
+ if ((dep->de_flag & DE_UPDATE) == 0 &&
+ vp->v_dirtyblkhd.lh_first == NULL)
+ continue;
+ if (vget(vp, 1)) /* not there anymore? */
+ goto loop;
+ if (error = VOP_FSYNC(vp, cred, waitfor, p))
+ allerror = error;
+ vput(vp); /* done with this one */
+ }
+
+ /*
+ * Flush filesystem control info.
+ */
+ if (error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p))
+ allerror = error;
+ return allerror;
+}
+
+int
+msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
+ struct defid *defhp = (struct defid *) fhp;
+ struct denode *dep;
+ struct netcred *np;
+ int error;
+
+ np = vfs_export_lookup(mp, &pmp->pm_export, nam);
+ if (np == NULL)
+ return EACCES;
+ error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs,
+ NULL, &dep);
+ if (error) {
+ *vpp = NULLVP;
+ return error;
+ }
+ *vpp = DETOV(dep);
+ *exflagsp = np->netc_exflags;
+ *credanonp = &np->netc_anon;
+ return 0;
+}
+
+
+int
+msdosfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ struct denode *dep = VTODE(vp);
+ struct defid *defhp = (struct defid *) fhp;
+
+ defhp->defid_len = sizeof(struct defid);
+ defhp->defid_dirclust = dep->de_dirclust;
+ defhp->defid_dirofs = dep->de_diroffset;
+ /* defhp->defid_gen = ip->i_gen; */
+ return 0;
+}
+
+int
+msdosfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+ return EOPNOTSUPP;
+}
+
+struct vfsops msdosfs_vfsops = {
+ msdosfs_mount,
+ msdosfs_start,
+ msdosfs_unmount,
+ msdosfs_root,
+ msdosfs_quotactl,
+ msdosfs_statfs,
+ msdosfs_sync,
+ msdosfs_vget,
+ msdosfs_fhtovp,
+ msdosfs_vptofh,
+ msdosfs_init
+};
diff --git a/sys/msdosfs/msdosfs_vnops.c b/sys/msdosfs/msdosfs_vnops.c
new file mode 100644
index 0000000..4294034
--- /dev/null
+++ b/sys/msdosfs/msdosfs_vnops.c
@@ -0,0 +1,1933 @@
+/* $Id$ */
+/* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h> /* defines plimit structure in proc struct */
+#include <sys/kernel.h>
+#include <sys/file.h> /* define FWRITE ... */
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
+#include <sys/malloc.h>
+#include <sys/dir.h> /* defines dirent structure */
+
+#include <msdosfs/bpb.h>
+#include <msdosfs/direntry.h>
+#include <msdosfs/denode.h>
+#include <msdosfs/msdosfsmount.h>
+#include <msdosfs/fat.h>
+/*
+ * 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
+ * retreive the correct block from the buffer cache since the hash value is
+ * based upon the vnode address and the desired block number.
+ */
+
+/*
+ * Create a regular file. On entry the directory to contain the file being
+ * created is locked. We must release before we return. We must also free
+ * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
+ * only if the SAVESTART bit in cn_flags is clear on success.
+ */
+int
+msdosfs_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct componentname *cnp = ap->a_cnp;
+ struct denode ndirent;
+ struct denode *dep;
+ struct denode *pdep = VTODE(ap->a_dvp);
+ struct timespec ts;
+ int error;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap);
+#endif
+
+ /*
+ * 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.
+ */
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & SAVENAME) == 0)
+ panic("msdosfs_create: no name");
+#endif
+ bzero(&ndirent, sizeof(ndirent));
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time);
+ unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen);
+ ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? 0 : ATTR_READONLY;
+ ndirent.de_StartCluster = 0;
+ ndirent.de_FileSize = 0;
+ ndirent.de_dev = pdep->de_dev;
+ ndirent.de_devvp = pdep->de_devvp;
+ if ((error = createde(&ndirent, pdep, &dep)) == 0) {
+ *ap->a_vpp = DETOV(dep);
+ if ((cnp->cn_flags & SAVESTART) == 0)
+ free(cnp->cn_pnbuf, M_NAMEI);
+ } else {
+ free(cnp->cn_pnbuf, M_NAMEI);
+ }
+ vput(ap->a_dvp); /* release parent dir */
+ return error;
+}
+
+int
+msdosfs_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int error;
+ struct denode *pdep = VTODE(ap->a_dvp);
+
+ switch (ap->a_vap->va_type) {
+ case VDIR:
+ error = msdosfs_mkdir((struct vop_mkdir_args *)ap);
+ break;
+
+ case VREG:
+ error = msdosfs_create((struct vop_create_args *)ap);
+ break;
+
+ default:
+ error = EINVAL;
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ break;
+ }
+ return error;
+}
+
+int
+msdosfs_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return 0;
+}
+
+int
+msdosfs_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct denode *dep = VTODE(vp);
+ struct timespec ts;
+
+ if (vp->v_usecount > 1 && !(dep->de_flag & DE_LOCKED)) {
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ DE_TIMES(dep, &ts);
+ }
+ return 0;
+}
+
+/*
+ * This routine will go into sys/kern/vfs_subr.c one day!
+ *
+ * Do the usual access checking.
+ * file_node, uid and gid are from the vnode in question,
+ * while acc_mode and cred are from the VOP_ACCESS parameter list.
+ */
+static int
+vaccess(file_mode, uid, gid, acc_mode, cred)
+ mode_t file_mode;
+ uid_t uid;
+ gid_t gid;
+ mode_t acc_mode;
+ struct ucred *cred;
+{
+ mode_t mask;
+ int i;
+ register gid_t *gp;
+
+ /* User id 0 always gets access. */
+ if (cred->cr_uid == 0)
+ return 0;
+
+ mask = 0;
+
+ /* Otherwise, check the owner. */
+ if (cred->cr_uid == uid) {
+ if (acc_mode & VEXEC)
+ mask |= S_IXUSR;
+ if (acc_mode & VREAD)
+ mask |= S_IRUSR;
+ if (acc_mode & VWRITE)
+ mask |= S_IWUSR;
+ return (file_mode & mask) == mask ? 0 : EACCES;
+ }
+
+ /* Otherwise, check the groups. */
+ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
+ if (gid == *gp) {
+ if (acc_mode & VEXEC)
+ mask |= S_IXGRP;
+ if (acc_mode & VREAD)
+ mask |= S_IRGRP;
+ if (acc_mode & VWRITE)
+ mask |= S_IWGRP;
+ return (file_mode & mask) == mask ? 0 : EACCES;
+ }
+
+ /* Otherwise, check everyone else. */
+ if (acc_mode & VEXEC)
+ mask |= S_IXOTH;
+ if (acc_mode & VREAD)
+ mask |= S_IROTH;
+ if (acc_mode & VWRITE)
+ mask |= S_IWOTH;
+ return (file_mode & mask) == mask ? 0 : EACCES;
+}
+
+int
+msdosfs_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ mode_t dosmode;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+
+ dosmode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
+ ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
+ dosmode &= pmp->pm_mask;
+
+ return vaccess(dosmode, pmp->pm_uid, pmp->pm_gid, ap->a_mode, ap->a_cred);
+}
+
+int
+msdosfs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ u_int cn;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct vattr *vap = ap->a_vap;
+ struct timespec ts;
+
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ DE_TIMES(dep, &ts);
+ vap->va_fsid = dep->de_dev;
+ /*
+ * The following computation of the fileid must be the same as that
+ * used in msdosfs_readdir() to compute d_fileno. If not, pwd
+ * doesn't work.
+ */
+ if (dep->de_Attributes & ATTR_DIRECTORY) {
+ if ((cn = dep->de_StartCluster) == MSDOSFSROOT)
+ cn = 1;
+ } else {
+ if ((cn = dep->de_dirclust) == MSDOSFSROOT)
+ cn = 1;
+ cn = (cn << 16) | (dep->de_diroffset & 0xffff);
+ }
+ vap->va_fileid = cn;
+ vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) |
+ ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH));
+ vap->va_mode &= dep->de_pmp->pm_mask;
+ if (dep->de_Attributes & ATTR_DIRECTORY)
+ vap->va_mode |= S_IFDIR;
+ vap->va_nlink = 1;
+ vap->va_gid = dep->de_pmp->pm_gid;
+ vap->va_uid = dep->de_pmp->pm_uid;
+ vap->va_rdev = 0;
+ vap->va_size = dep->de_FileSize;
+ dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime);
+ vap->va_mtime = vap->va_atime;
+#ifndef MSDOSFS_NODIRMOD
+ if (vap->va_mode & S_IFDIR)
+ TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime);
+#endif
+ vap->va_ctime = vap->va_atime;
+ vap->va_flags = dep->de_flag;
+ vap->va_gen = 0;
+ vap->va_blocksize = dep->de_pmp->pm_bpcluster;
+ vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) &
+ ~(dep->de_pmp->pm_crbomask);
+ vap->va_type = ap->a_vp->v_type;
+ return 0;
+}
+
+int
+msdosfs_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = 0;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct vattr *vap = ap->a_vap;
+ struct ucred *cred = ap->a_cred;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n",
+ ap->a_vp, vap, cred, ap->a_p);
+#endif
+ if ((vap->va_type != VNON) ||
+ (vap->va_nlink != VNOVAL) ||
+ (vap->va_fsid != VNOVAL) ||
+ (vap->va_fileid != VNOVAL) ||
+ (vap->va_blocksize != VNOVAL) ||
+ (vap->va_rdev != VNOVAL) ||
+ (vap->va_bytes != VNOVAL) ||
+ (vap->va_gen != VNOVAL) ||
+ (vap->va_uid != VNOVAL) ||
+ (vap->va_gid != VNOVAL)) {
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_setattr(): returning EINVAL\n");
+ printf(" va_type %d, va_nlink %x, va_fsid %x, va_fileid %x\n",
+ vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid);
+ printf(" va_blocksize %x, va_rdev %x, va_bytes %x, va_gen %x\n",
+ vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
+ printf(" va_uid %x, va_gid %x\n",
+ vap->va_uid, vap->va_gid);
+#endif
+ return EINVAL;
+ }
+
+ if (vap->va_size != VNOVAL) {
+ if (ap->a_vp->v_type == VDIR)
+ return EISDIR;
+ if (error = detrunc(dep, vap->va_size, 0, cred, ap->a_p))
+ return error;
+ }
+ if (vap->va_mtime.ts_sec != VNOVAL) {
+ dep->de_flag |= DE_UPDATE;
+ if (error = deupdat(dep, &vap->va_mtime, 1))
+ return error;
+ }
+
+ /*
+ * DOS files only have the ability to have thier writability
+ * attribute set, so we use the owner write bit to set the readonly
+ * attribute.
+ */
+ if (vap->va_mode != (u_short) VNOVAL) {
+ /* We ignore the read and execute bits */
+ if (vap->va_mode & VWRITE)
+ dep->de_Attributes &= ~ATTR_READONLY;
+ else
+ dep->de_Attributes |= ATTR_READONLY;
+ dep->de_flag |= DE_UPDATE;
+ }
+
+ if (vap->va_flags != VNOVAL) {
+ if (error = suser(cred, &ap->a_p->p_acflag))
+ return error;
+ if (cred->cr_uid == 0)
+ dep->de_flag = vap->va_flags;
+ else {
+ dep->de_flag &= 0xffff0000;
+ dep->de_flag |= (vap->va_flags & 0xffff);
+ }
+ dep->de_flag |= DE_UPDATE;
+ }
+ return error;
+}
+
+int
+msdosfs_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = 0;
+ int diff;
+ int isadir;
+ long n;
+ long on;
+ daddr_t bn;
+ daddr_t lbn;
+ daddr_t rablock;
+ int rasize;
+ struct buf *bp;
+ struct vnode *vp = ap->a_vp;
+ struct denode *dep = VTODE(vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct uio *uio = ap->a_uio;
+
+ /*
+ * If they didn't ask for any data, then we are done.
+ */
+ if (uio->uio_resid == 0)
+ return 0;
+ if (uio->uio_offset < 0)
+ return EINVAL;
+
+ isadir = dep->de_Attributes & ATTR_DIRECTORY;
+ do {
+ lbn = uio->uio_offset >> pmp->pm_cnshift;
+ on = uio->uio_offset & pmp->pm_crbomask;
+ n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
+ diff = dep->de_FileSize - uio->uio_offset;
+ if (diff <= 0)
+ return 0;
+ /* convert cluster # to block # if a directory */
+ if (isadir) {
+ error = pcbmap(dep, lbn, &lbn, 0);
+ if (error)
+ return error;
+ }
+ if (diff < n)
+ n = diff;
+ /*
+ * If we are operating on a directory file then be sure to
+ * do i/o with the vnode for the filesystem instead of the
+ * vnode for the directory.
+ */
+ if (isadir) {
+ error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster,
+ NOCRED, &bp);
+ } else {
+ rablock = lbn + 1;
+ if (vp->v_lastr + 1 == lbn &&
+ rablock * pmp->pm_bpcluster < dep->de_FileSize) {
+ rasize = pmp->pm_bpcluster;
+ error = breadn(vp, lbn, pmp->pm_bpcluster,
+ &rablock, &rasize, 1,
+ NOCRED, &bp);
+ } else {
+ error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED,
+ &bp);
+ }
+ vp->v_lastr = lbn;
+ }
+ n = min(n, pmp->pm_bpcluster - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ error = uiomove(bp->b_data + on, (int) n, uio);
+ /*
+ * If we have read everything from this block or have read
+ * to end of file then we are done with this block. Mark
+ * it to say the buffer can be reused if need be.
+ */
+#if 0
+ if (n + on == pmp->pm_bpcluster ||
+ uio->uio_offset == dep->de_FileSize)
+ bp->b_flags |= B_AGE;
+#endif
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return error;
+}
+
+/*
+ * Write data to a file or directory.
+ */
+int
+msdosfs_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int n;
+ int isadir;
+ int croffset;
+ int resid;
+ int osize;
+ int error = 0;
+ u_long count;
+ daddr_t bn, lastcn;
+ struct buf *bp;
+ int ioflag = ap->a_ioflag;
+ struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ struct vnode *vp = ap->a_vp;
+ struct vnode *thisvp;
+ struct denode *dep = VTODE(vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct ucred *cred = ap->a_cred;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n",
+ vp, uio, ioflag, cred);
+ printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n",
+ dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster);
+#endif
+
+ switch (vp->v_type) {
+ case VREG:
+ if (ioflag & IO_APPEND)
+ uio->uio_offset = dep->de_FileSize;
+ isadir = 0;
+ thisvp = vp;
+ break;
+
+ case VDIR:
+ if ((ioflag & IO_SYNC) == 0)
+ panic("msdosfs_write(): non-sync directory update");
+ isadir = 1;
+ thisvp = pmp->pm_devvp;
+ break;
+
+ default:
+ panic("msdosfs_write(): bad file type");
+ break;
+ }
+
+ if (uio->uio_offset < 0)
+ return EINVAL;
+
+ if (uio->uio_resid == 0)
+ return 0;
+
+ /*
+ * If they've exceeded their filesize limit, tell them about it.
+ */
+ if (vp->v_type == VREG && p &&
+ ((uio->uio_offset + uio->uio_resid) >
+ p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) {
+ psignal(p, SIGXFSZ);
+ return EFBIG;
+ }
+
+ /*
+ * If attempting to write beyond the end of the root directory we
+ * stop that here because the root directory can not grow.
+ */
+ if ((dep->de_Attributes & ATTR_DIRECTORY) &&
+ dep->de_StartCluster == MSDOSFSROOT &&
+ (uio->uio_offset + uio->uio_resid) > dep->de_FileSize)
+ return ENOSPC;
+
+ /*
+ * If the offset we are starting the write at is beyond the end of
+ * the file, then they've done a seek. Unix filesystems allow
+ * files with holes in them, DOS doesn't so we must fill the hole
+ * with zeroed blocks.
+ */
+ if (uio->uio_offset > dep->de_FileSize) {
+ error = deextend(dep, uio->uio_offset, cred);
+ if (error)
+ return error;
+ }
+
+ /*
+ * Remember some values in case the write fails.
+ */
+ resid = uio->uio_resid;
+ osize = dep->de_FileSize;
+
+
+ /*
+ * If we write beyond the end of the file, extend it to its ultimate
+ * size ahead of the time to hopefully get a contiguous area.
+ */
+ if (uio->uio_offset + resid > osize) {
+ count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize);
+ if ((error = extendfile(dep, count, NULL, NULL, 0))
+ && (error != ENOSPC || (ioflag & IO_UNIT)))
+ goto errexit;
+ lastcn = dep->de_fc[FC_LASTFC].fc_frcn;
+ } else
+ lastcn = de_clcount(pmp, osize) - 1;
+
+ do {
+ bn = de_blk(pmp, uio->uio_offset);
+ if (isadir) {
+ if (error = pcbmap(dep, bn, &bn, 0))
+ break;
+ } else if (bn > lastcn) {
+ error = ENOSPC;
+ break;
+ }
+
+ if ((uio->uio_offset & pmp->pm_crbomask) == 0
+ && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset)
+ || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) {
+ /*
+ * If either the whole cluster gets written,
+ * or we write the cluster from its start beyond EOF,
+ * then no need to read data from disk.
+ */
+ bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0);
+ clrbuf(bp);
+ /*
+ * Do the bmap now, since pcbmap needs buffers
+ * for the fat table. (see msdosfs_strategy)
+ */
+ if (!isadir) {
+ if (bp->b_blkno == bp->b_lblkno) {
+ if (error = pcbmap(dep, bp->b_lblkno,
+ &bp->b_blkno, 0))
+ bp->b_blkno = -1;
+ }
+ if (bp->b_blkno == -1) {
+ brelse(bp);
+ if (!error)
+ error = EIO; /* XXX */
+ break;
+ }
+ }
+ } else {
+ /*
+ * The block we need to write into exists, so read it in.
+ */
+ if (error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp))
+ break;
+ }
+
+ croffset = uio->uio_offset & pmp->pm_crbomask;
+ n = min(uio->uio_resid, pmp->pm_bpcluster - croffset);
+ if (uio->uio_offset + n > dep->de_FileSize) {
+ dep->de_FileSize = uio->uio_offset + n;
+ vnode_pager_setsize(vp, dep->de_FileSize); /* why? */
+ }
+ (void) vnode_pager_uncache(vp); /* why not? */
+ /*
+ * Should these vnode_pager_* functions be done on dir
+ * files?
+ */
+
+ /*
+ * Copy the data from user space into the buf header.
+ */
+ error = uiomove(bp->b_data + croffset, n, uio);
+
+ /*
+ * If they want this synchronous then write it and wait for
+ * it. Otherwise, if on a cluster boundary write it
+ * asynchronously so we can move on to the next block
+ * without delay. Otherwise do a delayed write because we
+ * may want to write somemore into the block later.
+ */
+ if (ioflag & IO_SYNC)
+ (void) bwrite(bp);
+ else if (n + croffset == pmp->pm_bpcluster) {
+ bp->b_flags |= B_AGE;
+ bawrite(bp);
+ } else
+ bdwrite(bp);
+ dep->de_flag |= DE_UPDATE;
+ } while (error == 0 && uio->uio_resid > 0);
+
+ /*
+ * If the write failed and they want us to, truncate the file back
+ * to the size it was before the write was attempted.
+ */
+errexit:
+ if (error) {
+ if (ioflag & IO_UNIT) {
+ detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL);
+ uio->uio_offset -= resid - uio->uio_resid;
+ uio->uio_resid = resid;
+ } else {
+ detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL);
+ if (uio->uio_resid != resid)
+ error = 0;
+ }
+ } else {
+ error = deupdat(dep, &time, 1);
+ }
+ return error;
+}
+
+int
+msdosfs_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return ENOTTY;
+}
+
+int
+msdosfs_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return 1; /* DOS filesystems never block? */
+}
+
+int
+msdosfs_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ return EINVAL;
+}
+
+/*
+ * Flush the blocks of a file to disk.
+ *
+ * This function is worthless for vnodes that represent directories. Maybe we
+ * could just do a sync if they try an fsync on a directory file.
+ */
+int
+msdosfs_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ int wait = ap->a_waitfor == MNT_WAIT;
+ int error;
+
+#if 0
+ /*
+ * Does this call to vflushbuf() do anything? I can find no code
+ * anywhere that sets v_dirtyblkhd in the vnode, which vflushbuf()
+ * seems to depend upon.
+ */
+ vflushbuf(vp, wait ? B_SYNC : 0);
+#endif
+ return deupdat(VTODE(vp), &time, wait);
+}
+
+/*
+ * Now the whole work of extending a file is done in the write function.
+ * So nothing to do here.
+ */
+int
+msdosfs_seek(ap)
+ struct vop_seek_args /* {
+ struct vnode *a_vp;
+ off_t a_oldoff;
+ off_t a_newoff;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ return 0;
+}
+
+int
+msdosfs_remove(ap)
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct denode *ddep = VTODE(ap->a_dvp);
+
+ error = removede(ddep,dep);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount);
+#endif
+ if (ddep == dep)
+ vrele(ap->a_vp);
+ else
+ vput(ap->a_vp); /* causes msdosfs_inactive() to be called
+ * via vrele() */
+ vput(ap->a_dvp);
+ return error;
+}
+
+/*
+ * DOS filesystems don't know what links are. But since we already called
+ * msdosfs_lookup() with create and lockparent, the parent is locked so we
+ * have to free it before we return the error.
+ */
+int
+msdosfs_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ return VOP_ABORTOP(ap->a_vp, ap->a_cnp);
+}
+
+/*
+ * Renames on files require moving the denode to a new hash queue since the
+ * denode's location is used to compute which hash queue to put the file
+ * in. Unless it is a rename in place. For example "mv a b".
+ *
+ * What follows is the basic algorithm:
+ *
+ * if (file move) {
+ * if (dest file exists) {
+ * remove dest file
+ * }
+ * if (dest and src in same directory) {
+ * rewrite name in existing directory slot
+ * } else {
+ * write new entry in dest directory
+ * update offset and dirclust in denode
+ * move denode to new hash chain
+ * clear old directory entry
+ * }
+ * } else {
+ * directory move
+ * if (dest directory exists) {
+ * if (dest is not empty) {
+ * return ENOTEMPTY
+ * }
+ * remove dest directory
+ * }
+ * if (dest and src in same directory) {
+ * rewrite name in existing entry
+ * } else {
+ * be sure dest is not a child of src directory
+ * write entry in dest directory
+ * update "." and ".." in moved directory
+ * update offset and dirclust in denode
+ * move denode to new hash chain
+ * clear old directory entry for moved directory
+ * }
+ * }
+ *
+ * On entry:
+ * source's parent directory is unlocked
+ * source file or directory is unlocked
+ * destination's parent directory is locked
+ * destination file or directory is locked if it exists
+ *
+ * On exit:
+ * all denodes should be released
+ *
+ * Notes:
+ * I'm not sure how the memory containing the pathnames pointed at by the
+ * componentname structures is freed, there may be some memory bleeding
+ * for each rename done.
+ */
+int
+msdosfs_rename(ap)
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap;
+{
+ u_char toname[11];
+ int error;
+ int newparent = 0;
+ int sourceisadirectory = 0;
+ u_long to_dirclust;
+ u_long to_diroffset;
+ u_long cn;
+ daddr_t bn;
+ struct vnode *tvp = ap->a_tvp;
+ struct denode *fddep; /* from file's parent directory */
+ struct denode *fdep; /* from file or directory */
+ struct denode *tddep; /* to file's parent directory */
+ struct denode *tdep; /* to file or directory */
+ struct msdosfsmount *pmp;
+ struct direntry *dotdotp;
+ struct direntry *ep;
+ struct buf *bp;
+
+ fddep = VTODE(ap->a_fdvp);
+ fdep = VTODE(ap->a_fvp);
+ tddep = VTODE(ap->a_tdvp);
+ tdep = tvp ? VTODE(tvp) : NULL;
+ pmp = fddep->de_pmp;
+
+#ifdef __NetBSD__
+ /* Check for cross-device rename */
+ if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) ||
+ (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) {
+ error = EXDEV;
+ goto bad;
+ }
+#endif
+
+ /*
+ * Convert the filename in tcnp into a dos filename. We copy this
+ * into the denode and directory entry for the destination
+ * file/directory.
+ */
+ unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr,
+ toname, ap->a_tcnp->cn_namelen);
+
+ /*
+ * At this point this is the lock state of the denodes:
+ * fddep referenced
+ * fdep referenced
+ * tddep locked
+ * tdep locked if it exists
+ */
+
+ /*
+ * Be sure we are not renaming ".", "..", or an alias of ".". This
+ * leads to a crippled directory tree. It's pretty tough to do a
+ * "ls" or "pwd" with the "." directory entry missing, and "cd .."
+ * doesn't work if the ".." entry is missing.
+ */
+ if (fdep->de_Attributes & ATTR_DIRECTORY) {
+ if ((ap->a_fcnp->cn_namelen == 1
+ && ap->a_fcnp->cn_nameptr[0] == '.')
+ || fddep == fdep
+ || (ap->a_fcnp->cn_flags & ISDOTDOT)) {
+ VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp);
+ vput(ap->a_tdvp);
+ if (tvp)
+ vput(tvp);
+ VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp);
+ vrele(ap->a_fdvp);
+ vrele(ap->a_fvp);
+ return EINVAL;
+ }
+ sourceisadirectory = 1;
+ }
+
+ /*
+ * If we are renaming a directory, and the directory is being moved
+ * to another directory, then we must be sure the destination
+ * directory is not in the subtree of the source directory. This
+ * could orphan everything under the source directory.
+ * doscheckpath() unlocks the destination's parent directory so we
+ * must look it up again to relock it.
+ */
+ if (fddep->de_StartCluster != tddep->de_StartCluster)
+ newparent = 1;
+ if (sourceisadirectory && newparent) {
+ if (tdep) {
+ vput(ap->a_tvp);
+ tdep = NULL;
+ }
+ /* doscheckpath() vput()'s tddep */
+ error = doscheckpath(fdep, tddep);
+ tddep = NULL;
+ if (error) {
+ goto bad;
+ }
+ if ((ap->a_tcnp->cn_flags & SAVESTART) == 0)
+ panic("msdosfs_rename(): lost to startdir");
+ if (error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp)) {
+ goto bad;
+ }
+ tddep = VTODE(ap->a_tdvp);
+ tdep = tvp ? VTODE(tvp) : NULL;
+ }
+
+ /*
+ * If the destination exists, then be sure its type (file or dir)
+ * matches that of the source. And, if it is a directory make sure
+ * it is empty. Then delete the destination.
+ */
+ if (tdep) {
+ if (tdep->de_Attributes & ATTR_DIRECTORY) {
+ if (!sourceisadirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ if (!dosdirempty(tdep)) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ } else { /* destination is file */
+ if (sourceisadirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+ }
+ to_dirclust = tdep->de_dirclust;
+ to_diroffset = tdep->de_diroffset;
+ if (error = removede(tddep,tdep)) {
+ goto bad;
+ }
+ vput(ap->a_tvp);
+ tdep = NULL;
+
+ /*
+ * Remember where the slot was for createde().
+ */
+ tddep->de_fndclust = to_dirclust;
+ tddep->de_fndoffset = to_diroffset;
+ }
+
+ /*
+ * If the source and destination are in the same directory then
+ * just read in the directory entry, change the name in the
+ * directory entry and write it back to disk.
+ */
+ if (newparent == 0) {
+ /* tddep and fddep point to the same denode here */
+ VOP_LOCK(ap->a_fvp); /* ap->a_fdvp is already locked */
+ if (error = readep(fddep->de_pmp,
+ fdep->de_dirclust,
+ fdep->de_diroffset,
+ &bp, &ep)) {
+ VOP_UNLOCK(ap->a_fvp);
+ goto bad;
+ }
+ bcopy(toname, ep->deName, 11);
+ if (error = bwrite(bp)) {
+ VOP_UNLOCK(ap->a_fvp);
+ goto bad;
+ }
+ bcopy(toname, fdep->de_Name, 11); /* update denode */
+ /*
+ * fdep locked fddep and tddep point to the same denode
+ * which is locked tdep is NULL
+ */
+ } else {
+ u_long dirsize = 0L;
+
+ /*
+ * If the source and destination are in different
+ * directories, then mark the entry in the source directory
+ * as deleted and write a new entry in the destination
+ * directory. Then move the denode to the correct hash
+ * chain for its new location in the filesystem. And, if
+ * we moved a directory, then update its .. entry to point
+ * to the new parent directory. If we moved a directory
+ * will also insure that the directory entry on disk has a
+ * filesize of zero.
+ */
+ VOP_LOCK(ap->a_fvp);
+ bcopy(toname, fdep->de_Name, 11); /* update denode */
+ if (fdep->de_Attributes & ATTR_DIRECTORY) {
+ dirsize = fdep->de_FileSize;
+ fdep->de_FileSize = 0;
+ }
+ error = createde(fdep, tddep, (struct denode **) 0);
+ if (fdep->de_Attributes & ATTR_DIRECTORY) {
+ fdep->de_FileSize = dirsize;
+ }
+ if (error) {
+ /* should put back filename */
+ VOP_UNLOCK(ap->a_fvp);
+ goto bad;
+ }
+ VOP_LOCK(ap->a_fdvp);
+ if (error = readep(fddep->de_pmp,
+ fddep->de_fndclust,
+ fddep->de_fndoffset,
+ &bp, &ep)) {
+ VOP_UNLOCK(ap->a_fvp);
+ VOP_UNLOCK(ap->a_fdvp);
+ goto bad;
+ }
+ ep->deName[0] = SLOT_DELETED;
+ if (error = bwrite(bp)) {
+ VOP_UNLOCK(ap->a_fvp);
+ VOP_UNLOCK(ap->a_fdvp);
+ goto bad;
+ }
+ fdep->de_dirclust = tddep->de_fndclust;
+ fdep->de_diroffset = tddep->de_fndoffset;
+ reinsert(fdep);
+ VOP_UNLOCK(ap->a_fdvp);
+ }
+ /* fdep is still locked here */
+
+ /*
+ * If we moved a directory to a new parent directory, then we must
+ * fixup the ".." entry in the moved directory.
+ */
+ if (sourceisadirectory && newparent) {
+ cn = fdep->de_StartCluster;
+ if (cn == MSDOSFSROOT) {
+ /* this should never happen */
+ panic("msdosfs_rename(): updating .. in root directory?\n");
+ } else {
+ bn = cntobn(pmp, cn);
+ }
+ error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
+ NOCRED, &bp);
+ if (error) {
+ /* should really panic here, fs is corrupt */
+ VOP_UNLOCK(ap->a_fvp);
+ goto bad;
+ }
+ dotdotp = (struct direntry *) bp->b_data + 1;
+ putushort(dotdotp->deStartCluster, tddep->de_StartCluster);
+ error = bwrite(bp);
+ VOP_UNLOCK(ap->a_fvp);
+ if (error) {
+ /* should really panic here, fs is corrupt */
+ goto bad;
+ }
+ } else
+ VOP_UNLOCK(ap->a_fvp);
+bad: ;
+ vrele(DETOV(fdep));
+ vrele(DETOV(fddep));
+ if (tdep)
+ vput(DETOV(tdep));
+ if (tddep)
+ vput(DETOV(tddep));
+ return error;
+}
+
+struct {
+ struct direntry dot;
+ struct direntry dotdot;
+} dosdirtemplate = {
+
+ ". ", " ", /* the . entry */
+ ATTR_DIRECTORY, /* file attribute */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */
+ 210, 4, 210, 4, /* time and date */
+ 0, 0, /* startcluster */
+ 0, 0, 0, 0, /* filesize */
+ ".. ", " ", /* the .. entry */
+ ATTR_DIRECTORY, /* file attribute */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */
+ 210, 4, 210, 4, /* time and date */
+ 0, 0, /* startcluster */
+ 0, 0, 0, 0, /* filesize */
+};
+
+int
+msdosfs_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struvt vnode **a_vpp;
+ struvt componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ int bn;
+ int error;
+ u_long newcluster;
+ struct denode *pdep;
+ struct denode *ndep;
+ struct direntry *denp;
+ struct denode ndirent;
+ struct msdosfsmount *pmp;
+ struct buf *bp;
+ struct timespec ts;
+ u_short dDate, dTime;
+
+ pdep = VTODE(ap->a_dvp);
+
+ /*
+ * 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_fndclust == (u_long)-1) {
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ return ENOSPC;
+ }
+
+ pmp = pdep->de_pmp;
+
+ /*
+ * Allocate a cluster to hold the about to be created directory.
+ */
+ if (error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL)) {
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ return error;
+ }
+
+ /*
+ * 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);
+ /* always succeeds */
+ bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0);
+ bzero(bp->b_data, pmp->pm_bpcluster);
+ bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate);
+ denp = (struct direntry *) bp->b_data;
+ putushort(denp->deStartCluster, newcluster);
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ unix2dostime(&ts, &dDate, &dTime);
+ putushort(denp->deDate, dDate);
+ putushort(denp->deTime, dTime);
+ denp++;
+ putushort(denp->deStartCluster, pdep->de_StartCluster);
+ putushort(denp->deDate, dDate);
+ putushort(denp->deTime, dTime);
+ if (error = bwrite(bp)) {
+ clusterfree(pmp, newcluster, NULL);
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ return error;
+ }
+
+ /*
+ * Now build up a directory entry pointing to the newly allocated
+ * cluster. This will be written to an empty slot in the parent
+ * directory.
+ */
+ ndep = &ndirent;
+ bzero(ndep, sizeof(*ndep));
+ unix2dosfn((u_char *)ap->a_cnp->cn_nameptr,
+ ndep->de_Name, ap->a_cnp->cn_namelen);
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time);
+ ndep->de_StartCluster = newcluster;
+ ndep->de_Attributes = ATTR_DIRECTORY;
+
+ error = createde(ndep, pdep, &ndep);
+ if (error) {
+ clusterfree(pmp, newcluster, NULL);
+ } else {
+ *ap->a_vpp = DETOV(ndep);
+ }
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp);
+#endif
+ vput(ap->a_dvp);
+ return error;
+}
+
+int
+msdosfs_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ struct denode *ddep;
+ struct denode *dep;
+ int error = 0;
+
+ ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */
+ dep = VTODE(ap->a_vp);/* directory to delete */
+
+ /*
+ * Don't let "rmdir ." go thru.
+ */
+ if (ddep == dep) {
+ vrele(ap->a_vp);
+ vput(ap->a_vp);
+ return EINVAL;
+ }
+
+ /*
+ * Be sure the directory being deleted is empty.
+ */
+ if (dosdirempty(dep) == 0) {
+ error = ENOTEMPTY;
+ goto out;
+ }
+
+ /*
+ * Delete the entry from the directory. For dos filesystems this
+ * gets rid of the directory entry on disk, the in memory copy
+ * still exists but the de_refcnt is <= 0. This prevents it from
+ * being found by deget(). When the vput() on dep is done we give
+ * up access and eventually msdosfs_reclaim() will be called which
+ * will remove it from the denode cache.
+ */
+ if (error = removede(ddep,dep))
+ goto out;
+
+ /*
+ * This is where we decrement the link count in the parent
+ * directory. Since dos filesystems don't do this we just purge
+ * the name cache and let go of the parent directory denode.
+ */
+ cache_purge(DETOV(ddep));
+ vput(ap->a_dvp);
+ ap->a_dvp = NULL;
+
+ /*
+ * Truncate the directory that is being deleted.
+ */
+ error = detrunc(dep, (u_long) 0, IO_SYNC);
+ cache_purge(DETOV(dep));
+
+out: ;
+ if (ap->a_dvp)
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ return error;
+}
+
+/*
+ * DOS filesystems don't know what symlinks are.
+ */
+int
+msdosfs_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ struct denode *pdep = VTODE(ap->a_dvp);
+
+ free(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ vput(ap->a_dvp);
+ return EINVAL;
+}
+
+/*
+ * Dummy dirents to simulate the "." and ".." entries of the root directory
+ * in a dos filesystem. Dos doesn't provide these. Note that each entry
+ * must be the same size as a dos directory entry (32 bytes).
+ */
+struct dos_dirent {
+ u_long d_fileno;
+ u_short d_reclen;
+ u_char d_type;
+ u_char d_namlen;
+ u_char d_name[24];
+} rootdots[2] = {
+
+ {
+ 1, /* d_fileno */
+ sizeof(struct direntry), /* d_reclen */
+ DT_DIR, /* d_type */
+ 1, /* d_namlen */
+ "." /* d_name */
+ },
+ {
+ 1, /* d_fileno */
+ sizeof(struct direntry), /* d_reclen */
+ DT_DIR, /* d_type */
+ 2, /* d_namlen */
+ ".." /* d_name */
+ }
+};
+
+int
+msdosfs_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ int *a_eofflag;
+ u_int *a_cookies;
+ int a_ncookies;
+ } */ *ap;
+{
+ int error = 0;
+ int diff;
+ char pushout;
+ long n;
+ long on;
+ long lost;
+ long count;
+ u_long cn;
+ u_long fileno;
+ long bias = 0;
+ daddr_t bn;
+ daddr_t lbn;
+ struct buf *bp;
+ struct denode *dep = VTODE(ap->a_vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct direntry *dentp;
+ struct dirent *prev;
+ struct dirent *crnt;
+ u_char dirbuf[512]; /* holds converted dos directories */
+ int i = 0;
+ struct uio *uio = ap->a_uio;
+ int ncookies = 1;
+ u_int* cookies = NULL;
+
+#ifdef MSDOSFS_DEBUG
+ printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n",
+ ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
+#endif
+
+#if 0
+ if (!ap->a_cookies)
+ ncookies = 1;
+#endif
+
+ /*
+ * msdosfs_readdir() won't operate properly on regular files since
+ * it does i/o only with the the filesystem vnode, and hence can
+ * retrieve the wrong block from the buffer cache for a plain file.
+ * So, fail attempts to readdir() on a plain file.
+ */
+ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
+ return ENOTDIR;
+
+ /*
+ * If the user buffer is smaller than the size of one dos directory
+ * entry or the file offset is not a multiple of the size of a
+ * directory entry, then we fail the read.
+ */
+ count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
+ lost = uio->uio_resid - count;
+ if (count < sizeof(struct direntry) ||
+ (uio->uio_offset & (sizeof(struct direntry) - 1)))
+ return EINVAL;
+ uio->uio_resid = count;
+ uio->uio_iov->iov_len = count;
+
+ /*
+ * If they are reading from the root directory then, we simulate
+ * the . and .. entries since these don't exist in the root
+ * directory. We also set the offset bias to make up for having to
+ * simulate these entries. By this I mean that at file offset 64 we
+ * read the first entry in the root directory that lives on disk.
+ */
+ if (dep->de_StartCluster == MSDOSFSROOT) {
+ /*
+ * printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n",
+ * uio->uio_offset);
+ */
+ bias = 2 * sizeof(struct direntry);
+ if (uio->uio_offset < 2 * sizeof(struct direntry)) {
+ if (uio->uio_offset
+ && uio->uio_offset != sizeof(struct direntry)) {
+ error = EINVAL;
+ goto out;
+ }
+ n = 1;
+ if (!uio->uio_offset) {
+ n = 2;
+ if (cookies) {
+ *cookies++ = sizeof(struct direntry);
+ ncookies--;
+ }
+ }
+ if (cookies) {
+ if (ncookies-- <= 0)
+ n--;
+ else
+ *cookies++ = 2 * sizeof(struct direntry);
+ }
+
+ error = uiomove((char *) rootdots + uio->uio_offset,
+ n * sizeof(struct direntry), uio);
+ }
+ }
+ while (!error && uio->uio_resid > 0 && ncookies > 0) {
+ lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift;
+ on = (uio->uio_offset - bias) & pmp->pm_crbomask;
+ n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid);
+ diff = dep->de_FileSize - (uio->uio_offset - bias);
+ if (diff <= 0)
+ return 0;
+ if (diff < n)
+ n = diff;
+ error = pcbmap(dep, lbn, &bn, &cn);
+ if (error)
+ break;
+ error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp);
+ n = min(n, pmp->pm_bpcluster - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+
+ /*
+ * code to convert from dos directory entries to ufs
+ * directory entries
+ */
+ pushout = 0;
+ dentp = (struct direntry *)(bp->b_data + on);
+ prev = 0;
+ crnt = (struct dirent *) dirbuf;
+ while ((char *) dentp < bp->b_data + on + n) {
+ /*
+ * printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n",
+ * dentp, prev, crnt, dentp->deName[0], dentp->deAttributes);
+ */
+ /*
+ * If we have an empty entry or a slot from a
+ * deleted file, or a volume label entry just
+ * concatenate its space onto the end of the
+ * previous entry or, manufacture an empty entry if
+ * there is no previous entry.
+ */
+ if (dentp->deName[0] == SLOT_EMPTY ||
+ dentp->deName[0] == SLOT_DELETED ||
+ (dentp->deAttributes & ATTR_VOLUME)) {
+ if (prev) {
+ prev->d_reclen += sizeof(struct direntry);
+ if (cookies) {
+ ncookies++;
+ cookies--;
+ }
+ } else {
+ prev = crnt;
+ prev->d_fileno = 0;
+ prev->d_reclen = sizeof(struct direntry);
+ prev->d_type = DT_UNKNOWN;
+ prev->d_namlen = 0;
+ prev->d_name[0] = 0;
+ }
+ } else {
+ /*
+ * this computation of d_fileno must match
+ * the computation of va_fileid in
+ * msdosfs_getattr
+ */
+ if (dentp->deAttributes & ATTR_DIRECTORY) {
+ /* if this is the root directory */
+ fileno = getushort(dentp->deStartCluster);
+ if (fileno == MSDOSFSROOT)
+ fileno = 1;
+ } else {
+ /*
+ * if the file's dirent lives in
+ * root dir
+ */
+ if ((fileno = cn) == MSDOSFSROOT)
+ fileno = 1;
+ fileno = (fileno << 16) |
+ ((dentp - (struct direntry *) bp->b_data) & 0xffff);
+ }
+ crnt->d_fileno = fileno;
+ crnt->d_reclen = sizeof(struct direntry);
+ crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY)
+ ? DT_DIR : DT_REG;
+ crnt->d_namlen = dos2unixfn(dentp->deName,
+ (u_char *)crnt->d_name);
+ /*
+ * printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n",
+ * crnt->d_name, crnt->d_fileno, dentp->deAttributes,
+ * dentp->deStartCluster);
+ */
+ prev = crnt;
+ }
+ dentp++;
+ if (cookies) {
+ *cookies++ = (u_int)((char *)dentp - bp->b_data - on)
+ + uio->uio_offset;
+ ncookies--;
+ }
+
+ crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry));
+ pushout = 1;
+
+ /*
+ * If our intermediate buffer is full then copy its
+ * contents to user space. I would just use the
+ * buffer the buf header points to but, I'm afraid
+ * that when we brelse() it someone else might find
+ * it in the cache and think its contents are
+ * valid. Maybe there is a way to invalidate the
+ * buffer before brelse()'ing it.
+ */
+ if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) {
+ pushout = 0;
+ error = uiomove(dirbuf, sizeof(dirbuf), uio);
+ if (error)
+ break;
+ prev = 0;
+ crnt = (struct dirent *) dirbuf;
+ }
+ if (ncookies <= 0)
+ break;
+ }
+ if (pushout) {
+ pushout = 0;
+ error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf,
+ uio);
+ }
+
+#if 0
+ /*
+ * If we have read everything from this block or have read
+ * to end of file then we are done with this block. Mark
+ * it to say the buffer can be reused if need be.
+ */
+ if (n + on == pmp->pm_bpcluster ||
+ (uio->uio_offset - bias) == dep->de_FileSize)
+ bp->b_flags |= B_AGE;
+#endif /* if 0 */
+ brelse(bp);
+ if (n == 0)
+ break;
+ }
+out: ;
+ uio->uio_resid += lost;
+
+#if 0
+ /*
+ * I don't know why we bother setting this eofflag, getdirentries()
+ * in vfs_syscalls.c doesn't bother to look at it when we return.
+ * (because NFS uses it in nfs_serv.c -- JMP)
+ */
+ if (dep->de_FileSize - uio->uio_offset - bias <= 0)
+ *ap->a_eofflag = 1;
+ else
+ *ap->a_eofflag = 0;
+#endif
+
+ return error;
+}
+
+/*
+ * DOS filesystems don't know what symlinks are.
+ */
+int
+msdosfs_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ return EINVAL;
+}
+
+int
+msdosfs_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return 0;
+}
+
+int
+msdosfs_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct denode *dep = VTODE(ap->a_vp);
+
+ while (dep->de_flag & DE_LOCKED) {
+ dep->de_flag |= DE_WANTED;
+ if (dep->de_lockholder == curproc->p_pid)
+ panic("msdosfs_lock: locking against myself");
+ dep->de_lockwaiter = curproc->p_pid;
+ (void) sleep((caddr_t) dep, PINOD);
+ }
+ dep->de_lockwaiter = 0;
+ dep->de_lockholder = curproc->p_pid;
+ dep->de_flag |= DE_LOCKED;
+ return 0;
+}
+
+int
+msdosfs_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *vp;
+ } */ *ap;
+{
+ struct denode *dep = VTODE(ap->a_vp);
+
+ if (!(dep->de_flag & DE_LOCKED))
+ panic("msdosfs_unlock: denode not locked");
+ dep->de_lockholder = 0;
+ dep->de_flag &= ~DE_LOCKED;
+ if (dep->de_flag & DE_WANTED) {
+ dep->de_flag &= ~DE_WANTED;
+ wakeup((caddr_t) dep);
+ }
+ return 0;
+}
+
+int
+msdosfs_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ return VTODE(ap->a_vp)->de_flag & DE_LOCKED ? 1 : 0;
+}
+
+/*
+ * vp - address of vnode file the file
+ * bn - which cluster we are interested in mapping to a filesystem block number.
+ * vpp - returns the vnode for the block special file holding the filesystem
+ * containing the file of interest
+ * bnp - address of where to return the filesystem relative block number
+ */
+int
+msdosfs_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ struct denode *dep = VTODE(ap->a_vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = dep->de_devvp;
+ if (ap->a_bnp == NULL)
+ return 0;
+ if (ap->a_runp) {
+ /*
+ * Sequential clusters should be counted here.
+ */
+ *ap->a_runp = 0;
+ }
+ return pcbmap(dep, ap->a_bn, ap->a_bnp, 0);
+}
+
+int msdosfs_reallocblks(ap)
+ struct vop_reallocblks_args /* {
+ struct vnode *a_vp;
+ struct cluster_save *a_buflist;
+ } */ *ap;
+{
+ /* Currently no support for clustering */ /* XXX */
+ return ENOSPC;
+}
+
+int
+msdosfs_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ struct denode *dep = VTODE(bp->b_vp);
+ struct msdosfsmount *pmp = dep->de_pmp;
+ struct vnode *vp;
+ int error = 0;
+
+ if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR)
+ panic("msdosfs_strategy: spec");
+ /*
+ * If we don't already know the filesystem relative block number
+ * then get it using pcbmap(). If pcbmap() returns the block
+ * number as -1 then we've got a hole in the file. DOS filesystems
+ * don't allow files with holes, so we shouldn't ever see this.
+ */
+ if (bp->b_blkno == bp->b_lblkno) {
+ if (error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0))
+ bp->b_blkno = -1;
+ if (bp->b_blkno == -1)
+ clrbuf(bp);
+ }
+ if (bp->b_blkno == -1) {
+ biodone(bp);
+ return error;
+ }
+#ifdef DIAGNOSTIC
+#endif
+ /*
+ * Read/write the block from/to the disk that contains the desired
+ * file block.
+ */
+ vp = dep->de_devvp;
+ bp->b_dev = vp->v_rdev;
+ VOCALL(vp->v_op, VOFFSET(vop_strategy), ap);
+ return 0;
+}
+
+int
+msdosfs_print(ap)
+ struct vop_print_args /* {
+ struct vnode *vp;
+ } */ *ap;
+{
+ struct denode *dep = VTODE(ap->a_vp);
+
+ printf("tag VT_MSDOSFS, startcluster %d, dircluster %d, diroffset %d ",
+ dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset);
+ printf(" dev %d, %d, %s\n",
+ major(dep->de_dev), minor(dep->de_dev),
+ dep->de_flag & DE_LOCKED ? "(LOCKED)" : "");
+ if (dep->de_lockholder) {
+ printf(" owner pid %d", dep->de_lockholder);
+ if (dep->de_lockwaiter)
+ printf(" waiting pid %d", dep->de_lockwaiter);
+ printf("\n");
+ }
+ return 0;
+}
+
+int
+msdosfs_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+ return EINVAL; /* we don't do locking yet */
+}
+
+int
+msdosfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = 1;
+ return 0;
+ case _PC_NAME_MAX:
+ *ap->a_retval = 12;
+ return 0;
+ case _PC_PATH_MAX:
+ *ap->a_retval = PATH_MAX; /* 255? */
+ return 0;
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return 0;
+ case _PC_NO_TRUNC:
+ *ap->a_retval = 0;
+ return 0;
+ default:
+ return EINVAL;
+ }
+}
+
+/* Global vfs data structures for msdosfs */
+int (**msdosfs_vnodeop_p)();
+struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, msdosfs_lookup }, /* lookup */
+ { &vop_create_desc, msdosfs_create }, /* create */
+ { &vop_mknod_desc, msdosfs_mknod }, /* mknod */
+ { &vop_open_desc, msdosfs_open }, /* open */
+ { &vop_close_desc, msdosfs_close }, /* close */
+ { &vop_access_desc, msdosfs_access }, /* access */
+ { &vop_getattr_desc, msdosfs_getattr }, /* getattr */
+ { &vop_setattr_desc, msdosfs_setattr }, /* setattr */
+ { &vop_read_desc, msdosfs_read }, /* read */
+ { &vop_write_desc, msdosfs_write }, /* write */
+ { &vop_ioctl_desc, msdosfs_ioctl }, /* ioctl */
+ { &vop_select_desc, msdosfs_select }, /* select */
+ { &vop_mmap_desc, msdosfs_mmap }, /* mmap */
+ { &vop_fsync_desc, msdosfs_fsync }, /* fsync */
+ { &vop_seek_desc, msdosfs_seek }, /* seek */
+ { &vop_remove_desc, msdosfs_remove }, /* remove */
+ { &vop_link_desc, msdosfs_link }, /* link */
+ { &vop_rename_desc, msdosfs_rename }, /* rename */
+ { &vop_mkdir_desc, msdosfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, msdosfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, msdosfs_symlink }, /* symlink */
+ { &vop_readdir_desc, msdosfs_readdir }, /* readdir */
+ { &vop_readlink_desc, msdosfs_readlink }, /* readlink */
+ { &vop_abortop_desc, msdosfs_abortop }, /* abortop */
+ { &vop_inactive_desc, msdosfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, msdosfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, msdosfs_lock }, /* lock */
+ { &vop_unlock_desc, msdosfs_unlock }, /* unlock */
+ { &vop_bmap_desc, msdosfs_bmap }, /* bmap */
+ { &vop_strategy_desc, msdosfs_strategy }, /* strategy */
+ { &vop_print_desc, msdosfs_print }, /* print */
+ { &vop_islocked_desc, msdosfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, msdosfs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, msdosfs_advlock }, /* advlock */
+ { &vop_reallocblks_desc, msdosfs_reallocblks }, /* reallocblks */
+ { &vop_bwrite_desc, vn_bwrite },
+ { (struct vnodeop_desc *)NULL, (int (*)())NULL }
+};
+struct vnodeopv_desc msdosfs_vnodeop_opv_desc =
+ { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries };
diff --git a/sys/msdosfs/msdosfsmount.h b/sys/msdosfs/msdosfsmount.h
new file mode 100644
index 0000000..62853c5
--- /dev/null
+++ b/sys/msdosfs/msdosfsmount.h
@@ -0,0 +1,183 @@
+/* $Id$ */
+/* $NetBSD: msdosfsmount.h,v 1.7 1994/08/21 18:44:17 ws Exp $ */
+
+/*-
+ * Copyright (C) 1994 Wolfgang Solfrank.
+ * Copyright (C) 1994 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
+ */
+
+/*
+ * Layout of the mount control block for a msdos file system.
+ */
+struct msdosfsmount {
+ struct mount *pm_mountp;/* vfs mount struct for this fs */
+ dev_t pm_dev; /* block special device mounted */
+ uid_t pm_mounter; /* uid of the user who mounted the FS */
+ uid_t pm_uid; /* uid to set as owner of the files */
+ gid_t pm_gid; /* gid to set as owner of the files */
+ mode_t pm_mask; /* mask to and with file protection bits */
+ struct vnode *pm_devvp; /* vnode for block device mntd */
+ struct bpb50 pm_bpb; /* BIOS parameter blk for this fs */
+ u_long pm_fatblk; /* block # of first FAT */
+ u_long pm_rootdirblk; /* block # of root directory */
+ u_long pm_rootdirsize; /* size in blocks (not clusters) */
+ u_long pm_firstcluster; /* block number of first cluster */
+ u_long pm_nmbrofclusters; /* # of clusters in filesystem */
+ u_long pm_maxcluster; /* maximum cluster number */
+ u_long pm_freeclustercount; /* number of free clusters */
+ u_long pm_bnshift; /* shift file offset right this amount to get a block number */
+ u_long pm_brbomask; /* and a file offset with this mask to get block rel offset */
+ u_long pm_cnshift; /* shift file offset right this amount to get a cluster number */
+ u_long pm_crbomask; /* and a file offset with this mask to get cluster rel offset */
+ u_long pm_bpcluster; /* bytes per cluster */
+ u_long pm_depclust; /* directory entries per cluster */
+ u_long pm_fmod; /* ~0 if fs is modified, this can rollover to 0 */
+ u_long pm_fatblocksize; /* size of fat blocks in bytes */
+ u_long pm_fatblocksec; /* size of fat blocks in sectors */
+ u_long pm_fatsize; /* size of fat in bytes */
+ u_int *pm_inusemap; /* ptr to bitmap of in-use clusters */
+ char pm_ronly; /* read only if non-zero */
+ char pm_waitonfat; /* wait for writes of the fat to complt, when 0 use bdwrite, else use bwrite */
+ struct netexport pm_export; /* export information */
+};
+
+/* Number of bits in one pm_inusemap item: */
+#define N_INUSEBITS (8 * sizeof(u_int))
+
+/*
+ * How to compute pm_cnshift and pm_crbomask.
+ *
+ * pm_crbomask = (pm_SectPerClust * pm_BytesPerSect) - 1
+ * if (bytesperclust == * 0)
+ * return EBADBLKSZ;
+ * bit = 1;
+ * for (i = 0; i < 32; i++) {
+ * if (bit & bytesperclust) {
+ * if (bit ^ bytesperclust)
+ * return EBADBLKSZ;
+ * pm_cnshift = * i;
+ * break;
+ * }
+ * bit <<= 1;
+ * }
+ */
+
+/*
+ * Shorthand for fields in the bpb contained in the msdosfsmount structure.
+ */
+#define pm_BytesPerSec pm_bpb.bpbBytesPerSec
+#define pm_SectPerClust pm_bpb.bpbSecPerClust
+#define pm_ResSectors pm_bpb.bpbResSectors
+#define pm_FATs pm_bpb.bpbFATs
+#define pm_RootDirEnts pm_bpb.bpbRootDirEnts
+#define pm_Sectors pm_bpb.bpbSectors
+#define pm_Media pm_bpb.bpbMedia
+#define pm_FATsecs pm_bpb.bpbFATsecs
+#define pm_SecPerTrack pm_bpb.bpbSecPerTrack
+#define pm_Heads pm_bpb.bpbHeads
+#define pm_HiddenSects pm_bpb.bpbHiddenSecs
+#define pm_HugeSectors pm_bpb.bpbHugeSectors
+
+/*
+ * Map a cluster number into a filesystem relative block number.
+ */
+#define cntobn(pmp, cn) \
+ ((((cn)-CLUST_FIRST) * (pmp)->pm_SectPerClust) + (pmp)->pm_firstcluster)
+
+/*
+ * Map a filesystem relative block number back into a cluster number.
+ */
+#define bntocn(pmp, bn) \
+ ((((bn) - pmp->pm_firstcluster)/ (pmp)->pm_SectPerClust) + CLUST_FIRST)
+
+/*
+ * Calculate block number for directory entry in root dir, offset dirofs
+ */
+#define roottobn(pmp, dirofs) \
+ (((dirofs) / (pmp)->pm_depclust) * (pmp)->pm_SectPerClust \
+ + (pmp)->pm_rootdirblk)
+
+/*
+ * Calculate block number for directory entry at cluster dirclu, offset
+ * dirofs
+ */
+#define detobn(pmp, dirclu, dirofs) \
+ ((dirclu) == MSDOSFSROOT \
+ ? roottobn((pmp), (dirofs)) \
+ : cntobn((pmp), (dirclu)))
+
+/*
+ * Convert pointer to buffer -> pointer to direntry
+ */
+#define bptoep(pmp, bp, dirofs) \
+ ((struct direntry *)((bp)->b_data) \
+ + (dirofs) % (pmp)->pm_depclust)
+
+
+/*
+ * Convert filesize to block number
+ */
+#define de_blk(pmp, off) \
+ ((off) >> (pmp)->pm_cnshift)
+
+/*
+ * Clusters required to hold size bytes
+ */
+#define de_clcount(pmp, size) \
+ (((size) + (pmp)->pm_bpcluster - 1) >> (pmp)->pm_cnshift)
+
+/*
+ * Prototypes for MSDOSFS virtual filesystem operations
+ */
+int msdosfs_mount __P((struct mount *, char *, caddr_t, struct nameidata *, struct proc *));
+int msdosfs_start __P((struct mount *, int, struct proc *));
+int msdosfs_unmount __P((struct mount *, int, struct proc *));
+int msdosfs_root __P((struct mount *, struct vnode **));
+int msdosfs_quotactl __P((struct mount *, int, uid_t, caddr_t, struct proc *));
+int msdosfs_statfs __P((struct mount *, struct statfs *, struct proc *));
+int msdosfs_sync __P((struct mount *, int, struct ucred *, struct proc *));
+int msdosfs_fhtovp __P((struct mount *, struct fid *, struct mbuf *, struct vnode **, int *, struct ucred **));
+int msdosfs_vptofh __P((struct vnode *, struct fid *));
+int msdosfs_init __P(());
diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h
index 46c7df7..3d190ed 100644
--- a/sys/sys/malloc.h
+++ b/sys/sys/malloc.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)malloc.h 8.3 (Berkeley) 1/12/94
- * $Id$
+ * $Id: malloc.h,v 1.3 1994/08/02 07:53:10 davidg Exp $
*/
#ifndef _SYS_MALLOC_H_
@@ -107,6 +107,9 @@
#define M_MRTABLE 56 /* multicast routing tables */
#define M_ISOFSMNT 57 /* ISOFS mount structure */
#define M_ISOFSNODE 58 /* ISOFS vnode private part */
+#define M_MSDOSFSMNT 59 /* MSDOSFS mount structure */
+#define M_MSDOSFSNODE 60 /* MSDOSFS vnode private part */
+#define M_MSDOSFSFAT 61 /* MSDOSFS file allocation table */
#define M_TEMP 74 /* misc temporary data buffers */
#define M_TTYS 75 /* tty data structures */
#define M_LAST 76 /* Must be last type + 1 */
@@ -171,9 +174,12 @@
"mrt", /* 56 M_MRTABLE */ \
"ISOFS mount", /* 57 M_ISOFSMNT */ \
"ISOFS node", /* 58 M_ISOFSNODE */ \
+ "MSDOSFS mount",/* 59 M_MSDOSFSMNT */ \
+ "MSDOSFS node", /* 60 M_MSDOSFSNODE */ \
+ "MSDOSFS FAT", /* 61 M_MSDOSFSFAR */ \
NULL, NULL, NULL, NULL, NULL, \
NULL, NULL, NULL, NULL, NULL, \
- NULL, NULL, NULL, NULL, NULL, \
+ NULL, NULL, \
"temp", /* 74 M_TEMP */ \
"ttys", /* 75 M_TTYS */ \
}
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index cae5442..1136343 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)mount.h 8.13 (Berkeley) 3/27/94
- * $Id: mount.h,v 1.5 1994/08/22 14:09:51 bde Exp $
+ * $Id: mount.h,v 1.6 1994/09/15 20:24:26 bde Exp $
*/
#ifndef _SYS_MOUNT_H_
@@ -308,6 +308,19 @@ struct mfs_args {
};
#endif /* MFS */
+#ifdef MSDOSFS
+/*
+ * Arguments to mount MSDOS filesystems.
+ */
+struct msdosfs_args {
+ char *fspec; /* blocks special holding the fs to mount */
+ struct export_args export; /* network export information */
+ uid_t uid; /* uid that owns msdosfs files */
+ gid_t gid; /* gid that owns msdosfs files */
+ mode_t mask; /* mask to be applied for msdosfs perms */
+};
+#endif
+
#ifdef CD9660
/*
* Arguments to mount ISO 9660 filesystems.
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index 89c8052..e20b71c 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)vnode.h 8.7 (Berkeley) 2/4/94
- * $Id: vnode.h,v 1.4 1994/08/29 06:09:11 davidg Exp $
+ * $Id: vnode.h,v 1.5 1994/09/15 20:24:29 bde Exp $
*/
#ifndef _SYS_VNODE_H_
@@ -58,7 +58,7 @@ enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD };
enum vtagtype {
VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PC, VT_LFS, VT_LOFS, VT_FDESC,
VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS,
- VT_UNION
+ VT_UNION, VT_MSDOSFS
};
/*
OpenPOWER on IntegriCloud