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