*BSD News Article 7857


Return to BSD News archive

Xref: sserve comp.unix.bsd:7908 alt.sources:4519
Path: sserve!manuel.anu.edu.au!munnari.oz.au!spool.mu.edu!yale.edu!qt.cs.utexas.edu!cs.utexas.edu!sun-barr!news2me.EBay.Sun.COM!exodus.Eng.Sun.COM!sun!amdahl!paulp
From: paulp@uts.amdahl.com (Paul Popelka)
Newsgroups: comp.unix.bsd,alt.sources
Subject: [386bsd] mountable DOS filesystem code, Part 4/5
Message-ID: <bdRJ03eIbb0c00@amdahl.uts.amdahl.com>
Date: 17 Nov 92 17:28:05 GMT
Organization: Amdahl Corporation, Sunnyvale CA
Lines: 1647


# 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/pcfs_vfsops.c
#	/sys/pcfs/pcfs_denode.c
#	/sys/pcfs/pcfs_conv.c
#
echo x - /sys/pcfs/pcfs_vfsops.c
sed 's/^X//' >/sys/pcfs/pcfs_vfsops.c << 'END-of-/sys/pcfs/pcfs_vfsops.c'
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#include "param.h"
X#include "systm.h"
X#include "namei.h"
X#include "proc.h"
X#include "kernel.h"
X#include "vnode.h"
X#include "specdev.h"	/* defines v_rdev	*/
X#include "mount.h"
X#include "buf.h"
X#include "file.h"
X#include "malloc.h"
X
X#include "bpb.h"
X#include "bootsect.h"
X#include "direntry.h"
X#include "denode.h"
X#include "pcfsmount.h"
X#include "fat.h"
X
Xint pcfsdoforce = 0;	/* 1 = force unmount */
X
X/*
X *  mp -
X *  path - addr in user space of mount point (ie /usr or whatever)
X *  data - addr in user space of mount params including the
X *         name of the block special file to treat as a filesystem.
X *  ndp  - 
X *  p    -
X */
Xint
Xpcfs_mount(mp, path, data, ndp, p)
X	struct mount *mp;
X	char *path;
X	caddr_t data;
X	struct nameidata *ndp;
X	struct proc *p;
X{
X	struct vnode *devvp;	/* vnode for blk device to mount	*/
X	struct pcfs_args args;	/* will hold data from mount request	*/
X	struct pcfsmount *pmp;	/* pcfs specific mount control block	*/
X	int error;
X	u_int size;
X
X/*
X *  Copy in the args for the mount request.
X */
X	if (error = copyin(data, (caddr_t)&args, sizeof(struct pcfs_args)))
X		return error;
X
X/*
X *  Check to see if they want it to be an exportable
X *  filesystem via nfs.  And, if they do, should it
X *  be read only, and what uid is root to be mapped
X *  to.
X */
X	if ((args.exflags & MNT_EXPORTED)  ||  (mp->mnt_flag & MNT_EXPORTED)) {
X		if (args.exflags & MNT_EXPORTED)
X			mp->mnt_flag |= MNT_EXPORTED;
X		else
X			mp->mnt_flag &= ~MNT_EXPORTED;
X		if (args.exflags & MNT_EXRDONLY)
X			mp->mnt_flag |= MNT_EXRDONLY;
X		else
X			mp->mnt_flag &= ~MNT_EXRDONLY;
X		mp->mnt_exroot = args.exroot;
X	}
X
X/*
X *  If they just want to update then be sure we can
X *  do what is asked.  Can't change a filesystem from
X *  read/write to read only.  Why?
X *  And if they've supplied a new filesystem then we
X *  continue, otherwise return.
X */
X	if (mp->mnt_flag & MNT_UPDATE) {
X		pmp = (struct pcfsmount *)mp->mnt_data;
X		if (pmp->pm_ronly  &&  (mp->mnt_flag & MNT_RDONLY) == 0)
X			pmp->pm_ronly = 0;
X		if (args.fspec == 0)
X			return 0;
X	}
X
X/*
X *  Now, lookup the name of the block device this
X *  mount or name update request is to apply to.
X */
X	ndp->ni_nameiop = LOOKUP | FOLLOW;
X	ndp->ni_segflg  = UIO_USERSPACE;
X	ndp->ni_dirp    = args.fspec;
X	if (error = namei(ndp, p))
X		return error;
X
X/*
X *  Be sure they've given us a block device to treat
X *  as a filesystem.  And, that its major number is
X *  within the bdevsw table.
X */
X	devvp = ndp->ni_vp;
X	if (devvp->v_type != VBLK) {
X		vrele(devvp);		/* namei() acquires this?	*/
X		return ENOTBLK;
X	}
X	if (major(devvp->v_rdev) >= nblkdev) {
X		vrele(devvp);
X		return ENXIO;
X	}
X
X/*
X *  If this is an update, then make sure the vnode
X *  for the block special device is the same as the
X *  one our filesystem is in.
X */
X	if (mp->mnt_flag & MNT_UPDATE) {
X		if (devvp != pmp->pm_devvp)
X			error = EINVAL;
X		else
X			vrele(devvp);
X	} else {
X
X/*
X *  Well, it's not an update, it's a real mount request.
X *  Time to get dirty.
X */
X		error = mountpcfs(devvp, mp, p);
X	}
X	if (error) {
X		vrele(devvp);
X		return error;
X	}
X
X/*
X *  Copy in the name of the directory the filesystem
X *  is to be mounted on.
X *  Then copy in the name of the block special file
X *  representing the filesystem being mounted.
X *  And we clear the remainder of the character strings
X *  to be tidy.
X *  Then, we try to fill in the filesystem stats structure
X *  as best we can with whatever applies from a dos file
X *  system.
X */
X	pmp = (struct pcfsmount *)mp->mnt_data;
X	copyinstr(path, (caddr_t)mp->mnt_stat.f_mntonname,
X		sizeof(mp->mnt_stat.f_mntonname)-1, &size);
X	bzero(mp->mnt_stat.f_mntonname + size,
X		sizeof(mp->mnt_stat.f_mntonname) - size);
X	copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN-1, &size);
X	bzero(mp->mnt_stat.f_mntfromname + size,
X		MNAMELEN - size);
X	(void)pcfs_statfs(mp, &mp->mnt_stat, p);
X#if defined(PCFSDEBUG)
Xprintf("pcfs_mount(): mp %x, pmp %x, inusemap %x\n", mp, pmp, pmp->pm_inusemap);
X#endif /* defined(PCFSDEBUG) */
X	return 0;
X}
X
Xint
Xmountpcfs(devvp, mp, p)
X	struct vnode *devvp;
X	struct mount *mp;
X	struct proc *p;
X{
X	int i;
X	int bpc;
X	int bit;
X	int error = 0;
X	int needclose;
X	int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
X	dev_t dev = devvp->v_rdev;
X	union bootsector *bsp;
X	struct pcfsmount *pmp;
X	struct buf *bp0 = 0;
X	struct byte_bpb33 *b33;
X	struct byte_bpb50 *b50;
X
X/*
X *  Multiple mounts of the same block special file
X *  aren't allowed.  Make sure no one else has the
X *  special file open.  And flush any old buffers
X *  from this filesystem.  Presumably this prevents
X *  us from running into buffers that are the wrong
X *  blocksize.
X *  NOTE: mountedon() is a part of the ufs filesystem.
X *  If the ufs filesystem is not gen'ed into the system
X *  we will get an unresolved reference.
X */
X	if (error = mountedon(devvp)) {
X		return error;
X	}
X	if (vcount(devvp) > 1)
X		return EBUSY;
X	vinvalbuf(devvp, 1);
X
X/*
X *  Now open the block special file.
X *  I wonder if you need this for the VOP_IOCTL() or
X *  for the bread() later.  Or, maybe both.
X */
X	if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p)) {
X		return error;
X	}
X	needclose = 1;
X#if defined(HDSUPPORT)
X/*
X *  Put this in when we support reading dos filesystems
X *  from partitioned harddisks.
X */
X	if (VOP_IOCTL(devvp, DIOCGPART, &pcfspart, FREAD, NOCRED, p) == 0) {
X	}
X#endif /* defined(HDSUPPORT) */
X
X/*
X *  Read the boot sector of the filesystem, and then
X *  check the boot signature.  If not a dos boot sector
X *  then error out.  We could also add some checking on
X *  the bsOemName field.  So far I've seen the following
X *  values:
X *    "IBM  3.3"
X *    "MSDOS3.3"
X *    "MSDOS5.0"
X */
X	if (error = bread(devvp, 0, 512, NOCRED, &bp0)) {
X		goto error_exit;
X	}
X	bsp = (union bootsector *)bp0->b_un.b_addr;
X	b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
X	b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
X	if (bsp->bs50.bsBootSectSig != BOOTSIG) {
X		error = EINVAL;
X		goto error_exit;
X	}
X
X	pmp = (struct pcfsmount *)malloc(sizeof *pmp, M_PCFSMNT, M_WAITOK);
X	if (pmp == NULL) {
X		error = ENOMEM;
X		goto error_exit;
X	}
X	pmp->pm_inusemap = NULL;
X	pmp->pm_mountp = mp;
X
X/*
X *  Compute several useful quantities from the bpb in
X *  the bootsector.  Copy in the dos 5 variant of the
X *  bpb then fix up the fields that are different between
X *  dos 5 and dos 3.3.
X */
X	pmp->pm_BytesPerSec  = getushort(b50->bpbBytesPerSec);
X	pmp->pm_SectPerClust = b50->bpbSecPerClust;
X	pmp->pm_ResSectors   = getushort(b50->bpbResSectors);
X	pmp->pm_FATs         = b50->bpbFATs;
X	pmp->pm_RootDirEnts  = getushort(b50->bpbRootDirEnts);
X	pmp->pm_Sectors      = getushort(b50->bpbSectors);
X	pmp->pm_Media        = b50->bpbMedia;
X	pmp->pm_FATsecs      = getushort(b50->bpbFATsecs);
X	pmp->pm_SecPerTrack  = getushort(b50->bpbSecPerTrack);
X	pmp->pm_Heads        = getushort(b50->bpbHeads);
X	if (bsp->bs50.bsOemName[5] == '5') {
X		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
X		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
X	} else {
X		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
X		pmp->pm_HugeSectors = 0;
X	}
X	if (pmp->pm_Sectors != 0)
X		pmp->pm_HugeSectors = pmp->pm_Sectors;
X	pmp->pm_fatblk = pmp->pm_ResSectors;
X	pmp->pm_rootdirblk = pmp->pm_fatblk +
X		(pmp->pm_FATs * pmp->pm_FATsecs);
X	pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry))
X					/
X				pmp->pm_BytesPerSec; /* in sectors */
X	pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
X	pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
X		pmp->pm_SectPerClust;
X	pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
X	if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0)
X		printf("mountpcfs(): root directory is not a multiple of the clustersize in length\n");
X
X/*
X *  Compute mask and shift value for isolating cluster relative
X *  byte offsets and cluster numbers from a file offset.
X */
X	bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec;
X	pmp->pm_bpcluster = bpc;
X	pmp->pm_depclust  = bpc/sizeof(struct direntry);
X	pmp->pm_crbomask = bpc - 1;
X	if (bpc == 0) {
X		error = EINVAL;
X		goto error_exit;
X	}
X	bit = 1;
X	for (i = 0; i < 32; i++) {
X		if (bit & bpc) {
X			if (bit ^ bpc) {
X				error = EINVAL;
X				goto error_exit;
X			}
X			pmp->pm_cnshift = i;
X			break;
X		}
X		bit <<= 1;
X	}
X
X	pmp->pm_brbomask = 0x01ff;	/* 512 byte blocks only (so far) */
X	pmp->pm_bnshift = 9;		/* shift right 9 bits to get bn */
X
X/*
X *  Release the bootsector buffer.
X */
X	brelse(bp0);
X	bp0 = NULL;
X
X/*
X *  Allocate memory for the bitmap of allocated clusters,
X *  and then fill it in.
X */
X	pmp->pm_inusemap = malloc((pmp->pm_maxcluster >> 3) + 1,
X		M_PCFSFAT, M_WAITOK);
X	if (!pmp->pm_inusemap) {
X		error = ENOMEM;
X		goto error_exit;
X	}
X
X/*
X *  fillinusemap() needs pm_devvp.
X */
X	pmp->pm_dev = dev;
X	pmp->pm_devvp = devvp;
X
X/*
X *  Have the inuse map filled in.
X */
X	error = fillinusemap(pmp);
X	if (error) {
X		goto error_exit;
X	}
X
X/*
X *  If they want fat updates to be synchronous then let
X *  them suffer the performance degradation in exchange
X *  for the on disk copy of the fat being correct just
X *  about all the time.  I suppose this would be a good
X *  thing to turn on if the kernel is still flakey.
X */
X	pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS;
X
X/*
X *  Finish up.
X */
X	pmp->pm_ronly = ronly;
X	if (ronly == 0)
X		pmp->pm_fmod = 1;
X	mp->mnt_data = (qaddr_t)pmp;
X	mp->mnt_stat.f_fsid.val[0] = (long)dev;
X	mp->mnt_stat.f_fsid.val[1] = MOUNT_PCFS;
X	mp->mnt_flag |= MNT_LOCAL;
X#if defined(QUOTA)
X/*
X *  If we ever do quotas for DOS filesystems this would
X *  be a place to fill in the info in the pcfsmount
X *  structure.
X *  You dolt, quotas on dos filesystems make no sense
X *  because files have no owners on dos filesystems.
X *  of course there is some empty space in the directory
X *  entry where we could put uid's and gid's.
X */
X#endif /* defined(QUOTA) */
X	devvp->v_specflags |= SI_MOUNTEDON;
X
X	return 0;
X
Xerror_exit:;
X	if (bp0)
X		brelse(bp0);
X	if (needclose)
X		(void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE,
X			NOCRED, p);
X	if (pmp) {
X		if (pmp->pm_inusemap)
X			free((caddr_t)pmp->pm_inusemap, M_PCFSFAT);
X		free((caddr_t)pmp, M_PCFSMNT);
X		mp->mnt_data = (qaddr_t)0;
X	}
X	return error;
X}
X
Xint
Xpcfs_start(mp, flags, p)
X	struct mount *mp;
X	int flags;
X	struct proc *p;
X{
X	return 0;
X}
X
X/*
X *  Unmount the filesystem described by mp.
X */
Xint
Xpcfs_unmount(mp, mntflags, p)
X	struct mount *mp;
X	int mntflags;
X	struct proc *p;
X{
X	int flags = 0;
X	int error;
X	struct pcfsmount *pmp = (struct pcfsmount *)mp->mnt_data;
X	struct vnode *vp = pmp->pm_devvp;
X
X	if (mntflags & MNT_FORCE) {
X		if (!pcfsdoforce)
X			return EINVAL;
X		flags |= FORCECLOSE;
X	}
X	mntflushbuf(mp, 0);
X	if (mntinvalbuf(mp))
X		return EBUSY;
X#if defined(QUOTA)
X#endif /* defined(QUOTA) */
X	if (error = vflush(mp, NULLVP, flags))
X		return error;
X	pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON;
X#if defined(PCFSDEBUG)
Xprintf("pcfs_umount(): just before calling VOP_CLOSE()\n");
Xprintf("flag %08x, usecount %d, writecount %d, holdcnt %d\n",
X	vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt);
Xprintf("lastr %d, id %d, mount %08x, op %08x\n",
X	vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op);
Xprintf("freef %08x, freeb %08x, mountf %08x, mountb %08x\n",
X	vp->v_freef, vp->v_freeb, vp->v_mountf, vp->v_mountb);
Xprintf("cleanblkhd %08x, dirtyblkhd %08x, numoutput %d, type %d\n",
X	vp->v_cleanblkhd, vp->v_dirtyblkhd, vp->v_numoutput, vp->v_type);
Xprintf("union %08x, tag %d, data[0] %08x, data[1] %08x\n",
X	vp->v_socket, vp->v_tag, vp->v_data[0], vp->v_data[1]);
X#endif /* defined(PCFSDEBUG) */
X	error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD|FWRITE,
X		NOCRED, p);
X	vrele(pmp->pm_devvp);
X	free((caddr_t)pmp->pm_inusemap, M_PCFSFAT);
X	free((caddr_t)pmp, M_PCFSMNT);
X	mp->mnt_data = (qaddr_t)0;
X	mp->mnt_flag &= ~MNT_LOCAL;
X	return error;
X}
X
Xint
Xpcfs_root(mp, vpp)
X	struct mount *mp;
X	struct vnode **vpp;
X{
X	struct denode *ndep;
X	struct pcfsmount *pmp = (struct pcfsmount *)(mp->mnt_data);
X	int error;
X
X	error = deget(pmp, ATTR_DIRECTORY, 0, 0, PCFSROOT, 0, &ndep);
X#if defined(PCFSDEBUG)
Xprintf("pcfs_root(); mp %08x, pmp %08x, ndep %08x, vp %08x\n",
X mp, pmp, ndep, DETOV(ndep));
X#endif /* defined(PCFSDEBUG) */
X	if (error == 0)
X		*vpp = DETOV(ndep);
X	return error;
X}
X
Xint
Xpcfs_quotactl(mp, cmds, uid, arg, p)
X	struct mount *mp;
X	int cmds;
X	uid_t uid;
X	caddr_t arg;
X	struct proc *p;
X{
X#if defined(QUOTA)
X#else
X	return EOPNOTSUPP;
X#endif /* defined(QUOTA) */
X}
X
Xint
Xpcfs_statfs(mp, sbp, p)
X	struct mount *mp;
X	struct statfs *sbp;
X	struct proc *p;
X{
X	struct pcfsmount *pmp = (struct pcfsmount *)mp->mnt_data;
X
X/*
X *  Fill in the stat block.
X */
X	sbp->f_type   = MOUNT_PCFS;
X	sbp->f_fsize  = pmp->pm_bpcluster;
X	sbp->f_bsize  = pmp->pm_bpcluster;
X	sbp->f_blocks = pmp->pm_nmbrofclusters;
X	sbp->f_bfree  = pmp->pm_freeclustercount;
X	sbp->f_bavail = pmp->pm_freeclustercount;
X	sbp->f_files  = pmp->pm_RootDirEnts;
X	sbp->f_ffree  = 0;		/* what to put in here? */
X
X/*
X *  Copy the mounted on and mounted from names into
X *  the passed in stat block, if it is not the one
X *  in the mount structure.
X */
X	if (sbp != &mp->mnt_stat) {
X		bcopy((caddr_t)mp->mnt_stat.f_mntonname,
X			(caddr_t)&sbp->f_mntonname[0], MNAMELEN);
X		bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
X			(caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
X	}
X	return 0;
X}
X
Xint
Xpcfs_sync(mp, waitfor)
X	struct mount *mp;
X	int waitfor;
X{
X	struct vnode *vp;
X	struct denode *dep;
X	struct pcfsmount *pmp;
X	int error;
X	int allerror = 0;
X
X	pmp = (struct pcfsmount *)mp->mnt_data;
X
X/*
X *  If we ever switch to not updating all of the fats
X *  all the time, this would be the place to update them
X *  from the first one.
X */
X	if (pmp->pm_fmod) {
X		if (pmp->pm_ronly) {
X			printf("pcfs_sync(): writing to readonly filesystem\n");
X			return EINVAL;
X		} else {
X			/* update fats here */
X		}
X	}
X
X/*
X *  Go thru in memory denodes and write them out along
X *  with unwritten file blocks.
X */
Xloop:
X	for (vp = mp->mnt_mounth; vp; vp = vp->v_mountf) {
X		if (vp->v_mount != mp)	/* not ours anymore	*/
X			goto loop;
X		if (VOP_ISLOCKED(vp))	/* file is busy		*/
X			continue;
X		dep = VTODE(vp);
X		if ((dep->de_flag & DEUPD) == 0  &&  vp->v_dirtyblkhd == NULL)
X			continue;
X		if (vget(vp))		/* not there anymore?	*/
X			goto loop;
X		if (vp->v_dirtyblkhd)	/* flush dirty file blocks */
X			vflushbuf(vp, 0);
X		if ((dep->de_flag & DEUPD)  &&
X		    (error = deupdat(dep, &time, 0)))
X			allerror = error;
X		vput(vp);		/* done with this one	*/
X	}
X
X/*
X *  Flush filesystem control info.
X */
X	vflushbuf(pmp->pm_devvp, waitfor == MNT_WAIT ? B_SYNC : 0);
X	return allerror;
X}
X
Xint
Xpcfs_fhtovp(mp, fhp, vpp)
X	struct mount *mp;
X	struct fid *fhp;
X	struct vnode **vpp;
X{
X	printf("pcfs_fhtovp(): someone is finally calling this!\n");
X	return EINVAL;
X}
X
Xint
Xpcfs_vptofh(vp, fhp)
X	struct vnode *vp;
X	struct fid *fhp;
X{
X	printf("pcfs_vptofh(): someone is finally calling this!\n");
X	return EINVAL;
X}
X
Xstruct vfsops pcfs_vfsops = {
X	pcfs_mount,
X	pcfs_start,
X	pcfs_unmount,
X	pcfs_root,
X	pcfs_quotactl,
X	pcfs_statfs,
X	pcfs_sync,
X	pcfs_fhtovp,
X	pcfs_vptofh,
X	pcfs_init
X};
END-of-/sys/pcfs/pcfs_vfsops.c
echo x - /sys/pcfs/pcfs_denode.c
sed 's/^X//' >/sys/pcfs/pcfs_denode.c << 'END-of-/sys/pcfs/pcfs_denode.c'
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#include "param.h"
X#include "systm.h"
X#include "mount.h"
X#include "proc.h"
X#include "buf.h"
X#include "vnode.h"
X#include "kernel.h"	/* defines "time"			*/
X
X#include "bpb.h"
X#include "pcfsmount.h"
X#include "direntry.h"
X#include "denode.h"
X#include "fat.h"
X
X#define	DEHSZ	512
X#if ((DEHSZ & (DEHSZ-1)) == 0)
X#define	DEHASH(dev, deno)	(((dev)+(deno)+((deno)>>16))&(DEHSZ-1))
X#else
X#define	DEHASH(dev, deno)	(((dev)+(deno)+((deno)>>16))%DEHSZ)
X#endif /* ((DEHSZ & (DEHSZ-1)) == 0) */
X
Xunion dehead {
X	union dehead *deh_head[2];
X	struct denode *deh_chain[2];
X} dehead[DEHSZ];
X
Xpcfs_init()
X{
X	int i;
X	union dehead *deh;
X
X	if (VN_MAXPRIVATE < sizeof(struct denode))
X		panic("pcfs_init: vnode too small");
X
X	for (i = DEHSZ, deh = dehead; --i >= 0; deh++) {
X		deh->deh_head[0] = deh;
X		deh->deh_head[1] = deh;
X	}
X}
X
X/*
X *  If deget() succeeds it returns with the gotten denode
X *  locked().
X *  pmp - address of pcfsmount structure of the filesystem
X *    containing the denode of interest.  The pm_dev field
X *    and the address of the pcfsmount structure are used. 
X *  isadir - a flag used to indicate whether the denode of
X *    interest represents a file or a directory.
X *  dirclust - which cluster bp contains, if dirclust is 0
X *    (root directory) diroffset is relative to the beginning
X *    of the root directory, otherwise it is cluster relative.
X *  diroffset - offset past begin of cluster of denode we
X *    want
X *  startclust - number of 1st cluster in the file the
X *    denode represents.  Similar to an inode number.
X *  bp - address of the buf header for the buffer containing
X *    the direntry structure of interest.
X *  depp - returns the address of the gotten denode.
X */
Xint
Xdeget(pmp, isadir, dirclust, diroffset, startclust, bp, depp)
X	struct pcfsmount *pmp;	/* so we know the maj/min number	*/
X	int isadir;		/* ~0 means the denode is a directory	*/
X	u_long dirclust;	/* cluster this dir entry came from	*/
X	u_long diroffset;	/* index of entry within the cluster	*/
X	u_long startclust;	/* # of the 1st cluster in file this de
X				 *  points to				*/
X	struct buf *bp;		/* buffer containing the dir entry	*/
X	struct denode **depp;	/* returns the addr of the gotten denode*/
X{
X	int error;
X	int deoff;
X	dev_t dev = pmp->pm_dev;
X	union dehead *deh;
X	struct mount *mntp = pmp->pm_mountp;
X	extern struct vnodeops pcfs_vnodeops;
X	struct denode *ldep;
X	struct vnode *nvp;
X	struct direntry *direntptr;
X#if defined(PCFSDEBUG)
Xprintf("deget(pmp %08x, isadir %d, dirclust %d, diroffset %d, startclust %d\n",
X	pmp, isadir, dirclust, diroffset, startclust);
Xprintf("      bp %08x, depp %08x)\n",
X	bp, depp);
X#endif /* defined(PCFSDEBUG) */
X
X/*
X *  See if the denode is in the denode cache.
X *  If the denode is for a directory then use the
X *  startcluster in computing the hash value.  If
X *  a regular file then use the location of the directory
X *  entry to compute the hash value.  We use startcluster
X *  for directories because several directory entries
X *  may point to the same directory.  For files
X *  we use the directory entry location because
X *  empty files have a startcluster of 0, which
X *  is non-unique and because it matches the root
X *  directory too.  I don't think the dos filesystem
X *  was designed.
X *  NOTE: The check for de_refcnt > 0 below insures the denode
X *  being examined does not represent an unlinked but
X *  still open file.  These files are not to be accessible
X *  even when the directory entry that represented the
X *  file happens to be reused while the deleted file is still
X *  open.
X */
X	if (isadir)
X		deh = &dehead[DEHASH(dev, startclust)];
X	else
X		deh = &dehead[DEHASH(dev, dirclust+diroffset)];
Xloop:
X	for (ldep = deh->deh_chain[0]; ldep != (struct denode *)deh;
X		ldep = ldep->de_forw) {
X		if (dev == ldep->de_dev  &&  ldep->de_refcnt > 0) {
X			if (ldep->de_Attributes & ATTR_DIRECTORY) {
X				if (ldep->de_StartCluster != startclust  ||
X				    !isadir)
X					continue;
X			} else {	/* it's a file */
X				if (isadir  ||
X				    dirclust  != ldep->de_dirclust  ||
X				    diroffset != ldep->de_diroffset)
X					continue;
X			}
X			if (ldep->de_flag & DELOCKED) {
X				/* should we brelse() the passed buf hdr to
X				 *  avoid some potential deadlock? */
X				ldep->de_flag |= DEWANT;
X				sleep((caddr_t)ldep, PINOD);
X				goto loop;
X			}
X			if (vget(DETOV(ldep)))
X				goto loop;
X#if defined(PCFSDEBUG)
Xprintf("deget(): entry found in cache %08x\n", ldep);
X#endif /* defined(PCFSDEBUG) */
X			*depp = ldep;
X			return 0;
X		}
X	}
X
X
X/*
X *  Directory entry was not in cache, have to create
X *  a vnode and copy it from the passed disk buffer.
X */
X	/* getnewvnode() does a VREF() on the vnode */
X	if (error = getnewvnode(VT_PCFS, mntp, &pcfs_vnodeops, &nvp)) {
X		*depp = 0;
X		return error;
X	}
X	ldep = VTODE(nvp);
X	ldep->de_vnode = nvp;
X	ldep->de_flag = 0;
X	ldep->de_devvp = 0;
X	ldep->de_lockf = 0;
X	ldep->de_dev   = dev;
X	fc_purge(ldep, 0);	/* init the fat cache for this denode */
X
X/*
X *  Insert the denode into the hash queue and lock the
X *  denode so it can't be accessed until we've read it
X *  in and have done what we need to it.
X */
X	insque(ldep, deh);
X	DELOCK(ldep);
X
X/*
X *  Note that the root directory is treated
X *  differently.  There isn't really a directory entry that
X *  describes the root directory (actually the bpb describes it).
X *  So, when we see a reference to cluster 0 we know they
X *  want a directory entry from the root directory.
X *  The value of diroffset for a directory entry  in the
X *  root directory is relative to the beginning of the root
X *  directory.
X */
X	if (dirclust == PCFSROOT) { /* root directory is special */
X		deoff = diroffset % pmp->pm_depclust;
X	} else {
X		deoff = diroffset;
X	}
X
X/*
X *  Copy the directory entry into the denode area of the
X *  vnode.  If they are going after the directory entry
X *  for the root directory, there isn't one so we manufacture
X *  one.
X *  We should probably rummage through the root directory and
X *  find a label entry (if it exists), and then use the time
X *  and date from that entry as the time and date for the
X *  root denode.
X */
X	if (startclust == PCFSROOT  &&  isadir) {
X		ldep->de_Attributes = ATTR_DIRECTORY;
X		ldep->de_StartCluster = PCFSROOT;
X		ldep->de_FileSize = 0;
X		/* fill in time and date so that dos2unixtime() doesn't
X		 * spit up when called from pcfs_getattr() with root denode */
X		ldep->de_Time = 0x0000;		/* 00:00:00	*/
X		ldep->de_Date = (0 << 9) | (1 << 5) | (1 << 0);
X						/* Jan 1, 1980	*/
X		/* leave the other fields as garbage */
X	} else {
X		direntptr = (struct direntry *)bp->b_un.b_addr;
X		direntptr += deoff;
X		ldep->de_de = *direntptr;
X	}
X
X/*
X *  Fill in a few fields of the vnode and finish filling
X *  in the denode.  Then return the address of the found
X *  denode.
X */
X	if (ldep->de_Attributes & ATTR_DIRECTORY) {
X		nvp->v_type = VDIR;
X		if (startclust == PCFSROOT)
X			nvp->v_flag |= VROOT;
X	} else
X		nvp->v_type = VREG;
X	ldep->de_pmp = pmp;
X	ldep->de_devvp = pmp->pm_devvp;
X	ldep->de_refcnt = 1;
X	ldep->de_dirclust = dirclust;
X	ldep->de_diroffset = diroffset;
X	VREF(ldep->de_devvp); /* mark filesystem as inuse? */
X	*depp = ldep;
X	return 0;
X}
X
Xvoid
Xdeput(dep)
X	struct denode *dep;
X{
X	if ((dep->de_flag & DELOCKED) == 0)
X		panic("deput: denode not locked");
X	DEUNLOCK(dep);
X	vrele(DETOV(dep));
X}
X
Xint
Xdeupdat(dep, tp, waitfor)
X	struct denode *dep;
X	struct timeval *tp;
X	int waitfor;
X{
X	int error;
X	daddr_t bn;
X	int diro;
X	struct buf *bp;
X	struct direntry *dirp;
X	struct pcfsmount *pmp = dep->de_pmp;
X	struct vnode *vp = DETOV(dep);
X#if defined(PCFSDEBUG)
Xprintf("deupdat(): dep %08x\n", dep);
X#endif /* defined(PCFSDEBUG) */
X
X/*
X *  If the update bit is off, or this denode is from
X *  a readonly filesystem, or this denode is for a
X *  directory, or the denode represents an open but
X *  unlinked file then don't do anything.  DOS directory
X *  entries that describe a directory do not ever
X *  get updated.  This is the way dos treats them.
X */
X	if ((dep->de_flag & DEUPD) == 0  ||
X	    vp->v_mount->mnt_flag & MNT_RDONLY  ||
X	    dep->de_Attributes & ATTR_DIRECTORY  ||
X	    dep->de_refcnt <= 0)
X		return 0;
X
X/*
X *  Read in the cluster containing the directory entry
X *  we want to update.
X */
X	if (error = readde(dep, &bp, &dirp)) {
X		return error;
X	}
X
X/*
X *  Put the passed in time into the directory entry.
X */
X	unix2dostime(&time, (union dosdate *)&dep->de_Date,
X		(union dostime *)&dep->de_Time);
X	dep->de_flag &= ~DEUPD;
X
X/*
X *  Copy the directory entry out of the denode into
X *  the cluster it came from.
X */
X	*dirp = dep->de_de;	/* structure copy */
X
X/*
X *  Write the cluster back to disk.  If they asked
X *  for us to wait for the write to complete, then
X *  use bwrite() otherwise use bdwrite().
X */
X	error = 0;	/* note that error is 0 from above, but ... */
X	if (waitfor)
X		error = bwrite(bp);
X	else
X		bdwrite(bp);
X	return error;
X}
X
X/*
X *  Truncate the file described by dep to the length
X *  specified by length.
X */
Xint
Xdetrunc(dep, length, flags)
X	struct denode *dep;
X	u_long length;
X	int flags;
X{
X	int error;
X	int allerror;
X	unsigned long eofentry;
X	unsigned long chaintofree;
X	daddr_t bn;
X	int boff;
X	int isadir = dep->de_Attributes & ATTR_DIRECTORY;
X	struct buf *bp;
X	struct pcfsmount *pmp = dep->de_pmp;
X#if defined(PCFSDEBUG)
Xprintf("detrunc(): file %s, length %d, flags %d\n", dep->de_Name, length, flags);
X#endif /* defined(PCFSDEBUG) */
X
X/*
X *  Disallow attempts to truncate the root directory
X *  since it is of fixed size.  That's just the way
X *  dos filesystems are.  We use the VROOT bit in the
X *  vnode because checking for the directory bit and
X *  a startcluster of 0 in the denode is not adequate
X *  to recognize the root directory at this point in
X *  a file or directory's life.
X */
X	if (DETOV(dep)->v_flag & VROOT) {
X		printf("detrunc(): can't truncate root directory, clust %d, offset %d\n",
X			dep->de_dirclust, dep->de_diroffset);
X		return EINVAL;
X	}
X
X	vnode_pager_setsize(DETOV(dep), length);
X
X/*
X *  If we are going to truncate a directory then we better
X *  find out how long it is.  DOS doesn't keep the length of
X *  a directory file in its directory entry.
X */
X	if (isadir) {
X		/* pcbmap() returns the # of clusters in the file */
X		error = pcbmap(dep, 0xffff, 0, &eofentry);
X		if (error != 0  &&  error != E2BIG)
X			return error;
X		dep->de_FileSize = eofentry << pmp->pm_cnshift;
X	}
X
X	if (dep->de_FileSize <= length) {
X		dep->de_flag |= DEUPD;
X		error = deupdat(dep, &time, 1);
X#if defined(PCFSDEBUG)
Xprintf("detrunc(): file is shorter return point, errno %d\n", error);
X#endif /* defined(PCFSDEBUG) */
X		return error;
X	}
X
X/*
X *  If the desired length is 0 then remember the starting
X *  cluster of the file and set the StartCluster field in
X *  the directory entry to 0.  If the desired length is
X *  not zero, then get the number of the last cluster in
X *  the shortened file.  Then get the number of the first
X *  cluster in the part of the file that is to be freed.
X *  Then set the next cluster pointer in the last cluster
X *  of the file to CLUST_EOFE.
X */
X	if (length == 0) {
X		chaintofree = dep->de_StartCluster;
X		dep->de_StartCluster = 0;
X		eofentry = ~0;
X	} else {
X		error = pcbmap(dep, (length-1) >> pmp->pm_cnshift,
X				0, &eofentry);
X		if (error) {
X#if defined(PCFSDEBUG)
Xprintf("detrunc(): pcbmap fails %d\n", error);
X#endif /* defined(PCFSDEBUG) */
X			return error;
X		}
X	}
X
X	fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift);
X
X/*
X *  If the new length is not a multiple of the cluster size
X *  then we must zero the tail end of the new last cluster in case
X *  it becomes part of the file again because of a seek.
X */
X	if ((boff = length & pmp->pm_crbomask) != 0) {
X		/* should read from file vnode or
X		 * filesystem vnode depending on if file or dir */
X		if (isadir) {
X			bn = cntobn(pmp, eofentry);
X			error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,
X				NOCRED, &bp);
X		} else {
X			bn = (length-1) >> pmp->pm_cnshift;
X			error = bread(DETOV(dep), bn, pmp->pm_bpcluster,
X				NOCRED, &bp);
X		}
X		if (error) {
X#if defined(PCFSDEBUG)
Xprintf("detrunc(): bread fails %d\n", error);
X#endif /* defined(PCFSDEBUG) */
X			brelse(bp);
X			return error;
X		}
X		vnode_pager_uncache(DETOV(dep));	/* what's this for? */
X							/* is this the right
X							 *  place for it? */
X		bzero(bp->b_un.b_addr + boff, pmp->pm_bpcluster - boff);
X		if (flags & IO_SYNC)
X			bwrite(bp);
X		else
X			bdwrite(bp);
X	}
X
X/*
X *  Write out the updated directory entry.  Even
X *  if the update fails we free the trailing clusters.
X */
X	dep->de_FileSize = length;
X	dep->de_flag |= DEUPD;
X	vinvalbuf(DETOV(dep), length > 0);
X	allerror = deupdat(dep, &time, MNT_WAIT);
X#if defined(PCFSDEBUG)
Xprintf("detrunc(): allerror %d, eofentry %d\n",
X	allerror, eofentry);
X#endif /* defined(PCFSDEBUG) */
X
X/*
X *  If we need to break the cluster chain for the file
X *  then do it now.
X */
X	if (eofentry != ~0) {
X		error = fatentry(FAT_GET_AND_SET, pmp, eofentry,
X			&chaintofree, CLUST_EOFE);
X		if (error) {
X#if defined(PCFSDEBUG)
Xprintf("detrunc(): fatentry errors %d\n", error);
X#endif /* defined(PCFSDEBUG) */
X			return error;
X		}
X		fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift,
X			eofentry);
X	}
X
X/*
X *  Now free the clusters removed from the file because
X *  of the truncation.
X */
X	if (chaintofree != 0  &&  !PCFSEOF(chaintofree))
X		freeclusterchain(pmp, chaintofree);
X
X	return allerror;
X}
X
X/*
X *  Move a denode to its correct hash queue after
X *  the file it represents has been moved to a new
X *  directory.
X */
Xreinsert(dep)
X	struct denode *dep;
X{
X	struct pcfsmount *pmp = dep->de_pmp;
X	union dehead *deh;
X
X/*
X *  Fix up the denode cache.  If the denode is
X *  for a directory, there is nothing to do since the
X *  hash is based on the starting cluster of the directory
X *  file and that hasn't changed.  If for a file the hash
X *  is based on the location
X *  of the directory entry, so we must remove it from the
X *  cache and re-enter it with the hash based on the new
X *  location of the directory entry.
X */
X	if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
X		remque(dep);
X		deh = &dehead[DEHASH(pmp->pm_dev,
X			dep->de_dirclust + dep->de_diroffset)];
X		insque(dep, deh);
X	}
X}
X
Xint pcfs_prtactive;	/* print reclaims of active vnodes */
X
Xint
Xpcfs_reclaim(vp)
X	struct vnode *vp;
X{
X	struct denode *dep = VTODE(vp);
X	int i;
X#if defined(PCFSDEBUG)
Xprintf("pcfs_reclaim(): dep %08x, file %s, refcnt %d\n",
X	dep, dep->de_Name, dep->de_refcnt);
X#endif /* defined(PCFSDEBUG) */
X
X	if (pcfs_prtactive && vp->v_usecount != 0)
X		vprint("pcfs_reclaim(): pushing active", vp);
X
X/*
X *  Remove the denode from the denode hash chain we
X *  are in.
X */
X	remque(dep);
X	dep->de_forw = dep;
X	dep->de_back = dep;
X
X	cache_purge(vp);
X/*
X *  Indicate that one less file on the filesystem is open.
X */
X	if (dep->de_devvp) {
X		vrele(dep->de_devvp);
X		dep->de_devvp = 0;
X	}
X
X	dep->de_flag = 0;
X	return 0;
X}
X
Xint
Xpcfs_inactive(vp, p)
X	struct vnode *vp;
X	struct proc *p;
X{
X	struct denode *dep = VTODE(vp);
X	int error = 0;
X#if defined(PCFSDEBUG)
Xprintf("pcfs_inactive(): dep %08x, de_Name[0] %x\n", dep, dep->de_Name[0]);
X#endif /* defined(PCFSDEBUG) */
X
X	if (pcfs_prtactive && vp->v_usecount != 0)
X		vprint("pcfs_inactive(): pushing active", vp);
X
X/*
X *  Get rid of denodes related to stale file handles.
X *  Hmmm, what does this really do?
X */
X	if (dep->de_Name[0] == SLOT_DELETED) {
X		if ((vp->v_flag & VXLOCK) == 0)
X			vgone(vp);
X		return 0;
X	}
X
X/*
X *  If the file has been deleted and it is on a read/write
X *  filesystem, then truncate the file, and mark the directory
X *  slot as empty.  (This may not be necessary for the dos
X *  filesystem.
X */
X#if defined(PCFSDEBUG)
Xprintf("pcfs_inactive(): dep %08x, refcnt %d, mntflag %x, MNT_RDONLY %x\n",
X	dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY);
X#endif /* defined(PCFSDEBUG) */
X	DELOCK(dep);
X	if (dep->de_refcnt <= 0  &&  (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
X		error = detrunc(dep, (u_long)0, 0);
X		dep->de_flag |= DEUPD;
X		dep->de_Name[0] = SLOT_DELETED;
X	}
X	DEUPDAT(dep, &time, 0);
X	DEUNLOCK(dep);
X	dep->de_flag = 0;
X
X/*
X *  If we are done with the denode, then reclaim
X *  it so that it can be reused now.
X */
X#if defined(PCFSDEBUG)
Xprintf("pcfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount,
X	dep->de_Name[0]);
X#endif /* defined(PCFSDEBUG) */
X	if (vp->v_usecount == 0  &&  dep->de_Name[0] == SLOT_DELETED)
X		vgone(vp);
X	return error;
X}
X
Xint
Xdelock(dep)
X	struct denode *dep;
X{
X	while (dep->de_flag & DELOCKED) {
X		dep->de_flag |= DEWANT;
X		if (dep->de_spare0 == curproc->p_pid)
X			panic("delock: locking against myself");
X		dep->de_spare1 = curproc->p_pid;
X		(void) sleep((caddr_t)dep, PINOD);
X	}
X	dep->de_spare1 = 0;
X	dep->de_spare0 = curproc->p_pid;
X	dep->de_flag |= DELOCKED;
X
X	return 0;
X}
X
Xint
Xdeunlock(dep)
X	struct denode *dep;
X{
X	if ((dep->de_flag & DELOCKED) == 0)
X		vprint("deunlock: found unlocked denode", DETOV(dep));
X	dep->de_spare0 = 0;
X	dep->de_flag &= ~DELOCKED;
X	if (dep->de_flag & DEWANT) {
X		dep->de_flag &= ~DEWANT;
X		wakeup((caddr_t)dep);
X	}
X
X	return 0;
X}
END-of-/sys/pcfs/pcfs_denode.c
echo x - /sys/pcfs/pcfs_conv.c
sed 's/^X//' >/sys/pcfs/pcfs_conv.c << 'END-of-/sys/pcfs/pcfs_conv.c'
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 *  System include files.
X */
X#include "param.h"
X#include "time.h"
X#include "kernel.h"	/* defines tz */
X
X/*
X *  PCFS include files.
X */
X#include "direntry.h"
X
X/*
X *  Days in each month in a regular year.
X */
Xunsigned short regyear[] = {
X	31,	28,	31,	30,	31,	30,
X	31,	31,	30,	31,	30,	31
X};
X
X/*
X *  Days in each month in a leap year.
X */
Xunsigned short leapyear[] = {
X	31,	29,	31,	30,	31,	30,
X	31,	31,	30,	31,	30,	31
X};
X
X/*
X *  Variables used to remember parts of the last time
X *  conversion.  Maybe we can avoid a full conversion.
X */
Xunsigned long lasttime = 0;
Xunsigned long lastday;
Xunion dosdate lastddate;
Xunion dostime lastdtime;
X
X/*
X *  Convert the unix version of time to dos's idea of time
X *  to be used in file timestamps.
X *  The passed in unix time is assumed to be in GMT.
X */
Xvoid
Xunix2dostime(tvp, ddp, dtp)
X	struct timeval *tvp;
X	union dosdate *ddp;
X	union dostime *dtp;
X{
X	unsigned long days;
X	unsigned long inc;
X	unsigned long year;
X	unsigned long month;
X	unsigned short *months;
X
X/*
X *  If the time from the last conversion is the same
X *  as now, then skip the computations and use the
X *  saved result.
X */
X	if (lasttime != tvp->tv_sec) {
X		lasttime = tvp->tv_sec - (tz.tz_minuteswest * 60)
X			/* +- daylight savings time correction */;
X		lastdtime.dts.dt_2seconds = (lasttime % 60) >> 1;
X		lastdtime.dts.dt_minutes  = (lasttime / 60) % 60;
X		lastdtime.dts.dt_hours    = (lasttime / (60 * 60)) % 24;
X
X/*
X *  If the number of days since 1970 is the same as the
X *  last time we did the computation then skip all this
X *  leap year and month stuff.
X */
X		days = lasttime / (24 * 60 * 60);
X		if (days != lastday) {
X			lastday = days;
X			for (year = 1970; ; year++) {
X				inc = year & 0x03 ? 365 : 366;
X				if (days < inc) break;
X				days -= inc;
X			}
X			months = year & 0x03 ? regyear : leapyear;
X			for (month = 0; month < 12; month++) {
X				if (days < months[month]) break;
X				days -= months[month];
X			}
X			lastddate.dds.dd_day = days + 1;
X			lastddate.dds.dd_month = month+1;
X/*
X *  Remember dos's idea of time is relative to 1980.
X *  unix's is relative to 1970.  If somehow we get a
X *  time before 1980 then don't give totally crazy
X *  results.
X */
X			lastddate.dds.dd_year = year < 1980 ? 0 : year - 1980;
X		}
X	}
X	dtp->dti = lastdtime.dti;
X	ddp->ddi = lastddate.ddi;
X}
X
X/*
X *  The number of seconds between Jan 1, 1970 and
X *  Jan 1, 1980.
X *  In that interval there were 8 regular years and
X *  2 leap years.
X */
X#define	SECONDSTO1980	(((8 * 365) + (2 * 366)) * (24 * 60 * 60))
X
Xunion dosdate lastdosdate;
Xunsigned long lastseconds;
X
X/*
X *  Convert from dos' idea of time to unix'.
X *  This will probably only be called from the
X *  stat(), and fstat() system calls
X *  and so probably need not be too efficient.
X */
Xvoid
Xdos2unixtime(ddp, dtp, tvp)
X	union dosdate *ddp;
X	union dostime *dtp;
X	struct timeval *tvp;
X{
X	unsigned long seconds;
X	unsigned long month;
X	unsigned long yr;
X	unsigned long days;
X	unsigned short *months;
X
X	seconds = (dtp->dts.dt_2seconds << 1) +
X		  (dtp->dts.dt_minutes * 60) +
X		  (dtp->dts.dt_hours * 60 * 60);
X/*
X *  If the year, month, and day from the last conversion
X *  are the same then use the saved value.
X */
X	if (lastdosdate.ddi != ddp->ddi) {
X		lastdosdate.ddi = ddp->ddi;
X		days = 0;
X		for (yr = 0; yr < ddp->dds.dd_year; yr++) {
X			days += yr & 0x03 ? 365 : 366;
X		}
X		months = yr & 0x03 ? regyear : leapyear;
X/*
X *  Prevent going from 0 to 0xffffffff in the following
X *  loop.
X */
X		if (ddp->dds.dd_month == 0) {
X			printf("dos2unixtime(): month value out of range (%d)\n",
X				ddp->dds.dd_month);
X			ddp->dds.dd_month = 1;
X		}
X		for (month = 0; month < ddp->dds.dd_month-1; month++) {
X			days += months[month];
X		}
X		days += ddp->dds.dd_day - 1;
X		lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
X	}
X	tvp->tv_sec = seconds + lastseconds + (tz.tz_minuteswest * 60)
X		/* -+ daylight savings time correction */;
X	tvp->tv_usec = 0;
X}
X
X/*
X *  Cheezy macros to do case detection and conversion
X *  for the ascii character set.  DOESN'T work for ebcdic.
X */
X#define	isupper(c)	(c >= 'A'  &&  c <= 'Z')
X#define	islower(c)	(c >= 'a'  &&  c <= 'z')
X#define	toupper(c)	(c & ~' ')
X#define	tolower(c)	(c | ' ')
X
X/*
X *  DOS filenames are made of 2 parts, the name part and
X *  the extension part.  The name part is 8 characters
X *  long and the extension part is 3 characters long.  They
X *  may contain trailing blanks if the name or extension
X *  are not long enough to fill their respective fields.
X */
X
X/*
X *  Convert a DOS filename to a unix filename.
X *  And, return the number of characters in the
X *  resulting unix filename excluding the terminating
X *  null.
X */
Xint
Xdos2unixfn(dn, un)
X	unsigned char dn[11];
X	unsigned char *un;
X{
X	int i;
X	int ni;
X	int ei;
X	int thislong = 0;
X	unsigned char c;
X	unsigned char *origun = un;
X
X/*
X *  Find the last character in the name portion
X *  of the dos filename.
X */
X	for (ni = 7; ni >= 0; ni--)
X		if (dn[ni] != ' ') break;
X
X/*
X *  Find the last character in the extension
X *  portion of the filename.
X */
X	for (ei = 10; ei >= 8; ei--)
X		if (dn[ei] != ' ') break;
X
X/*
X *  Copy the name portion into the unix filename
X *  string.
X *  NOTE: DOS filenames are usually kept in upper
X *  case.  To make it more unixy we convert all
X *  DOS filenames to lower case.  Some may like
X *  this, some may not.
X */
X	for (i = 0; i <= ni; i++) {
X		c = dn[i];
X		*un++ = isupper(c) ? tolower(c) : c;
X		thislong++;
X	}
X
X/*
X *  Now, if there is an extension then put in a period
X *  and copy in the extension.
X */
X	if (ei >= 8) {
X		*un++ = '.';
X		thislong++;
X		for (i = 8; i <= ei; i++) {
X			c = dn[i];
X			*un++ = isupper(c) ? tolower(c) : c;
X			thislong++;
X		}
X	}
X	*un++ = 0;
X
X/*
X *  If first char of the filename is SLOT_E5 (0x05), then
X *  the real first char of the filename should be 0xe5.
X *  But, they couldn't just have a 0xe5 mean 0xe5 because
X *  that is used to mean a freed directory slot.
X *  Another dos quirk.
X */
X	if (*origun == SLOT_E5)
X		*origun = 0xe5;
X
X	return thislong;
X}
X
X/*
X *  Convert a unix filename to a DOS filename.
X *  This function does not ensure that valid
X *  characters for a dos filename are supplied.
X */
Xvoid
Xunix2dosfn(un, dn, unlen)
X	unsigned char *un;
X	unsigned char dn[11];
X	int unlen;
X{
X	int i;
X	unsigned char c;
X
X/*
X *  Fill the dos filename string with blanks.
X *  These are DOS's pad characters.
X */
X	for (i = 0; i <= 10; i++)
X		dn[i] = ' ';
X
X/*
X *  The filenames "." and ".." are handled specially,
X *  since they don't follow dos filename rules.
X */
X	if (un[0] == '.'  &&  un[1] == '\0') {
X		dn[0] = '.';
X		return;
X	}
X	if (un[0] == '.'  &&  un[1] == '.'  &&  un[2] == '\0') {
X		dn[0] = '.';
X		dn[1] = '.';
X		return;
X	}
X
X/*
X *  Copy the unix filename into the dos filename string
X *  upto the end of string, a '.', or 8 characters.
X *  Whichever happens first stops us.
X *  This forms the name portion of the dos filename.
X *  Fold to upper case.
X */
X	for (i = 0; i <= 7  &&  unlen  &&  (c = *un)  &&  c != '.'; i++) {
X		dn[i] = islower(c) ? toupper(c) : c;
X		un++;
X		unlen--;
X	}
X
X/*
X *  If the first char of the filename is 0xe5, then translate
X *  it to 0x05.  This is because 0xe5 is the marker for a
X *  deleted directory slot.  I guess this means you can't
X *  have filenames that start with 0x05.  I suppose we should
X *  check for this and doing something about it.
X */
X	if (dn[0] == SLOT_DELETED)
X		dn[0] = SLOT_E5;
X
X/*
X *  Strip any further characters up to a '.' or the
X *  end of the string.
X */
X	while (unlen  &&  (c = *un)  &&  c != '.') {
X		un++;
X		unlen--;
X	}
X
X/*
X *  If we stopped on a '.', then get past it.
X */
X	if (c == '.') un++;
X
X/*
X *  Copy in the extension part of the name, if any.
X *  Force to upper case.
X *  Note that the extension is allowed to contain '.'s.
X *  Filenames in this form are probably inaccessable
X *  under dos.
X */
X	for (i = 8; i <= 10  &&  unlen  &&  (c = *un); i++) {
X		dn[i] = islower(c) ? toupper(c) : c;
X		un++;
X		unlen--;
X	}
X}
X
X/*
X *  Get rid of these macros before someone discovers
X *  we are using such hideous things.
X */
X#undef	isupper
X#undef	islower
X#undef	toupper
X#undef	tolower
END-of-/sys/pcfs/pcfs_conv.c
exit