From: paulp@uts.amdahl.com (Paul Popelka)
Newsgroups: comp.unix.bsd,alt.sources
Subject: [386bsd] mountable DOS filesystem code, Part 1/5
Date: 17 Nov 92 17:25:12 GMT
Followup-To: comp.unix.bsd
Organization: Amdahl Corporation, Sunnyvale CA


# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	/sys/pcfs
#	/sys/pcfs/INSTALL
#	/sys/pcfs/README
#	/sys/pcfs/pcfs.8
#	/sys/pcfs/pcfs.cdiff
#	/sys/pcfs/fat.h
#	/sys/pcfs/direntry.h
#	/sys/pcfs/denode.h
#	/sys/pcfs/bpb.h
#	/sys/pcfs/bootsect.h
#	/sys/pcfs/pcfsmount.h
#	/usr/src/sbin/mount_pcfs
#	/usr/src/sbin/mount_pcfs/mount_pcfs.c
#	/usr/src/sbin/mount_pcfs/Makefile
#
echo c - /sys/pcfs
mkdir /sys/pcfs > /dev/null 2>&1
echo x - /sys/pcfs/INSTALL
sed 's/^X//' >/sys/pcfs/INSTALL << 'END-of-/sys/pcfs/INSTALL'
XThis note describes the installation of the pcfs filesystem into
Xa 386bsd 0.1 system.
X
XFirst unshar the shar files.  They are made with absolute pathnames.
XThey do not overwrite anything that is part of a standard system.
XThey deposit files into the /usr/src/sbin/mount_pcfs and /sys/pcfs
Xdirectories.
X
XIn the /sys/pcfs directory is a context diff file pcfs.cdiff.  It
Xcontains context diffs to the following files:
X	/sys/sys/vnode.h
X	/sys/sys/mount.h
X	/sys/sys/malloc.h
X	/sys/conf/files
X	/sys/kern/vfs_conf.c
X
XApply them with the following command:
X	patch -p <pcfs.cdiff
X
XNext go to the /usr/src/sbin/mount_pcfs directory and build and install
Xthe mount_pcfs command:
X	cd /usr/src/sbin/mount_pcfs
X	make install
X
XTo configure pcfs into your kernel goto to your configuration file
Xin the /sys/i386/conf directory and add the following statement:
X	options PCFS
X
XThen run the config command.
X	config WHATEVER_YOU_CALL_IT
X
XThen goto the your kernel build directory in /sys/compile/WHATEVER_YOU_CALL_IT
Xand do a "make depend" and then do a make.
X	make depend
X	make
X
XThen copy the kernel into the root directory and reboot your system.
X	cp 386bsd /
X
XTo mount a pcfs filesystem:
X	mount -t pcfs /dev/fd0a /mnt
X
XTo unmount a pcfs filesystem:
X	umount /mnt
END-of-/sys/pcfs/INSTALL
echo x - /sys/pcfs/README
sed 's/^X//' >/sys/pcfs/README << 'END-of-/sys/pcfs/README'
XThis is version 0.0 of the code to allow mountable DOS filesystems
Xunder 386bsd 0.1.  It is refered as pcfs from now on.  No, this is
Xnot related to sun's pcfs, although they essentially do the same
Xthing.
X
XThis package has been installed on a 386bsd 0.1 system that has NOT
Xhad the patchkit installed.
X
XUnshar each of the following 4 shar files.  They create files in
X/sys/pcfs and /usr/src/sbin/mount_pcfs.  They do not overwrite any
Xexisting files.  Once you have them unshared read the /sys/pcfs/INSTALL
Xfile to find out how to install it and mount pc filesystems.  Then look
Xat the /sys/pcfs/pcfs.8 file for a list of the filesystem's quirks.
X
XIf you have problems send me some email and I'll see what I can do.
XIf you add things please send me the mods and I will try to incorporate
Xthem for the next release.
X
XHave fun,
XPaul
Xpaulp@sde.uts.amdahl.com
END-of-/sys/pcfs/README
echo x - /sys/pcfs/pcfs.8
sed 's/^X//' >/sys/pcfs/pcfs.8 << 'END-of-/sys/pcfs/pcfs.8'
XPCFS quirks file
X
XPCFS filesystems on floppy disks only are supported in this release.
XAnd, only high density floppy disks are supported.  This is because
Xthe floppy disk driver only supports high density disks.  PCFS
Xfilesystems on hard disks are not supported yet.  This is because
Xadditions to the disk drivers to make them better utilize dos
Xpartition information are required.
X
XCreated files use only the user permissions bits.  And of these
Xonly the write bit is meaningful.  DOS files always have the
Xexecute and read bits on.
X
XPCFS does not turn on or off the DOS archive attribute bit.
X
XThe timestamp on dos files is updated when ever the file is modified.
XThere is no inode time or create time stamp.
X
XThe timestamp placed on a dos file does not have corrections for
Xdaylight savings time included.  It does have the correction for
Xtimezone though.
X
XUnix times before 1980 will have their year set to 1980 in dos file
Xtimestamps.  This is because dos's idea of time starts in 1980.
X
XPCFS filesystems do not support sparse files.  Any attempt to seek
Xpast the end of a file results in the blocks being allocated and
Xcleared.
X
XWhen read() is used to examine pcfs directories you will get dos
Xdirectory contents.  Note that the root directory does not contain
Xa "." or ".." entry.  Only the readdir() system call simulates these
Xentries in the root directory of a dos filesystem.  readdir() returns
Xdirectory entries as described in getdirentries(2).
X
XUsing read() and write() to manipulate the contents of dos directories
Xis unwise on an active dos filesystem since a more up to date copy of
Xtheir contents may reside in data structures in the kernel.  It is
Xprobably safe to examine the filename field of dos directory entries.
XThe filesystem code keeps this up to date at all times.
X
XThe cluster allocation algorithm is very simplistic.  It starts at
Xcluster 2 and searchs until the last cluster of the filesystem and
Xtakes the first available cluster.
X
XThe fsync() system call does not work on file descriptors open on
Xdirectories.  This isn't a terrible thing since very few programs
Xopen directories for writing.
X
XThe pcfs filesystem truncates filenames quietly.  If a filename has
Xmore than 8 characters before the 1st period only the 1st eigth are
Xused.  It only uses the 1st three characters after the period if
Xthey exist.  The filenames "abc" and "abc." are the same to pcfs.
XFilenames that begin with a "." are considered to be dos filenames
Xwith an extension only and so are limited to 3 characters after the
Xleading ".".  For example ".imlost" would be seen as ".iml" by pcfs.
XPCFS folds filenames to upper case before writing them to disk or
Xlooking up filenames, and folds them to lower case when reading them
Xfrom disk for presentation to the user (for example by readdir()).
X
XDirectory entries for the DOS filesystem label are quietly ignored.
X
XThis is probably going to be a problem.  This implementation expects
Xthe length of the root directory to be a multiple of the size of
Xa cluster.  If this is not true a warning message is printed when
Xthe filesystem is mounted.
X
XPCFS supports DOS filesystems with 12 bit or 16 bit FATs.  It supports
Xboth regular and huge filesystems ( > 32 megabytes).  It supports
Xboth version 3.3 and 5.0 BPB's.  Don't know about version 4.x and
Xless than 3.3.  It has not been tested with 16 bit fats or huge
Xfilesystems. This is because the hard disk drivers need to support
Xdos partitions to do these things. 
X
XPCFS does not support symbolic links or hard links.  It does not
Xsupport quotas.  How could it, pcfs files have no owners.  PCFS
Xfiles have a simulated owner and group of 0.  PCFS does not support
Xfile locking.  Though it may in the future.  PCFS filesystems are
Xnot remote mountable, but they will be in the future.
X
XThis is the first release and as such has performance problems.
XReading large files is very slow because the read ahead code in pcfs_read()
Xdoesn't read far enough ahead for filesystems with small blocksizes.
XPerformance and dos hard disk paritions are the next areas to be
Xworked on.  Unless someone else does it.
X
X
XOperational Details
X-------------------
X
XTo mount a pcfs filesystem:
X	mount -t pcfs /dev/fd0a /mnt
X
XTo unmount a pcfs filesystem:
X	umount /mnt
X
XIf you want to be sure the fat is ALWAYS up to date, mount the
Xfilesystem with the synchronous option:
X	mount -t pcfs -o synchronous /dev/fd0a /mnt
XThis reasults in very slow file write performance because it turns
Xoff write behind of fst disk blocks.
X
X
XConfiguring PCFS into your kernel
X---------------------------------
X
XAdd the following statements to your configuration file in /sys/i386/conf/BLOT.
XOr whatever you call your config file.
X
X	options PCFS
X
XThen do a "config BLOT" in /sys/i386/conf.
X
XThen do a "make depend" in /sys/compile/BLOT.
X
XAnd then do a "make" in /sys/compile/BLOT.
X
XCopy the kernel to / and boot your system.
X
XPCFS consumes approximately 24000 bytes of kernel code space and
Xapproximately 4000 bytes of bss.
X
XPCFS has some debug printf's that can be turned on by defining PCFSDEBUG.
XIt produces lots of output.  If you use it be sure to kill syslogd before
Xusing a PCFS filesystem with debug.
END-of-/sys/pcfs/pcfs.8
echo x - /sys/pcfs/pcfs.cdiff
sed 's/^X//' >/sys/pcfs/pcfs.cdiff << 'END-of-/sys/pcfs/pcfs.cdiff'
X*** /sys/sys/vnode.h	Tue Dec 24 14:24:19 1991
X--- /sys/sys/NEWvnode.h	Thu Oct 22 21:44:42 1992
X***************
X*** 53,59 ****
X   * These are for the benefit of external programs only (e.g., pstat)
X   * and should NEVER be inspected inside the kernel.
X   */
X! enum vtagtype	{ VT_NON, VT_UFS, VT_NFS, VT_MFS };
X  
X  /*
X   * This defines the maximum size of the private data area
X--- 53,59 ----
X   * These are for the benefit of external programs only (e.g., pstat)
X   * and should NEVER be inspected inside the kernel.
X   */
X! enum vtagtype	{ VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PCFS };
X  
X  /*
X   * This defines the maximum size of the private data area
X*** /sys/sys/mount.h	Sun May 24 18:36:01 1992
X--- /sys/sys/NEWmount.h	Thu Oct 22 21:45:47 1992
X***************
X*** 76,82 ****
X  #define	MOUNT_UFS	1		/* UNIX "Fast" Filesystem */
X  #define	MOUNT_NFS	2		/* Network Filesystem */
X  #define	MOUNT_MFS	3		/* Memory Filesystem */
X! #define	MOUNT_MSDOS	4		/* MSDOS Filesystem */
X  #define MOUNT_ISOFS	5		/* iso9660 cdrom */
X  #define	MOUNT_MAXTYPE	5
X  
X--- 76,82 ----
X  #define	MOUNT_UFS	1		/* UNIX "Fast" Filesystem */
X  #define	MOUNT_NFS	2		/* Network Filesystem */
X  #define	MOUNT_MFS	3		/* Memory Filesystem */
X! #define	MOUNT_PCFS	4		/* MSDOS Filesystem */
X  #define MOUNT_ISOFS	5		/* iso9660 cdrom */
X  #define	MOUNT_MAXTYPE	5
X  
X***************
X*** 260,265 ****
X--- 260,276 ----
X  #define	NFSMNT_COMPRESS	0x0800	/* Compress nfs rpc xdr */
X  #define	NFSMNT_LOCKBITS	(NFSMNT_SCKLOCK | NFSMNT_WANTSCK)
X  #endif NFS
X+ 
X+ #ifdef PCFS
X+ /*
X+  *  Arguments to mount MSDOS filesystems.
X+  */
X+ struct pcfs_args {
X+ 	char *fspec;		/* blocks special holding the fs to mount */
X+ 	int exflags;		/* mount flags				*/
X+ 	uid_t exroot;		/* mapping for root uid			*/
X+ };
X+ #endif /* PCFS */
X  
X  #ifdef KERNEL
X  /*
X*** /sys/sys/malloc.h	Tue Dec 24 14:24:17 1991
X--- /sys/sys/NEWmalloc.h	Thu Oct 22 21:46:27 1992
X***************
X*** 91,97 ****
X  #define	M_PROC		41	/* Proc structures */
X  #define	M_SUBPROC	42	/* Proc sub-structures */
X  #define	M_TEMP		49	/* misc temporary data buffers */
X! #define	M_LAST		50
X  
X  #define INITKMEMNAMES { \
X  	"free",		/* 0 M_FREE */ \
X--- 91,99 ----
X  #define	M_PROC		41	/* Proc structures */
X  #define	M_SUBPROC	42	/* Proc sub-structures */
X  #define	M_TEMP		49	/* misc temporary data buffers */
X! #define	M_PCFSMNT	50	/* PCFS mount structure */
X! #define	M_PCFSFAT	51	/* PCFS fat table */
X! #define	M_LAST		52
X  
X  #define INITKMEMNAMES { \
X  	"free",		/* 0 M_FREE */ \
X***************
X*** 139,144 ****
X--- 141,148 ----
X  	"subproc",	/* 42 M_PROC */ \
X  	0, 0, 0, 0, 0, 0, \
X  	"temp",		/* 49 M_TEMP */ \
X+ 	"PCFS mount",	/* 50 M_PCFSMNT */ \
X+ 	"PCFS fat",	/* 51 M_PCFSFAT */ \
X  }
X  
X  struct kmemstats {
X*** /sys/conf/files	Mon May 25 12:26:27 1992
X--- /sys/conf/NEWfiles	Thu Oct 22 21:48:05 1992
X***************
X*** 214,216 ****
X--- 214,222 ----
X  ddb/db_variables.c	optional ddb
X  ddb/db_watch.c		optional ddb
X  ddb/db_write_cmd.c	optional ddb
X+ pcfs/pcfs_fat.c		optional pcfs
X+ pcfs/pcfs_conv.c	optional pcfs
X+ pcfs/pcfs_denode.c	optional pcfs
X+ pcfs/pcfs_lookup.c	optional pcfs
X+ pcfs/pcfs_vfsops.c	optional pcfs
X+ pcfs/pcfs_vnops.c	optional pcfs
X*** /sys/kern/vfs_conf.c	Mon May 25 02:43:56 1992
X--- /sys/kern/NEWvfs_conf.c	Thu Oct 22 21:48:58 1992
X***************
X*** 63,68 ****
X--- 63,72 ----
X  extern	struct vfsops mfs_vfsops;
X  #endif
X  
X+ #ifdef PCFS
X+ extern	struct vfsops pcfs_vfsops;
X+ #endif
X+ 
X  #ifdef ISOFS
X  extern	struct vfsops isofs_vfsops;
X  #endif
X***************
X*** 80,86 ****
X  #else
X  	(struct vfsops *)0,
X  #endif
X! 	(struct vfsops *)0,	/* 4 = MOUNT_MSDOS */
X  #ifdef ISOFS
X  	&isofs_vfsops,		/* 5 = MOUNT_ISOFS */
X  #else
X--- 84,94 ----
X  #else
X  	(struct vfsops *)0,
X  #endif
X! #ifdef PCFS
X! 	&pcfs_vfsops,		/* 4 = MOUNT_PCFS */
X! #else
X! 	(struct vfsops *)0,
X! #endif
X  #ifdef ISOFS
X  	&isofs_vfsops,		/* 5 = MOUNT_ISOFS */
X  #else
END-of-/sys/pcfs/pcfs.cdiff
echo x - /sys/pcfs/fat.h
sed 's/^X//' >/sys/pcfs/fat.h << 'END-of-/sys/pcfs/fat.h'
X/*
X *  Some useful cluster numbers.
X */
X#define	PCFSROOT	0	/* cluster 0 means the root dir		*/
X#define	CLUST_FREE	0	/* cluster 0 also means a free cluster	*/
X#define	PCFSFREE	CLUST_FREE
X#define	CLUST_FIRST	2	/* first legal cluster number		*/
X#define	CLUST_RSRVS	0xfff0	/* start of reserved cluster range	*/
X#define	CLUST_RSRVE	0xfff6	/* end of reserved cluster range	*/
X#define	CLUST_BAD	0xfff7	/* a cluster with a defect		*/
X#define	CLUST_EOFS	0xfff8	/* start of eof cluster range		*/
X#define	CLUST_EOFE	0xffff	/* end of eof cluster range		*/
X
X#define	FAT12_MASK	0x0fff	/* mask for 12 bit cluster numbers	*/
X#define	FAT16_MASK	0xffff	/* mask for 16 bit cluster numbers	*/
X
X/*
X *  Return true if filesystem uses 12 bit fats.
X *  Microsoft Programmer's Reference says if the
X *  maximum cluster number in a filesystem is greater
X *  than 4086 then we've got a 16 bit fat filesystem.
X */
X#define	FAT12(pmp)	(pmp->pm_maxcluster <= 4086)
X#define	FAT16(pmp)	(pmp->pm_maxcluster >  4086)
X
X#define	PCFSEOF(cn)	(((cn) & 0xfff8) == 0xfff8)
X
X/*
X *  These are the values for the function argument to
X *  the function fatentry().
X */
X#define	FAT_GET		0x0001		/* get a fat entry		*/
X#define	FAT_SET		0x0002		/* set a fat entry		*/
X#define	FAT_GET_AND_SET	(FAT_GET | FAT_SET)
X
X/*
X *  This union is useful for manipulating entries
X *  in 12 bit fats.
X */
Xunion fattwiddle {
X	unsigned short word;
X	unsigned char  byte[2];
X};
X
X#if defined(KERNEL)
Xint pcbmap __P((struct denode *dep,
X		unsigned long findcn,
X		daddr_t *bnp,
X		unsigned long *cnp));
Xint clusterfree __P((struct pcfsmount *pmp, unsigned long cn));
Xint clusteralloc __P((struct pcfsmount *pmp, unsigned long *retcluster,
X	unsigned long fillwith));
Xint fatentry __P((int function, struct pcfsmount *pmp,
X	unsigned long cluster,
X	unsigned long *oldcontents,
X	unsigned long newcontents));
Xint freeclusterchain __P((struct pcfsmount *pmp, unsigned long startchain));
X#endif /* defined(KERNEL) */
END-of-/sys/pcfs/fat.h
echo x - /sys/pcfs/direntry.h
sed 's/^X//' >/sys/pcfs/direntry.h << 'END-of-/sys/pcfs/direntry.h'
X/*
X *  Structure of a dos directory entry.
X */
Xstruct direntry {
X	unsigned char deName[8];	/* filename, blank filled	*/
X#define	SLOT_EMPTY	0x00		/* slot has never been used	*/
X#define	SLOT_E5		0x05		/* the real value is 0xe5	*/
X#define	SLOT_DELETED	0xe5		/* file in this slot deleted	*/
X	unsigned char deExtension[3];	/* extension, blank filled	*/
X	unsigned char deAttributes;	/* file attributes		*/
X#define	ATTR_NORMAL	0x00		/* normal file			*/
X#define	ATTR_READONLY	0x01		/* file is readonly		*/
X#define	ATTR_HIDDEN	0x02		/* file is hidden		*/
X#define	ATTR_SYSTEM	0x04		/* file is a system file	*/
X#define	ATTR_VOLUME	0x08		/* entry is a volume label	*/
X#define	ATTR_DIRECTORY	0x10		/* entry is a directory name	*/
X#define	ATTR_ARCHIVE	0x20		/* file is new or modified	*/
X	char deReserved[10];		/* reserved			*/
X	unsigned short deTime;		/* create/last update time	*/
X	unsigned short deDate;		/* create/last update date	*/
X	unsigned short deStartCluster;	/* starting cluster of file	*/
X	unsigned long deFileSize;	/* size of file in bytes	*/
X};
X
X/*
X *  This is the format of the contents of the deTime
X *  field in the direntry structure.
X */
Xstruct DOStime {
X	unsigned short
X			dt_2seconds:5,	/* seconds divided by 2		*/
X			dt_minutes:6,	/* minutes			*/
X			dt_hours:5;	/* hours			*/
X};
X
X/*
X *  This is the format of the contents of the deDate
X *  field in the direntry structure.
X */
Xstruct DOSdate {
X	unsigned short
X			dd_day:5,	/* day of month			*/
X			dd_month:4,	/* month			*/
X			dd_year:7;	/* years since 1980		*/
X};
X
Xunion dostime {
X	struct DOStime dts;
X	unsigned short dti;
X};
X
Xunion dosdate {
X	struct DOSdate dds;
X	unsigned short ddi;
X};
X
X/*
X *  The following defines are used to rename fields in
X *  the ufs_specific structure in the nameidata structure
X *  in namei.h
X */
X#define	ni_pcfs		ni_ufs
X#define	pcfs_count	ufs_count
X#define	pcfs_offset	ufs_offset
X#define	pcfs_cluster	ufs_ino
X
X#if defined(KERNEL)
Xvoid unix2dostime __P((struct timeval *tvp,
X			union dosdate *ddp,
X			union dostime *dtp));
Xvoid dos2unixtime __P((union dosdate *ddp,
X			union dostime *dtp,
X			struct timeval *tvp));
Xint  dos2unixfn __P((unsigned char dn[11], unsigned char *un));
Xvoid unix2dosfn __P((unsigned char *un, unsigned char dn[11], int unlen));
X#endif /* defined(KERNEL) */
END-of-/sys/pcfs/direntry.h
echo x - /sys/pcfs/denode.h
sed 's/^X//' >/sys/pcfs/denode.h << 'END-of-/sys/pcfs/denode.h'
X/*
X *  Written by Paul Popelka (paulp@uts.amdahl.com)
X *
X *  You can do anything you want with this software,
X *    just don't say you wrote it,
X *    and don't remove this notice.
X *
X *  This software is provided "as is".
X *
X *  The author supplies this software to be publicly
X *  redistributed on the understanding that the author
X *  is not responsible for the correct functioning of
X *  this software in any circumstances and is not liable
X *  for any damages caused by this software.
X *
X *  October 1992
X */
X/*
X *  This is the pc filesystem specific portion of the
X *  vnode structure.
X *  To describe a file uniquely the de_dirclust, de_diroffset,
X *  and de_de.deStartCluster fields are used.  de_dirclust
X *  contains the cluster number of the directory cluster containing
X *  the entry for a file or directory.  de_diroffset is the
X *  index into the cluster for the entry describing a file
X *  or directory.  de_de.deStartCluster is the number of the
X *  first cluster of the file or directory.  Now to describe the
X *  quirks of the pc filesystem.
X *  - Clusters 0 and 1 are reserved.
X *  - The first allocatable cluster is 2.
X *  - The root directory is of fixed size and all blocks that
X *    make it up are contiguous.
X *  - Cluster 0 refers to the root directory when it is found
X *    in the startcluster field of a directory entry that points
X *    to another directory.
X *  - Cluster 0 implies a 0 length file when found in the start
X *    cluster field of a directory entry that points to a file.
X *  - You can't use the cluster number 0 to derive
X *    the address of the root directory.
X *  - Multiple directory entries can point to a directory.
X *    The entry in the parent directory points to a child
X *    directory.  Any directories in the child directory contain
X *    a ".." entry that points back to the child.  The child
X *    directory itself contains a "." entry that points to
X *    itself.
X *  - The root directory does not contain a "." or ".." entry.
X *  - Directory entries for directories are never changed once
X *    they are created (except when removed).  The size stays
X *    0, and the last modification time is never changed.  This
X *    is because so many directory entries can point to the physical
X *    clusters that make up a directory.  It would lead to an update
X *    nightmare.
X *  - The length field in a directory entry pointing to a directory
X *    contains 0 (always).  The only way to find the end of a directory
X *    is to follow the cluster chain until the "last cluster"
X *    marker is found.
X *  My extensions to make this house of cards work.  These apply
X *  only to the in memory copy of the directory entry.
X *  - A reference count for each denode will be kept since dos doesn't
X *    keep such things.
X */
X
X/*
X *  The fat cache structure.
X *  fc_fsrcn is the filesystem relative cluster number
X *  that corresponds to the file relative cluster number
X *  in this structure (fc_frcn).
X */
Xstruct fatcache {
X	unsigned short fc_frcn;		/* file relative cluster number	*/
X	unsigned short fc_fsrcn;	/* filesystem relative cluster number */
X};
X
X/*
X *  The fat entry cache as it stands helps make extending
X *  files a "quick" operation by avoiding having to scan
X *  the fat to discover the last cluster of the file.
X *  The cache also helps sequential reads by remembering
X *  the last cluster read from the file.  This also prevents
X *  us from having to rescan the fat to find the next cluster
X *  to read.  This cache is probably pretty worthless if a
X *  file is opened by multiple processes.
X */
X#define	FC_SIZE		2	/* number of entries in the cache	*/
X#define	FC_LASTMAP	0	/* entry the last call to pcbmap() resolved to */
X#define	FC_LASTFC	1	/* entry for the last cluster in the file */
X
X#define	FCE_EMPTY	0xffff	/* doesn't represent an actual cluster # */
X
X/*
X *  Set a slot in the fat cache.
X */
X#define	fc_setcache(dep, slot, frcn, fsrcn) \
X	(dep)->de_fc[slot].fc_frcn = frcn; \
X	(dep)->de_fc[slot].fc_fsrcn = fsrcn;
X
X/*
X *  This is the in memory variant of a dos directory
X *  entry.  It is usually contained within a vnode.
X */
Xstruct denode {
X	struct denode *de_chain[2];	/* hash chain ptrs		*/
X	struct vnode *de_vnode;		/* addr of vnode we are part of	*/
X	struct vnode *de_devvp;		/* vnode of blk dev we live on	*/
X	u_long de_flag;			/* flag bits			*/
X	dev_t de_dev;			/* device where direntry lives	*/
X	u_long de_dirclust;		/* cluster of the directory file
X					 *  containing this entry	*/
X	u_long de_diroffset;		/* ordinal of this entry in the
X					 *  directory			*/
X	long de_refcnt;			/* reference count		*/
X	struct pcfsmount *de_pmp;	/* addr of our mount struct	*/
X	struct lockf *de_lockf;		/* byte level lock list		*/
X	long de_spare0;			/* current lock holder		*/
X	long de_spare1;			/* lock wanter			*/
X	struct direntry de_de;		/* the actual directory entry	*/
X	struct fatcache de_fc[FC_SIZE];	/* fat cache			*/
X};
X
X/*
X *  Values for the de_flag field of the denode.
X */
X#define	DELOCKED	0x0001		/* directory entry is locked	*/
X#define	DEWANT		0x0002		/* someone wants this de	*/
X#define	DERENAME	0x0004		/* de is being renamed		*/
X#define	DEUPD		0x0008		/* file has been modified	*/
X#define	DESHLOCK	0x0010		/* file has shared lock		*/
X#define	DEEXLOCK	0x0020		/* file has exclusive lock	*/
X#define	DELWAIT		0x0040		/* someone waiting on file lock	*/
X#define	DEMOD		0x0080		/* denode wants to be written back
X					 *  to disk			*/
X
X/*
X *  Shorthand macros used to reference fields in the direntry
X *  contained in the denode structure.
X */
X#define	de_Name		de_de.deName
X#define	de_Extension	de_de.deExtension
X#define	de_Attributes	de_de.deAttributes
X#define	de_Reserved	de_de.deReserved
X#define	de_Time		de_de.deTime
X#define	de_Date		de_de.deDate
X#define	de_StartCluster	de_de.deStartCluster
X#define	de_FileSize	de_de.deFileSize
X#define	de_forw		de_chain[0]
X#define	de_back		de_chain[1]
X
X#if defined(KERNEL)
X
X#define	VTODE(vp)	((struct denode *)(vp)->v_data)
X#define	DETOV(de)	((de)->de_vnode)
X
X#define	DELOCK(de)	delock(de)
X#define	DEUNLOCK(de)	deunlock(de)
X
X#define	DEUPDAT(dep, t, waitfor) \
X	if (dep->de_flag & DEUPD) \
X		(void) deupdat(dep, t, waitfor);
X
X#define	DETIMES(dep, t) \
X	if (dep->de_flag & DEUPD) { \
X		(dep)->de_flag |= DEMOD; \
X		unix2dostime(t, (union dosdate *)&dep->de_Date, \
X			(union dostime *)&dep->de_Time); \
X		(dep)->de_flag &= ~DEUPD; \
X	}
X
X/*
X * Prototypes for PCFS vnode operations
X */
Xint pcfs_lookup __P((struct vnode *vp, struct nameidata *ndp, struct proc *p));
Xint pcfs_create __P((struct nameidata *ndp, struct vattr *vap, struct proc *p));
Xint pcfs_mknod __P((struct nameidata *ndp, struct vattr *vap, struct ucred *cred,
X	struct proc *p));
Xint pcfs_open __P((struct vnode *vp, int mode, struct ucred *cred,
X	struct proc *p));
Xint pcfs_close __P((struct vnode *vp, int fflag, struct ucred *cred,
X	struct proc *p));
Xint pcfs_access __P((struct vnode *vp, int mode, struct ucred *cred,
X	struct proc *p));
Xint pcfs_getattr __P((struct vnode *vp, struct vattr *vap, struct ucred *cred,
X	struct proc *p));
Xint pcfs_setattr __P((struct vnode *vp, struct vattr *vap, struct ucred *cred,
X	struct proc *p));
Xint pcfs_read __P((struct vnode *vp, struct uio *uio, int ioflag,
X	struct ucred *cred));
Xint pcfs_write __P((struct vnode *vp, struct uio *uio, int ioflag,
X	struct ucred *cred));
Xint pcfs_ioctl __P((struct vnode *vp, int command, caddr_t data, int fflag,
X	struct ucred *cred, struct proc *p));
Xint pcfs_select __P((struct vnode *vp, int which, int fflags, struct ucred *cred,
X	struct proc *p));
Xint pcfs_mmap __P((struct vnode *vp, int fflags, struct ucred *cred,
X	struct proc *p));
Xint pcfs_fsync __P((struct vnode *vp, int fflags, struct ucred *cred,
X	int waitfor, struct proc *p));
Xint pcfs_seek __P((struct vnode *vp, off_t oldoff, off_t newoff,
X	struct ucred *cred));
Xint pcfs_remove __P((struct nameidata *ndp, struct proc *p));
Xint pcfs_link __P((struct vnode *vp, struct nameidata *ndp, struct proc *p));
Xint pcfs_rename __P((struct nameidata *fndp, struct nameidata *tdnp,
X	struct proc *p));
Xint pcfs_mkdir __P((struct nameidata *ndp, struct vattr *vap, struct proc *p));
Xint pcfs_rmdir __P((struct nameidata *ndp, struct proc *p));
Xint pcfs_symlink __P((struct nameidata *ndp, struct vattr *vap, char *target,
X	struct proc *p));
Xint pcfs_readdir __P((struct vnode *vp, struct uio *uio, struct ucred *cred,
X	int *eofflagp));
Xint pcfs_readlink __P((struct vnode *vp, struct uio *uio, struct ucred *cred));
Xint pcfs_abortop __P((struct nameidata *ndp));
Xint pcfs_inactive __P((struct vnode *vp, struct proc *p));
Xint pcfs_reclaim __P((struct vnode *vp));
Xint pcfs_lock __P((struct vnode *vp));
Xint pcfs_unlock __P((struct vnode *vp));
Xint pcfs_bmap __P((struct vnode *vp, daddr_t bn, struct vnode **vpp,
X	daddr_t *bnp));
Xint pcfs_strategy __P((struct buf *bp));
Xint pcfs_print __P((struct vnode *vp));
Xint pcfs_islocked __P((struct vnode *vp));
Xint pcfs_advlock __P((struct vnode *vp, caddr_t id, int op, struct flock *fl,
X	int flags));
X
X/*
X *  Internal service routine prototypes.
X */
Xint deget __P((struct pcfsmount *pmp, int isadir, unsigned long dirclust,
X	unsigned long diroffset, unsigned long startclust,
X	struct buf *bp, struct denode **depp));
X#endif /* defined(KERNEL) */
END-of-/sys/pcfs/denode.h
echo x - /sys/pcfs/bpb.h
sed 's/^X//' >/sys/pcfs/bpb.h << 'END-of-/sys/pcfs/bpb.h'
X/*
X *  BIOS Parameter Block (BPB) for DOS 3.3
X */
Xstruct bpb33 {
X	u_short bpbBytesPerSec;	/* bytes per sector			*/
X	u_char bpbSecPerClust;	/* sectors per cluster			*/
X	u_short bpbResSectors;	/* number of reserved sectors		*/
X	u_char bpbFATs;		/* number of FATs			*/
X	u_short bpbRootDirEnts;	/* number of root directory entries	*/
X	u_short bpbSectors;	/* total number of sectors		*/
X	u_char bpbMedia;	/* media descriptor			*/
X	u_short bpbFATsecs;	/* number of sectors per FAT		*/
X	u_short bpbSecPerTrack;	/* sectors per track			*/
X	u_short bpbHeads;	/* number of heads			*/
X	u_short bpbHiddenSecs;	/* number of hidden sectors		*/
X};
X
X/*
X *  BPB for DOS 5.0
X *  The difference is bpbHiddenSecs is a short for DOS 3.3,
X *  and bpbHugeSectors is not in the 3.3 bpb.
X */
Xstruct bpb50 {
X	u_short bpbBytesPerSec;	/* bytes per sector			*/
X	u_char bpbSecPerClust;	/* sectors per cluster			*/
X	u_short bpbResSectors;	/* number of reserved sectors		*/
X	u_char bpbFATs;		/* number of FATs			*/
X	u_short bpbRootDirEnts;	/* number of root directory entries	*/
X	u_short bpbSectors;	/* total number of sectors		*/
X	u_char bpbMedia;	/* media descriptor			*/
X	u_short bpbFATsecs;	/* number of sectors per FAT		*/
X	u_short bpbSecPerTrack;	/* sectors per track			*/
X	u_short bpbHeads;	/* number of heads			*/
X	u_long bpbHiddenSecs;	/* number of hidden sectors		*/
X	u_long bpbHugeSectors;	/* number of sectrs if bpbSectors == 0	*/
X};
X
X/*
X *  The following structures represent how the bpb's look
X *  on disk.  shorts and longs are just character arrays
X *  of the appropriate length.  This is because the compiler
X *  forces shorts and longs to align on word or halfword
X *  boundaries.
X */
X#define	getushort(x)	*((unsigned short *)(x))
X#define	getulong(x)	*((unsigned long *)(x))
X
X/*
X *  BIOS Parameter Block (BPB) for DOS 3.3
X */
Xstruct byte_bpb33 {
X	char bpbBytesPerSec[2];	/* bytes per sector			*/
X	char bpbSecPerClust;	/* sectors per cluster			*/
X	char bpbResSectors[2];	/* number of reserved sectors		*/
X	char bpbFATs;		/* number of FATs			*/
X	char bpbRootDirEnts[2];	/* number of root directory entries	*/
X	char bpbSectors[2];	/* total number of sectors		*/
X	char bpbMedia;		/* media descriptor			*/
X	char bpbFATsecs[2];	/* number of sectors per FAT		*/
X	char bpbSecPerTrack[2];	/* sectors per track			*/
X	char bpbHeads[2];	/* number of heads			*/
X	char bpbHiddenSecs[2];	/* number of hidden sectors		*/
X};
X
X/*
X *  BPB for DOS 5.0
X *  The difference is bpbHiddenSecs is a short for DOS 3.3,
X *  and bpbHugeSectors is not in the 3.3 bpb.
X */
Xstruct byte_bpb50 {
X	char bpbBytesPerSec[2];	/* bytes per sector			*/
X	char bpbSecPerClust;	/* sectors per cluster			*/
X	char bpbResSectors[2];	/* number of reserved sectors		*/
X	char bpbFATs;		/* number of FATs			*/
X	char bpbRootDirEnts[2];	/* number of root directory entries	*/
X	char bpbSectors[2];	/* total number of sectors		*/
X	char bpbMedia;		/* media descriptor			*/
X	char bpbFATsecs[2];	/* number of sectors per FAT		*/
X	char bpbSecPerTrack[2];	/* sectors per track			*/
X	char bpbHeads[2];	/* number of heads			*/
X	char bpbHiddenSecs[4];	/* number of hidden sectors		*/
X	char bpbHugeSectors[4];	/* number of sectrs if bpbSectors == 0	*/
X};
END-of-/sys/pcfs/bpb.h
echo x - /sys/pcfs/bootsect.h
sed 's/^X//' >/sys/pcfs/bootsect.h << 'END-of-/sys/pcfs/bootsect.h'
X/*
X *  Format of a boot sector.  This is the first sector
X *  on a DOS floppy disk or the fist sector of a partition
X *  on a hard disk.  But, it is not the first sector of
X *  a partitioned hard disk.
X */
Xstruct bootsector33 {
X	char bsJump[3];		/* jump instruction E9xxxx or EBxx90	*/
X	char bsOemName[8];	/* OEM name and version			*/
X	char bsBPB[19];		/* BIOS parameter block			*/
X	char bsDriveNumber;	/* drive number (0x80)			*/
X	char bsBootCode[474];	/* pad so structure is 512 bytes long	*/
X	unsigned short bsBootSectSig;
X#define	BOOTSIG	0xaa55
X};
X
Xstruct bootsector50 {
X	char bsJump[3];		/* jump instruction E9xxxx or EBxx90	*/
X	char bsOemName[8];	/* OEM name and version			*/
X	char bsBPB[25];		/* BIOS parameter block			*/
X	char bsDriveNumber;	/* drive number (0x80)			*/
X	char bsReserved1;	/* reserved				*/
X	char bsBootSignature;	/* extended boot signature (0x29)	*/
X#define	EXBOOTSIG	0x29
X	char bsVolumeID[4];	/* volume ID number			*/
X	char bsVolumeLabel[11];	/* volume label				*/
X	char bsFileSysType[8];	/* file system type (FAT12 or FAT16)	*/
X	char bsBootCode[448];	/* pad so structure is 512 bytes long	*/
X	unsigned short bsBootSectSig;
X#define	BOOTSIG	0xaa55
X};
X
Xunion bootsector {
X	struct bootsector33 bs33;
X	struct bootsector50 bs50;
X};
X
X/*
X *  Shorthand for fields in the bpb.
X */
X#define	bsBytesPerSec	bsBPB.bpbBytesPerSec
X#define	bsSectPerClust	bsBPB.bpbSectPerClust
X#define	bsResSectors	bsBPB.bpbResSectors
X#define	bsFATS		bsBPB.bpbFATS
X#define	bsRootDirEnts	bsBPB.bpbRootDirEnts
X#define	bsSectors	bsBPB.bpbSectors
X#define	bsMedia		bsBPB.bpbMedia
X#define	bsFATsecs	bsBPB.bpbFATsecs
X#define	bsSectPerTrack	bsBPB.bpbSectPerTrack
X#define	bsHeads		bsBPB.bpbHeads
X#define	bsHiddenSecs	bsBPB.bpbHiddenSecs
X#define	bsHugeSectors	bsBPB.bpbHugeSectors
END-of-/sys/pcfs/bootsect.h
echo x - /sys/pcfs/pcfsmount.h
sed 's/^X//' >/sys/pcfs/pcfsmount.h << 'END-of-/sys/pcfs/pcfsmount.h'
X/*
X *  Written by Paul Popelka (paulp@uts.amdahl.com)
X *
X *  You can do anything you want with this software,
X *    just don't say you wrote it,
X *    and don't remove this notice.
X *
X *  This software is provided "as is".
X *
X *  The author supplies this software to be publicly
X *  redistributed on the understanding that the author
X *  is not responsible for the correct functioning of
X *  this software in any circumstances and is not liable
X *  for any damages caused by this software.
X *
X *  October 1992
X */
X
X/*
X *  Layout of the mount control block for a msdos
X *  file system.
X */
Xstruct pcfsmount {
X	struct mount *pm_mountp;	/* vfs mount struct for this fs	*/
X	dev_t pm_dev;			/* block special device mounted	*/
X	struct vnode *pm_devvp;		/* vnode for block device mntd	*/
X	struct bpb50 pm_bpb;		/* BIOS parameter blk for this fs */
X	long pm_fatblk;			/* block # of first FAT		*/
X	long pm_rootdirblk;		/* block # of root directory	*/
X	long pm_rootdirsize;		/* size in blocks (not clusters) */
X	long pm_firstcluster;		/* block number of first cluster */
X	long pm_nmbrofclusters;		/* # of clusters in filesystem	*/
X	long pm_maxcluster;		/* maximum cluster number	*/
X	long pm_freeclustercount;	/* number of free clusters	*/
X	long pm_lookhere;		/* start free cluster search here */
X	long pm_bnshift;		/* shift file offset right this
X					 *  amount to get a block number */
X	long pm_brbomask;		/* and a file offset with this
X					 *  mask to get block rel offset */
X	long pm_cnshift;		/* shift file offset right this
X					 *  amount to get a cluster number */
X	long pm_crbomask;		/* and a file offset with this
X					 *  mask to get cluster rel offset */
X	long pm_bpcluster;		/* bytes per cluster		*/
X	long pm_depclust;		/* directory entries per cluster */
X	long pm_fmod;			/* ~0 if fs is modified, this can
X					 * rollover to 0		*/
X	char pm_ronly;			/* read only if non-zero	*/
X	char pm_waitonfat;		/* wait for writes of the fat to complt,
X					 * when 0 use bdwrite, else use bwrite */
X	unsigned char *pm_inusemap;	/* ptr to bitmap of in-use clusters */
X};
X/*
X *  How to compute pm_cnshift and pm_crbomask.
X *
X *  pm_crbomask = (pm_SectPerClust * pm_BytesPerSect) - 1
X *  if (bytesperclust == 0) return EBADBLKSZ;
X *  bit = 1;
X *  for (i = 0; i < 32; i++) {
X *    if (bit & bytesperclust) {
X *      if (bit ^ bytesperclust) return EBADBLKSZ;
X *      pm_cnshift = i;
X *      break;
X *    }
X *    bit <<= 1;
X * }
X */
X
X/*
X *  Shorthand for fields in the bpb contained in
X *  the pcfsmount structure.
X */
X#define	pm_BytesPerSec	pm_bpb.bpbBytesPerSec
X#define	pm_SectPerClust	pm_bpb.bpbSecPerClust
X#define	pm_ResSectors	pm_bpb.bpbResSectors
X#define	pm_FATs		pm_bpb.bpbFATs
X#define	pm_RootDirEnts	pm_bpb.bpbRootDirEnts
X#define	pm_Sectors	pm_bpb.bpbSectors
X#define	pm_Media	pm_bpb.bpbMedia
X#define	pm_FATsecs	pm_bpb.bpbFATsecs
X#define	pm_SecPerTrack	pm_bpb.bpbSecPerTrack
X#define	pm_Heads	pm_bpb.bpbHeads
X#define	pm_HiddenSects	pm_bpb.bpbHiddenSecs
X#define	pm_HugeSectors	pm_bpb.bpbHugeSectors
X
X/*
X *  Map a cluster number into a filesystem relative
X *  block number.
X */
X#define	cntobn(pmp, cn) \
X	((((cn)-CLUST_FIRST) * (pmp)->pm_SectPerClust) + (pmp)->pm_firstcluster)
X
X/*
X *  Map a filesystem relative block number back into
X *  a cluster number.
X */
X#define	bntocn(pmp, bn) \
X	((((bn) - pmp->pm_firstcluster)/ (pmp)->pm_SectPerClust) + CLUST_FIRST)
X
X/*
X * Prototypes for PCFS virtual filesystem operations
X */
Xint pcfs_mount __P((struct mount *mp, char *path, caddr_t data,
X	struct nameidata *ndp, struct proc *p));
Xint pcfs_start __P((struct mount *mp, int flags, struct proc *p));
Xint pcfs_unmount __P((struct mount *mp, int mntflags, struct proc *p));
Xint pcfs_root __P((struct mount *mp, struct vnode **vpp));
Xint pcfs_quotactl __P((struct mount *mp, int cmds, int uid, /* should be uid_t */
X	caddr_t arg, struct proc *p));
Xint pcfs_statfs __P((struct mount *mp, struct statfs *sbp, struct proc *p));
Xint pcfs_sync __P((struct mount *mp, int waitfor));
Xint pcfs_fhtovp __P((struct mount *mp, struct fid *fhp, struct vnode **vpp));
Xint pcfs_vptofh __P((struct vnode *vp, struct fid *fhp));
Xint pcfs_init __P(());
END-of-/sys/pcfs/pcfsmount.h
echo c - /usr/src/sbin/mount_pcfs
mkdir /usr/src/sbin/mount_pcfs > /dev/null 2>&1
echo x - /usr/src/sbin/mount_pcfs/mount_pcfs.c
sed 's/^X//' >/usr/src/sbin/mount_pcfs/mount_pcfs.c << 'END-of-/usr/src/sbin/mount_pcfs/mount_pcfs.c'
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/mount.h>
X
Xchar *progname;
X
Xvoid
Xusage ()
X{
X	fprintf (stderr, "usage: %s bdev dir\n", progname);
X	exit (1);
X}
X		
Xint
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X	char *dev;
X	char *dir;
X	struct pcfs_args args;
X	int c;
X	extern char *optarg;
X	extern int optind;
X	int opts;
X
X	progname = argv[0];
X
X	opts = 0;
X
X	while ((c = getopt (argc, argv, "F:")) != EOF) {
X		switch (c) {
X		case 'F':
X			opts |= atoi (optarg);
X			break;
X		default:
X			usage ();
X		}
X	}
X
X	if (optind + 2 != argc)
X		usage ();
X
X	dev = argv[optind];
X	dir = argv[optind + 1];
X
X	args.fspec = dev;
X	args.exflags = 0;
X	args.exroot = 0;
X
X	if (mount (MOUNT_PCFS, dir, opts, &args) < 0) {
X		perror ("mount");
X		exit (1);
X	}
X
X	exit (0);
X}
END-of-/usr/src/sbin/mount_pcfs/mount_pcfs.c
echo x - /usr/src/sbin/mount_pcfs/Makefile
sed 's/^X//' >/usr/src/sbin/mount_pcfs/Makefile << 'END-of-/usr/src/sbin/mount_pcfs/Makefile'
XPROG = mount_pcfs
XCFLAGS += -DPCFS
XNOMAN = noman
X
X.include <bsd.prog.mk>
END-of-/usr/src/sbin/mount_pcfs/Makefile
exit