Return to BSD News archive
Xref: sserve comp.unix.bsd:7907 alt.sources:4518 Path: sserve!manuel.anu.edu.au!munnari.oz.au!spool.mu.edu!uwm.edu!linac!pacific.mps.ohio-state.edu!zaphod.mps.ohio-state.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 3/5 Message-ID: <bc0H03vfbbM800@amdahl.uts.amdahl.com> Date: 17 Nov 92 17:26:49 GMT Followup-To: comp.unix.bsd Organization: Amdahl Corporation, Sunnyvale CA Lines: 1659 # 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_lookup.c # /sys/pcfs/pcfs_fat.c # echo x - /sys/pcfs/pcfs_lookup.c sed 's/^X//' >/sys/pcfs/pcfs_lookup.c << 'END-of-/sys/pcfs/pcfs_lookup.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 "namei.h" X#include "buf.h" X#include "vnode.h" X#include "mount.h" X X#include "bpb.h" X#include "direntry.h" X#include "denode.h" X#include "pcfsmount.h" X#include "fat.h" X X/* X * When we search a directory the blocks containing directory X * entries are read and examined. The directory entries X * contain information that would normally be in the inode X * of a unix filesystem. This means that some of a directory's X * contents may also be in memory resident denodes (sort of X * an inode). This can cause problems if we are searching X * while some other process is modifying a directory. To X * prevent one process from accessing incompletely modified X * directory information we depend upon being the soul owner X * of a directory block. bread/brelse provide this service. X * This being the case, when a process modifies a directory X * it must first acquire the disk block that contains the X * directory entry to be modified. Then update the disk X * block and the denode, and then write the disk block out X * to disk. This way disk blocks containing directory X * entries and in memory denode's will be in synch. X */ Xint Xpcfs_lookup(vdp, ndp, p) X struct vnode *vdp; /* vnode of directory to search */ X struct nameidata *ndp; X struct proc *p; X{ X daddr_t bn; X int flag; X int error; X int lockparent; X int wantparent; X int slotstatus; X#define NONE 0 X#define FOUND 1 X int slotoffset; X int slotcluster; X int frcn; X unsigned long cluster; X int rootreloff; X int diroff; X int isadir; /* ~0 if found direntry is a directory */ X unsigned long scn; /* starting cluster number */ X struct denode *dp; X struct denode *pdp; X struct denode *tdp; X struct pcfsmount *pmp; X struct buf *bp = 0; X struct direntry *dep; X unsigned char dosfilename[12]; X X#if defined(PCFSDEBUG) Xprintf("pcfs_lookup(): looking for %s\n", ndp->ni_ptr); X#endif /* defined(PCFSDEBUG) */ X ndp->ni_dvp = vdp; X ndp->ni_vp = NULL; X dp = VTODE(vdp); X pmp = dp->de_pmp; X lockparent = ndp->ni_nameiop & LOCKPARENT; X flag = ndp->ni_nameiop & OPMASK; X wantparent = ndp->ni_nameiop & (LOCKPARENT | WANTPARENT); X#if defined(PCFSDEBUG) Xprintf("pcfs_lookup(): vdp %08x, dp %08x, Attr %02x\n", X vdp, dp, dp->de_Attributes); X#endif /* defined(PCFSDEBUG) */ X X/* X * Be sure vdp is a directory. Since dos filesystems X * don't have the concept of execute permission anybody X * can search a directory. X */ X if ((dp->de_Attributes & ATTR_DIRECTORY) == 0) X return ENOTDIR; X X/* X * See if the component of the pathname we are looking for X * is in the directory cache. If so then do a few things X * and return. X */ X if (error = cache_lookup(ndp)) { X int vpid; X X if (error == ENOENT) X return error; X#ifdef PARANOID X if (vdp == ndp->ni_rdir && ndp->ni_isdotdot) X panic("pcfs_lookup: .. thru root"); X#endif /* PARANOID */ X pdp = dp; X vdp = ndp->ni_vp; X dp = VTODE(vdp); X vpid = vdp->v_id; X if (pdp == dp) { X VREF(vdp); X error = 0; X } else if (ndp->ni_isdotdot) { X DEUNLOCK(pdp); X error = vget(vdp); X if (!error && lockparent && *ndp->ni_next == '\0') X DELOCK(pdp); X } else { X error = vget(vdp); X if (!lockparent || error || *ndp->ni_next != '\0') X DEUNLOCK(pdp); X } X X if (!error) { X if (vpid == vdp->v_id) { X#if defined(PCFSDEBUG) Xprintf("pcfs_lookup(): cache hit, vnode %08x, file %s\n", vdp, dp->de_Name); X#endif /* defined(PCFSDEBUG) */ X return 0; X } X deput(dp); X if (lockparent && pdp != dp && *ndp->ni_next == '\0') X DEUNLOCK(pdp); X } X DELOCK(pdp); X dp = pdp; X vdp = DETOV(dp); X ndp->ni_vp = NULL; X } X X/* X * If they are going after the . or .. entry in the X * root directory, they won't find it. DOS filesystems X * don't have them in the root directory. So, we fake it. X * deget() is in on this scam too. X */ X if ((vdp->v_flag & VROOT) && ndp->ni_ptr[0] == '.' && X (ndp->ni_namelen == 1 || X (ndp->ni_namelen == 2 && ndp->ni_ptr[1] == '.'))) { X isadir = ATTR_DIRECTORY; X scn = PCFSROOT; X diroff = -1; X cluster = ~0; X#if defined(PCFSDEBUG) Xprintf("pcfs_lookup(): looking for . or .. in root directory\n"); X#endif /* defined(PCFSDEBUG) */ X goto foundroot; X } X X/* X * Don't search for free slots unless we are creating X * a filename and we are at the end of the pathname. X */ X slotstatus = FOUND; X if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == '\0') { X slotstatus = NONE; X slotoffset = -1; X } X X unix2dosfn((unsigned char *)ndp->ni_ptr, dosfilename, ndp->ni_namelen); X dosfilename[11] = 0; X#if defined(PCFSDEBUG) Xprintf("pcfs_lookup(): dos version of filename %s, length %d\n", X dosfilename, ndp->ni_namelen); X#endif /* defined(PCFSDEBUG) */ X/* X * Search the directory pointed at by vdp for the X * name pointed at by ndp->ni_ptr. X */ X tdp = NULL; X/* X * The outer loop ranges over the clusters that make X * up the directory. Note that the root directory is X * different from all other directories. It has a X * fixed number of blocks that are not part of the X * pool of allocatable clusters. So, we treat it a X * little differently. X * The root directory starts at "cluster" 0. X */ X rootreloff = 0; X for (frcn = 0; ; frcn++) { X if (error = pcbmap(dp, frcn, &bn, &cluster)) { X if (error == E2BIG) X break; X return error; X } X if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp)) { X brelse(bp); X return error; X } X for (diroff = 0; diroff < pmp->pm_depclust; diroff++) { X dep = (struct direntry *)bp->b_un.b_addr + diroff; X X/* X * If the slot is empty and we are still looking for X * an empty then remember this one. If the slot is X * not empty then check to see if it matches what we X * are looking for. If the slot has never been filled X * with anything, then the remainder of the directory X * has never been used, so there is no point in searching X * it. X */ X if (dep->deName[0] == SLOT_EMPTY || X dep->deName[0] == SLOT_DELETED) { X if (slotstatus != FOUND) { X slotstatus = FOUND; X if (cluster == PCFSROOT) X slotoffset = rootreloff; X else X slotoffset = diroff; X slotcluster = cluster; X } X if (dep->deName[0] == SLOT_EMPTY) { X brelse(bp); X goto notfound; X } X } else { X /* Ignore volume labels (anywhere, not just X * the root directory). */ X if ((dep->deAttributes & ATTR_VOLUME) == 0 && X bcmp(dosfilename, dep->deName, 11) == 0) { X#if defined(PCFSDEBUG) Xprintf("pcfs_lookup(): match diroff %d, rootreloff %d\n", diroff, rootreloff); X#endif /* defined(PCFSDEBUG) */ X/* X * Remember where this directory entry came from X * for whoever did this lookup. X * If this is the root directory we are interested X * in the offset relative to the beginning of the X * directory (not the beginning of the cluster). X */ X if (cluster == PCFSROOT) X diroff = rootreloff; X ndp->ni_pcfs.pcfs_offset = diroff; X ndp->ni_pcfs.pcfs_cluster = cluster; X goto found; X } X } X rootreloff++; X } /* for (diroff = 0; .... */ X/* X * Release the buffer holding the directory cluster X * just searched. X */ X brelse(bp); X } /* for (frcn = 0; ; frcn++) */ Xnotfound:; X/* X * We hold no disk buffers at this point. X */ X X/* X * If we get here we didn't find the entry we were looking X * for. But that's ok if we are creating or renaming and X * are at the end of the pathname and the directory hasn't X * been removed. X */ X#if defined(PCFSDEBUG) Xprintf("pcfs_lookup(): flag %d, refcnt %d, slotstatus %d\n", X flag, dp->de_refcnt, slotstatus); Xprintf(" slotoffset %d, slotcluster %d\n", X slotoffset, slotcluster); X#endif /* defined(PCFSDEBUG) */ X if ((flag == CREATE || flag == RENAME) && X *ndp->ni_next == '\0' && dp->de_refcnt != 0) { X if (slotstatus == NONE) { X ndp->ni_pcfs.pcfs_offset = 0; X ndp->ni_pcfs.pcfs_cluster = 0; X ndp->ni_pcfs.pcfs_count = 0; X } else { X#if defined(PCFSDEBUG) Xprintf("pcfs_lookup(): saving empty slot location\n"); X#endif /* defined(PCFSDEBUG) */ X ndp->ni_pcfs.pcfs_offset = slotoffset; X ndp->ni_pcfs.pcfs_cluster = slotcluster; X ndp->ni_pcfs.pcfs_count = 1; X } X/* dp->de_flag |= DEUPD; /* never update dos directories */ X ndp->ni_nameiop |= SAVENAME; X if (!lockparent) /* leave searched dir locked? */ X DEUNLOCK(dp); X } X/* X * Insert name in cache as non-existant if not X * trying to create it. X */ X if (ndp->ni_makeentry && flag != CREATE) X cache_enter(ndp); X return ENOENT; X Xfound:; X/* X * NOTE: We still have the buffer with matched X * directory entry at this point. X */ X isadir = dep->deAttributes & ATTR_DIRECTORY; X scn = dep->deStartCluster; X Xfoundroot:; X/* X * If we entered at foundroot, then we are looking X * for the . or .. entry of the filesystems root X * directory. isadir and scn were setup before X * jumping here. And, bp is null. There is no buf header. X */ X X/* X * If deleting and at the end of the path, then X * if we matched on "." then don't deget() we would X * probably panic(). Otherwise deget() the directory X * entry. X */ X if (flag == DELETE && ndp->ni_next == '\0') { X if (dp->de_StartCluster == scn && X isadir) { /* "." */ X VREF(vdp); X ndp->ni_vp = vdp; X if (bp) brelse(bp); X return 0; X } X error = deget(pmp, isadir, X cluster, diroff, scn, bp, &tdp); X if (error) { X if (bp) brelse(bp); X return error; X } X ndp->ni_vp = DETOV(tdp); X if (!lockparent) X DEUNLOCK(dp); X if (bp) brelse(bp); X return 0; X } X X/* X * If renaming. X */ X if (flag == RENAME && wantparent && *ndp->ni_next == '\0') { X if (dp->de_StartCluster == scn && X isadir) { X if (bp) brelse(bp); X return EISDIR; X } X error = deget(pmp, isadir, cluster, diroff, X scn, bp, &tdp); X if (error) { X if (bp) brelse(bp); X return error; X } X ndp->ni_vp = DETOV(tdp); X ndp->ni_nameiop |= SAVENAME; X if (!lockparent) X DEUNLOCK(dp); X if (bp) brelse(bp); X return 0; X } X X/* X * ? X */ X pdp = dp; X if (ndp->ni_isdotdot) { X DEUNLOCK(pdp); X error = deget(pmp, isadir, cluster, diroff, X scn, bp, &tdp); X if (error) { X DELOCK(pdp); X if (bp) brelse(bp); X return error; X } X if (lockparent && *ndp->ni_next == '\0') X DELOCK(pdp); X ndp->ni_vp = DETOV(tdp); X } else if (dp->de_StartCluster == scn && X isadir) { /* "." */ X VREF(vdp); X ndp->ni_vp = vdp; X } else { X error = deget(pmp, isadir, cluster, diroff, X scn, bp, &tdp); X if (error) { X if (bp) brelse(bp); X return error; X } X if (!lockparent || *ndp->ni_next != '\0') X DEUNLOCK(pdp); X ndp->ni_vp = DETOV(tdp); X } X if (bp) brelse(bp); X X/* X * Insert name in cache if wanted. X */ X if (ndp->ni_makeentry) X cache_enter(ndp); X return 0; X} X X/* X * dep - directory to copy into the directory X * ndp - nameidata structure containing info on X * where to put the directory entry in the directory. X * depp - return the address of the denode for the X * created directory entry if depp != 0 X */ Xint Xcreatede(dep, ndp, depp) X struct denode *dep; X struct nameidata *ndp; X struct denode **depp; X{ X int bn; X int error; X int theoff; X int isadir = dep->de_Attributes & ATTR_DIRECTORY; X unsigned long newcluster; X struct direntry *ndep; X struct denode *ddep = VTODE(ndp->ni_dvp); /* directory to add to */ X struct pcfsmount *pmp = dep->de_pmp; X struct buf *bp; X#if defined(PCFSDEBUG) Xprintf("createde(dep %08x, ndp %08x, depp %08x)\n", dep, ndp, depp); X#endif /* defined(PCFSDEBUG) */ X X/* X * If no space left in the directory then allocate X * another cluster and chain it onto the end of the X * file. There is one exception to this. That is, X * if the root directory has no more space it can NOT X * be expanded. extendfile() checks for and fails attempts to X * extend the root directory. We just return an error X * in that case. X */ X if (ndp->ni_pcfs.pcfs_count == 0) { X if (error = extendfile(ddep, &bp, &newcluster)) X return error; X ndep = (struct direntry *)bp->b_un.b_addr; X *ndep = dep->de_de; X/* X * If they want us to return with the denode gotten. X */ X if (depp) { X error = deget(pmp, isadir, newcluster, 0, X dep->de_StartCluster, bp, depp); X if (error) { X return error; X } X } X X if (error = bwrite(bp)) { X/*deput()?*/ X return error; X } X/* X * Let caller know where we put the directory entry. X */ X ndp->ni_pcfs.pcfs_cluster = newcluster; X ndp->ni_pcfs.pcfs_offset = 0; X return 0; X } X X/* X * There is space in the existing directory. So, X * we just read in the cluster with space. Copy X * the new directory entry in. Then write it to X * disk. X * NOTE: DOS directories do not get smaller as X * clusters are emptied. X */ X if (ndp->ni_pcfs.pcfs_cluster == PCFSROOT) { X bn = pmp->pm_rootdirblk + X (ndp->ni_pcfs.pcfs_offset / pmp->pm_depclust); X theoff = ndp->ni_pcfs.pcfs_offset % pmp->pm_depclust; X } else { X bn = cntobn(pmp, ndp->ni_pcfs.pcfs_cluster); X theoff = ndp->ni_pcfs.pcfs_offset; X } X if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, X NOCRED, &bp)) { X brelse(bp); X return error; X } X ndep = (struct direntry *)(bp->b_un.b_addr) + theoff; X *ndep = dep->de_de; X/* X * If they want us to return with the denode gotten. X */ X if (depp) { X error = deget(pmp, isadir, ndp->ni_pcfs.pcfs_cluster, X ndp->ni_pcfs.pcfs_offset, X dep->de_StartCluster, bp, depp); X if (error) { X return error; X } X } X if (error = bwrite(bp)) X/*deput()?*/ X return error; /* do a brelse() if error ? */ X return 0; X} X X/* X * Read in a directory entry and mark it as being deleted. X */ Xint Xmarkdeleted(pmp, dirclust, diroffset) X struct pcfsmount *pmp; X unsigned long dirclust; X unsigned long diroffset; X{ X int offset; X int error; X daddr_t bn; X struct direntry *ep; X struct buf *bp; X X if (dirclust == PCFSROOT) { X bn = pmp->pm_rootdirblk + (diroffset / pmp->pm_depclust); X offset = diroffset % pmp->pm_depclust; X } else { X bn = cntobn(pmp, dirclust); X offset = diroffset; X } X/*printf("markdeleted(): bread()ing block %d\n", bn);*/ X if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp)) { X brelse(bp); X return error; X } X X ep = (struct direntry *)bp->b_un.b_addr + offset; X ep->deName[0] = SLOT_DELETED; X X return bwrite(bp); X} X X/* X * Remove a directory entry. X * At this point the file represented by the directory X * entry to be removed is still full length until no X * one has it open. When the file no longer being X * used pcfs_inactive() is called and will truncate X * the file to 0 length. When the vnode containing X * the denode is needed for some other purpose by X * VFS it will call pcfs_reclaim() which will remove X * the denode from the denode cache. X */ Xint Xremovede(ndp) X struct nameidata *ndp; X{ X struct denode *dep = VTODE(ndp->ni_vp); /* the file being removed */ X struct pcfsmount *pmp = dep->de_pmp; X int error; X X#if defined(PCFSDEBUG) X/*printf("removede(): filename %s\n", dep->de_Name); Xprintf("rmde(): dep %08x, ndpcluster %d, ndpoffset %d\n", X dep, ndp->ni_pcfs.pcfs_cluster, ndp->ni_pcfs.pcfs_offset);*/ X#endif /* defined(PCFSDEBUG) */ X X/* X * Read the directory block containing the directory X * entry we are to make free. The nameidata structure X * holds the cluster number and directory entry index X * number of the entry to free. X */ X error = markdeleted(pmp, ndp->ni_pcfs.pcfs_cluster, X ndp->ni_pcfs.pcfs_offset); X X dep->de_refcnt--; X return error; X} X X/* X * Be sure a directory is empty except for "." and "..". X * Return 1 if empty, return 0 if not empty or error. X */ Xint Xdosdirempty(dep) X struct denode *dep; X{ X int dei; X int error; X unsigned long cn; X daddr_t bn; X struct buf *bp; X struct pcfsmount *pmp = dep->de_pmp; X struct direntry *dentp; X X/* X * Since the filesize field in directory entries for a directory X * is zero, we just have to feel our way through the directory X * until we hit end of file. X */ X for (cn = 0;; cn++) { X error = pcbmap(dep, cn, &bn, 0); X if (error == E2BIG) X return 1; /* it's empty */ X error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, X &bp); X if (error) { X brelse(bp); X return error; X } X dentp = (struct direntry *)bp->b_un.b_addr; X for (dei = 0; dei < pmp->pm_depclust; dei++) { X if (dentp->deName[0] != SLOT_DELETED) { X/* X * In dos directories an entry whose name starts with SLOT_EMPTY (0) X * starts the beginning of the unused part of the directory, so we X * can just return that it is empty. X */ X if (dentp->deName[0] == SLOT_EMPTY) { X brelse(bp); X return 1; X } X/* X * Any names other than "." and ".." in a directory mean X * it is not empty. X */ X if (bcmp(dentp->deName, ". ", 11) && X bcmp(dentp->deName, ".. ", 11)) { X brelse(bp); X#if defined(PCFSDEBUG) Xprintf("dosdirempty(): entry %d found %02x, %02x\n", dei, dentp->deName[0], X dentp->deName[1]); X#endif /* defined(PCFSDEBUG) */ X return 0; /* not empty */ X } X } X dentp++; X } X brelse(bp); X } X /*NOTREACHED*/ X} X X/* X * Check to see if the directory described by target is X * in some subdirectory of source. This prevents something X * like the following from succeeding and leaving a bunch X * or files and directories orphaned. X * mv /a/b/c /a/b/c/d/e/f X * Where c and f are directories. X * source - the inode for /a/b/c X * target - the inode for /a/b/c/d/e/f X * Returns 0 if target is NOT a subdirectory of source. X * Otherwise returns a non-zero error number. X * The target inode is always unlocked on return. X */ Xint Xdoscheckpath(source, target) X struct denode *source; X struct denode *target; X{ X daddr_t scn; X struct denode dummy; X struct pcfsmount *pmp; X struct direntry *ep; X struct denode *dep; X struct buf *bp = NULL; X int error = 0; X X dep = target; X if ((target->de_Attributes & ATTR_DIRECTORY) == 0 || X (source->de_Attributes & ATTR_DIRECTORY) == 0) { X error = ENOTDIR; X goto out; X } X if (dep->de_StartCluster == source->de_StartCluster) { X error = EEXIST; X goto out; X } X if (dep->de_StartCluster == PCFSROOT) X goto out; X for (;;) { X if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { X error = ENOTDIR; X goto out; X } X pmp = dep->de_pmp; X scn = dep->de_StartCluster; X error = bread(pmp->pm_devvp, cntobn(pmp, scn), X pmp->pm_bpcluster, NOCRED, &bp); X if (error) { X break; X } X ep = (struct direntry *)bp->b_un.b_addr + 1; X if ((ep->deAttributes & ATTR_DIRECTORY) == 0 || X bcmp(ep->deName, ".. ", 11) != 0) { X error = ENOTDIR; X break; X } X if (ep->deStartCluster == source->de_StartCluster) { X error = EINVAL; X break; X } X if (ep->deStartCluster == PCFSROOT) X break; X deput(dep); X /* NOTE: deget() clears dep on error */ X error = deget(pmp, ATTR_DIRECTORY, scn, 1, X ep->deStartCluster, bp, &dep); X brelse(bp); X bp = NULL; X if (error) X break; X } Xout:; X if (bp) X brelse(bp); X if (error == ENOTDIR) X printf("doscheckpath(): .. not a directory?\n"); X if (dep != NULL) X deput(dep); X return error; X} X X/* X * Read in the disk block containing the directory entry X * dep came from and return the address of the buf header, X * and the address of the directory entry within the block. X */ Xint Xreadde(dep, bpp, epp) X struct denode *dep; X struct buf **bpp; X struct direntry **epp; X{ X int error; X daddr_t bn; X unsigned long theoff; X struct pcfsmount *pmp = dep->de_pmp; X X if (dep->de_dirclust == PCFSROOT) { X bn = pmp->pm_rootdirblk + X (dep->de_diroffset / pmp->pm_depclust); X theoff = dep->de_diroffset % pmp->pm_depclust; X } else { X bn = cntobn(pmp, dep->de_dirclust); X theoff = dep->de_diroffset; X } X if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp)) { X brelse(*bpp); X *bpp = NULL; X return error; X } X if (epp) X *epp = (struct direntry *)((*bpp)->b_un.b_addr) + theoff; X return 0; X} END-of-/sys/pcfs/pcfs_lookup.c echo x - /sys/pcfs/pcfs_fat.c sed 's/^X//' >/sys/pcfs/pcfs_fat.c << 'END-of-/sys/pcfs/pcfs_fat.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 * kernel include files. X */ X#include "param.h" X#include "systm.h" X#include "buf.h" X#include "file.h" X#include "namei.h" X#include "mount.h" /* to define statfs structure */ X#include "vnode.h" /* to define vattr structure */ X#include "errno.h" X X/* X * pcfs include files. X */ X#include "bpb.h" X#include "pcfsmount.h" X#include "direntry.h" X#include "denode.h" X#include "fat.h" X X/* X * Fat cache stats. X */ Xint fc_fileextends = 0; /* # of file extends */ Xint fc_lfcempty = 0; /* # of time last file cluster cache entry X * was empty */ Xint fc_bmapcalls = 0; /* # of times pcbmap was called */ X#define LMMAX 20 Xint fc_lmdistance[LMMAX]; /* counters for how far off the last cluster X * mapped entry was. */ Xint fc_largedistance = 0; /* off by more than LMMAX */ X X/* X * Map the logical cluster number of a file into X * a physical disk sector that is filesystem relative. X * dep - address of denode representing the file of interest X * findcn - file relative cluster whose filesystem relative X * cluster number and/or block number are/is to be found X * bnp - address of where to place the file system relative X * block number. If this pointer is null then don't return X * this quantity. X * cnp - address of where to place the file system relative X * cluster number. If this pointer is null then don't return X * this quantity. X * NOTE: X * Either bnp or cnp must be non-null. X * This function has one side effect. If the requested X * file relative cluster is beyond the end of file, then X * the actual number of clusters in the file is returned X * in *cnp. This is useful for determining how long a X * directory is. If cnp is null, nothing is returned. X */ Xint Xpcbmap(dep, findcn, bnp, cnp) X struct denode *dep; X unsigned long findcn; /* file relative cluster to get */ X daddr_t *bnp; /* returned filesys relative blk number */ X unsigned long *cnp; /* returned cluster number */ X{ X int error; X u_long i; X u_long cn; X u_long prevcn; X u_long byteoffset; X u_long bn; X u_long bo; X struct buf *bp0 = 0; X u_long bp0_bn = -1; X struct buf *bp1 = 0; X struct pcfsmount *pmp = dep->de_pmp; X union fattwiddle x; X X fc_bmapcalls++; X X/* X * If they don't give us someplace to return a value X * then don't bother doing anything. X */ X if (bnp == NULL && cnp == NULL) X return 0; X X i = 0; X cn = dep->de_StartCluster; X/* X * The "file" that makes up the root directory is contiguous, X * permanently allocated, of fixed size, and is not made up X * of clusters. If the cluster number is beyond the end of X * the root directory, then return the number of clusters in X * the file. X */ X if (cn == PCFSROOT) { X if (dep->de_Attributes & ATTR_DIRECTORY) { X if (findcn * pmp->pm_SectPerClust > pmp->pm_rootdirsize) { X if (cnp) X *cnp = pmp->pm_rootdirsize / pmp->pm_SectPerClust; X return E2BIG; X } X if (bnp) X *bnp = pmp->pm_rootdirblk + (findcn * pmp->pm_SectPerClust); X if (cnp) X *cnp = PCFSROOT; X return 0; X } else { /* just an empty file */ X if (cnp) X *cnp = 0; X return E2BIG; X } X } X X/* X * Rummage around in the fat cache, maybe we can avoid X * tromping thru every fat entry for the file. X * And, keep track of how far off the cache was from X * where we wanted to be. X */ X fc_lookup(dep, findcn, &i, &cn); X if ((bn = findcn - i) >= LMMAX) X fc_largedistance++; X else X fc_lmdistance[bn]++; X X/* X * Handle all other files or directories the normal way. X */ X if (FAT12(pmp)) { /* 12 bit fat */ X for (; i < findcn; i++) { X if (PCFSEOF(cn)) { X goto hiteof; X } X byteoffset = cn + (cn >> 1); X bn = (byteoffset >> pmp->pm_bnshift) + pmp->pm_fatblk; X bo = byteoffset & pmp->pm_brbomask; X if (bn != bp0_bn) { X if (bp0) X brelse(bp0); X if (error = bread(pmp->pm_devvp, bn, X pmp->pm_BytesPerSec, NOCRED, &bp0)) { X brelse(bp0); X return error; X } X bp0_bn = bn; X } X x.byte[0] = bp0->b_un.b_addr[bo]; X/* X * If the first byte of the fat entry was the last byte X * in the block, then we must read the next block in the X * fat. We hang on to the first block we read to insure X * that some other process doesn't update it while we X * are waiting for the 2nd block. X * Note that we free bp1 even though the next iteration of X * the loop might need it. X */ X if (bo == pmp->pm_BytesPerSec-1) { X if (error = bread(pmp->pm_devvp, bn+1, X pmp->pm_BytesPerSec, NOCRED, &bp1)) { X brelse(bp0); X return error; X } X x.byte[1] = bp1->b_un.b_addr[0]; X brelse(bp1); X } else { X x.byte[1] = bp0->b_un.b_addr[bo+1]; X } X if (cn & 1) X x.word >>= 4; X prevcn = cn; X cn = x.word & 0x0fff; X/* X * Force the special cluster numbers in the range X * 0x0ff0-0x0fff to be the same as for 16 bit cluster X * numbers to let the rest of pcfs think it is always X * dealing with 16 bit fats. X */ X if ((cn & 0x0ff0) == 0x0ff0) X cn |= 0xf000; X } X } else { /* 16 bit fat */ X for (; i < findcn; i++) { X if (PCFSEOF(cn)) { X goto hiteof; X } X byteoffset = cn << 1; X bn = (byteoffset >> pmp->pm_bnshift) + pmp->pm_fatblk; X bo = byteoffset & pmp->pm_brbomask; X if (bn != bp0_bn) { X if (bp0) X brelse(bp0); X if (error = bread(pmp->pm_devvp, bn, X pmp->pm_BytesPerSec, NOCRED, &bp0)) { X brelse(bp0); X return error; X } X bp0_bn = bn; X } X prevcn = cn; X cn = *(u_short *)(bp0->b_un.b_addr+bo); X } X } X X if (!PCFSEOF(cn)) { X if (bp0) X brelse(bp0); X if (bnp) X *bnp = cntobn(pmp, cn); X if (cnp) X *cnp = cn; X fc_setcache(dep, FC_LASTMAP, i, cn); X return 0; X } X Xhiteof:; X if (cnp) X *cnp = i; X if (bp0) X brelse(bp0); X /* update last file cluster entry in the fat cache */ X fc_setcache(dep, FC_LASTFC, i-1, prevcn); X return E2BIG; X} X X/* X * Find the closest entry in the fat cache to the X * cluster we are looking for. X */ Xfc_lookup(dep, findcn, frcnp, fsrcnp) X struct denode *dep; X unsigned long findcn; X unsigned long *frcnp; X unsigned long *fsrcnp; X{ X int i; X unsigned long cn; X struct fatcache *closest = 0; X X for (i = 0; i < FC_SIZE; i++) { X cn = dep->de_fc[i].fc_frcn; X if (cn != FCE_EMPTY && cn <= findcn) { X if (closest == 0 || cn > closest->fc_frcn) X closest = &dep->de_fc[i]; X } X } X if (closest) { X *frcnp = closest->fc_frcn; X *fsrcnp = closest->fc_fsrcn; X } X} X X/* X * Purge the fat cache in denode dep of all entries X * relating to file relative cluster frcn and beyond. X */ Xfc_purge(dep, frcn) X struct denode *dep; X unsigned int frcn; X{ X int i; X struct fatcache *fcp; X X fcp = dep->de_fc; X for (i = 0; i < FC_SIZE; i++) { X if (fcp->fc_frcn != FCE_EMPTY && fcp->fc_frcn >= frcn) { X fcp->fc_frcn = FCE_EMPTY; X } X fcp++; X } X} X X/* X * Once the first fat is updated the other copies of X * the fat must also be updated. This function does X * this. X * pmp - pcfsmount structure for filesystem to update X * bp0 - addr of modified fat block X * bp1 - addr of 2nd modified fat block (0 if not needed) X * fatbn - block number relative to begin of filesystem X * of the modified fat block. X */ Xvoid Xupdateotherfats(pmp, bp0, bp1, fatbn) X struct pcfsmount *pmp; X struct buf *bp0; X struct buf *bp1; X u_long fatbn; X{ X int i; X struct buf *bpn0; X struct buf *bpn1; X X#if defined(PCFSDEBUG) Xprintf("updateotherfats(pmp %08x, bp0 %08x, bp1 %08x, fatbn %d)\n", X pmp, bp0, bp1, fatbn); X#endif /* defined(PCFSDEBUG) */ X X/* X * Now copy the block(s) of the modified fat to the other X * copies of the fat and write them out. This is faster X * than reading in the other fats and then writing them X * back out. This could tie up the fat for quite a while. X * Preventing others from accessing it. To prevent us X * from going after the fat quite so much we use delayed X * writes, unless they specfied "synchronous" when the X * filesystem was mounted. If synch is asked for then X * use bwrite()'s and really slow things down. X */ X for (i = 1; i < pmp->pm_FATs; i++) { X fatbn += pmp->pm_FATsecs; X /* getblk() never fails */ X bpn0 = getblk(pmp->pm_devvp, fatbn, pmp->pm_BytesPerSec); X bcopy(bp0->b_un.b_addr, bpn0->b_un.b_addr, X pmp->pm_BytesPerSec); X if (pmp->pm_waitonfat) X bwrite(bpn0); X else X bdwrite(bpn0); X if (bp1) { X /* getblk() never fails */ X bpn1 = getblk(pmp->pm_devvp, fatbn+1, X pmp->pm_BytesPerSec); X bcopy(bp1->b_un.b_addr, bpn1->b_un.b_addr, X pmp->pm_BytesPerSec); X if (pmp->pm_waitonfat) X bwrite(bpn1); X else X bdwrite(bpn1); X } X } X} X X/* X * Updating entries in 12 bit fats is a pain in the butt. X * So, we have a function to hide this ugliness. X * X * The following picture shows where nibbles go when X * moving from a 12 bit cluster number into the appropriate X * bytes in the FAT. X * X * byte m byte m+1 byte m+2 X * +----+----+ +----+----+ +----+----+ X * | 0 1 | | 2 3 | | 4 5 | FAT bytes X * +----+----+ +----+----+ +----+----+ X * X * +----+----+----+ +----+----+----+ X * | 3 0 1 | | 4 5 2 | X * +----+----+----+ +----+----+----+ X * cluster n cluster n+1 X * X * Where n is even. X * m = n + (n >> 2) X * X * This function is written for little endian machines. X * least significant byte stored into lowest address. X */ Xvoid Xsetfat12slot(bp0, bp1, oddcluster, byteoffset, newvalue) X struct buf *bp0; X struct buf *bp1; X int oddcluster; X u_int byteoffset; X u_short newvalue; X{ X unsigned char *b0; X unsigned char *b1; X union fattwiddle x; X X/* X * If we have a 2nd buf header and the byte offset is not the X * last byte in the buffer, then something is fishy. Better X * tell someone. X */ X if (bp1 && byteoffset != 511) X printf("setfat12slot(): bp1 %08x, byteoffset %d shouldn't happen\n", X bp1, byteoffset); X X/* X * Get address of 1st byte and 2nd byte setup X * so we don't worry about which buf header to X * be poking around in. X */ X b0 = (unsigned char *)&bp0->b_un.b_addr[byteoffset]; X if (bp1) X b1 = (unsigned char *)&bp1->b_un.b_addr[0]; X else X b1 = b0 + 1; X X/*printf("setfat12(): offset %d, old %02x%02x, new %04x\n", byteoffset, *b0, *b1, newvalue); */ X X if (oddcluster) { X x.word = newvalue << 4; X *b0 = (*b0 & 0x0f) | x.byte[0]; X *b1 = x.byte[1]; X } else { X x.word = newvalue & 0x0fff; X *b0 = x.byte[0]; X *b1 = (*b1 & 0xf0) | x.byte[1]; X } X/*printf("setfat12(): result %02x%02x\n", *b0, *b1); */ X} X Xint Xclusterfree(pmp, cluster) X struct pcfsmount *pmp; X unsigned long cluster; X{ X int error; X X error = fatentry(FAT_SET, pmp, cluster, 0, PCFSFREE); X if (error == 0) { X/* X * If the cluster was successfully marked free, then update the count of X * free clusters, and turn off the "allocated" bit in the X * "in use" cluster bit map. X */ X pmp->pm_freeclustercount++; X pmp->pm_inusemap[cluster >> 3] &= ~(1 << (cluster & 0x07)); X } X return error; X} X X/* X * Get or Set or 'Get and Set' the cluster'th entry in the X * fat. X * function - whether to get or set a fat entry X * pmp - address of the pcfsmount structure for the X * filesystem whose fat is to be manipulated. X * cluster - which cluster is of interest X * oldcontents - address of a word that is to receive X * the contents of the cluster'th entry if this is X * a get function X * newcontents - the new value to be written into the X * cluster'th element of the fat if this is a set X * function. X * X * This function can also be used to free a cluster X * by setting the fat entry for a cluster to 0. X * X * All copies of the fat are updated if this is a set X * function. X * NOTE: X * If fatentry() marks a cluster as free it does not X * update the inusemap in the pcfsmount structure. X * This is left to the caller. X */ Xint Xfatentry(function, pmp, cluster, oldcontents, newcontents) X int function; X struct pcfsmount *pmp; X u_long cluster; X u_long *oldcontents; X u_long newcontents; X{ X int error; X u_long whichbyte; X u_long whichblk; X struct buf *bp0 = 0; X struct buf *bp1 = 0; X union fattwiddle x; X/*printf("fatentry(func %d, pmp %08x, clust %d, oldcon %08x, newcon %d)\n", X function, pmp, cluster, oldcontents, newcontents);*/ X X/* X * Be sure they asked us to do something. X */ X if ((function & (FAT_SET | FAT_GET)) == 0) { X printf("fatentry(): function code doesn't specify get or set\n"); X return EINVAL; X } X X/* X * If they asked us to return a cluster number X * but didn't tell us where to put it, give them X * an error. X */ X if ((function & FAT_GET) && oldcontents == NULL) { X printf("fatentry(): get function with no place to put result\n"); X return EINVAL; X } X X/* X * Be sure the requested cluster is in the filesystem. X */ X if (cluster < CLUST_FIRST || cluster > pmp->pm_maxcluster) X return EINVAL; X X if (FAT12(pmp)) { X whichbyte = cluster + (cluster >> 1); X whichblk = (whichbyte >> pmp->pm_bnshift) + pmp->pm_fatblk; X whichbyte &= pmp->pm_brbomask; X/* X * Read in the fat block containing the entry of interest. X * If the entry spans 2 blocks, read both blocks. X */ X error = bread(pmp->pm_devvp, whichblk, X pmp->pm_BytesPerSec, NOCRED, &bp0); X if (error) { X brelse(bp0); X return error; X } X if (whichbyte == (pmp->pm_BytesPerSec-1)) { X error = bread(pmp->pm_devvp, whichblk+1, X pmp->pm_BytesPerSec, NOCRED, &bp1); X if (error) { X brelse(bp0); X return error; X } X } X if (function & FAT_GET) { X x.byte[0] = bp0->b_un.b_addr[whichbyte]; X x.byte[1] = bp1 ? bp1->b_un.b_addr[0] : X bp0->b_un.b_addr[whichbyte+1]; X if (cluster & 1) X x.word >>= 4; X x.word &= 0x0fff; X /* map certain 12 bit fat entries to 16 bit */ X if ((x.word & 0x0ff0) == 0x0ff0) X x.word |= 0xf000; X *oldcontents = x.word; X } X if (function & FAT_SET) { X setfat12slot(bp0, bp1, cluster & 1, whichbyte, X newcontents); X updateotherfats(pmp, bp0, bp1, whichblk); X X/* X * Write out the first fat last. X */ X if (pmp->pm_waitonfat) X bwrite(bp0); X else X bdwrite(bp0); X bp0 = NULL; X if (bp1) { X if (pmp->pm_waitonfat) X bwrite(bp1); X else X bdwrite(bp1); X bp1 = NULL; X } X pmp->pm_fmod++; X } X } else { /* fat16 */ X whichbyte = cluster << 1; X whichblk = (whichbyte >> pmp->pm_bnshift) + pmp->pm_fatblk; X whichbyte &= pmp->pm_brbomask; X error = bread(pmp->pm_devvp, whichblk, X pmp->pm_BytesPerSec, NOCRED, &bp0); X if (error) { X brelse(bp0); X return error; X } X if (function & FAT_GET) { X *oldcontents = *((u_short *)(bp0->b_un.b_addr + X whichbyte)); X } X if (function & FAT_SET) { X *(u_short *)(bp0->b_un.b_addr+whichbyte) = newcontents; X updateotherfats(pmp, bp0, 0, whichblk); X if (pmp->pm_waitonfat) X bwrite(bp0); /* write out blk from the 1st fat */ X else X bdwrite(bp0); X bp0 = NULL; X pmp->pm_fmod++; X } X } X if (bp0) X brelse(bp0); X if (bp1) X brelse(bp1); X return 0; X} X X/* X * Allocate a free cluster. X * pmp - X * retcluster - put the allocated cluster's number here. X * fillwith - put this value into the fat entry for the X * allocated cluster. X */ Xint Xclusteralloc(pmp, retcluster, fillwith) X struct pcfsmount *pmp; X unsigned long *retcluster; X unsigned long fillwith; X{ X int error; X u_long cn; X u_long end_cn; X X /* This for loop really needs to start from 0. */ X for (cn = 0; cn <= pmp->pm_maxcluster; cn += 8) { X if (pmp->pm_inusemap[cn >> 3] != 0xff) { X end_cn = cn | 0x07; X for (;cn <= end_cn; cn++) { X if ((pmp->pm_inusemap[cn >> 3] & (1 << (cn & 0x07))) == 0) X goto found_one; X } X printf("clusteralloc(): this shouldn't happen\n"); X } X } X return ENOSPC; X Xfound_one:; X error = fatentry(FAT_SET, pmp, cn, 0, fillwith); X if (error == 0) { X pmp->pm_inusemap[cn >> 3] |= 1 << (cn & 0x07); X pmp->pm_freeclustercount--; X pmp->pm_fmod++; X *retcluster = cn; X } X#if defined(PCFSDEBUG) Xprintf("clusteralloc(): allocated cluster %d\n", cn); X#endif /* defined(PCFSDEBUG) */ X return error; X} X X/* X * Free a chain of clusters. X * pmp - address of the pcfs mount structure for the X * filesystem containing the cluster chain to be freed. X * startcluster - number of the 1st cluster in the chain X * of clusters to be freed. X */ Xint Xfreeclusterchain(pmp, startcluster) X struct pcfsmount *pmp; X unsigned long startcluster; X{ X unsigned long nextcluster; X int error = 0; X X while (startcluster >= CLUST_FIRST && startcluster <= pmp->pm_maxcluster) { X error = fatentry(FAT_GET_AND_SET, pmp, startcluster, X &nextcluster, PCFSFREE); X if (error) { X printf("freeclusterchain(): free failed, cluster %d\n", X startcluster); X break; X } X/* X * If the cluster was successfully marked free, then update the count of X * free clusters, and turn off the "allocated" bit in the X * "in use" cluster bit map. X */ X pmp->pm_freeclustercount++; X pmp->pm_inusemap[startcluster >> 3] &= X ~(1 << (startcluster & 0x07)); X startcluster = nextcluster; X pmp->pm_fmod++; X } X return error; X} X X/* X * Read in fat blocks looking for free clusters. X * For every free cluster found turn off its X * corresponding bit in the pm_inusemap. X */ Xint Xfillinusemap(pmp) X struct pcfsmount *pmp; X{ X struct buf *bp0 = 0; X daddr_t bp0_blk = -1; X struct buf *bp1 = 0; X daddr_t cn; X daddr_t whichblk; X int whichbyte; X int error; X union fattwiddle x; X X/* X * Mark all clusters in use, we mark the free ones in the X * fat scan loop further down. X */ X for (cn = 0; cn < (pmp->pm_maxcluster >> 3) + 1; cn++) X pmp->pm_inusemap[cn] = 0xff; X X/* X * Figure how many free clusters are in the filesystem X * by ripping thougth the fat counting the number of X * entries whose content is zero. These represent free X * clusters. X */ X pmp->pm_freeclustercount = 0; X pmp->pm_lookhere = -1; X for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) { X if (FAT12(pmp)) { X whichbyte = cn + (cn >> 1); X whichblk = (whichbyte >> pmp->pm_bnshift) + pmp->pm_fatblk; X whichbyte = whichbyte & pmp->pm_brbomask; X if (whichblk != bp0_blk) { X if (bp0) X brelse(bp0); X error = bread(pmp->pm_devvp, whichblk, X pmp->pm_BytesPerSec, NOCRED, &bp0); X if (error) { X goto error_exit; X } X bp0_blk = whichblk; X } X x.byte[0] = bp0->b_un.b_addr[whichbyte]; X if (whichbyte == (pmp->pm_BytesPerSec-1)) { X error = bread(pmp->pm_devvp, whichblk+1, X pmp->pm_BytesPerSec, NOCRED, &bp1); X if (error) X goto error_exit; X x.byte[1] = bp1->b_un.b_addr[0]; X brelse(bp0); X bp0 = bp1; X bp1 = NULL; X bp0_blk++; X } else { X x.byte[1] = bp0->b_un.b_addr[whichbyte + 1]; X } X if (cn & 1) X x.word >>= 4; X x.word &= 0x0fff; X } else { /* 16 bit fat */ X whichbyte = cn << 1; X whichblk = (whichbyte >> pmp->pm_bnshift) + pmp->pm_fatblk; X whichbyte = whichbyte & pmp->pm_brbomask; X if (whichblk != bp0_blk) { X if (bp0) X brelse(bp0); X error = bread(pmp->pm_devvp, whichblk, X pmp->pm_BytesPerSec, NOCRED, &bp0); X if (error) X goto error_exit; X bp0_blk = whichblk; X } X x.byte[0] = bp0->b_un.b_addr[whichbyte]; X x.byte[1] = bp0->b_un.b_addr[whichbyte+1]; X } X if (x.word == 0) { X pmp->pm_freeclustercount++; X pmp->pm_inusemap[cn >> 3] &= ~(1 << (cn & 0x07)); X if (pmp->pm_lookhere < 0) X pmp->pm_lookhere = cn; X } X } X brelse(bp0); X return 0; X Xerror_exit:; X if (bp0) X brelse(bp0); X if (bp1) X brelse(bp1); X return error; X} X X/* X * Allocate a new cluster and chain it onto the end of the X * file. X * dep - the file to extend X * bpp - where to return the address of the buf header for the X * new file block X * ncp - where to put cluster number of the newly allocated file block X * If this pointer is 0, do not return the cluster number. X * X * NOTE: X * This function is not responsible for turning on the DEUPD X * bit if the de_flag field of the denode and it does not X * change the de_FileSize field. This is left for the caller X * to do. X */ Xint Xextendfile(dep, bpp, ncp) X struct denode *dep; X struct buf **bpp; X unsigned int *ncp; X{ X int error = 0; X unsigned long frcn; X unsigned long cn; X struct pcfsmount *pmp = dep->de_pmp; X X/* X * Don't try to extend the root directory X */ X if (DETOV(dep)->v_flag & VROOT) { X printf("extendfile(): attempt to extend root directory\n"); X return ENOSPC; X } X X/* X * If the "file's last cluster" cache entry is empty, X * and the file is not empty, X * then fill the cache entry by calling pcbmap(). X */ X fc_fileextends++; X if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY && X dep->de_StartCluster != 0) { X fc_lfcempty++; X error = pcbmap(dep, 0xffff, 0, &cn); X /* we expect it to return E2BIG */ X if (error != E2BIG) X return error; X error = 0; X } X X/* X * Allocate another cluster and chain onto the end of the file. X * If the file is empty we make de_StartCluster point to the X * new block. Note that de_StartCluster being 0 is sufficient X * to be sure the file is empty since we exclude attempts to X * extend the root directory above, and the root dir is the X * only file with a startcluster of 0 that has blocks allocated X * (sort of). X */ X if (error = clusteralloc(pmp, &cn, CLUST_EOFE)) X return error; X if (dep->de_StartCluster == 0) { X dep->de_StartCluster = cn; X frcn = 0; X } else { X error = fatentry(FAT_SET, pmp, dep->de_fc[FC_LASTFC].fc_fsrcn, X 0, cn); X if (error) { X clusterfree(pmp, cn); X return error; X } X X frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1; X } X X/* X * Update the "last cluster of the file" entry in the denode's X * fat cache. X */ X fc_setcache(dep, FC_LASTFC, frcn, cn); X X/* X * Get the buf header for the new block of the file. X */ X if (dep->de_Attributes & ATTR_DIRECTORY) { X *bpp = getblk(pmp->pm_devvp, cntobn(pmp, cn), X pmp->pm_bpcluster); X } else { X *bpp = getblk(DETOV(dep), frcn, X pmp->pm_bpcluster); X } X clrbuf(*bpp); X X/* X * Give them the filesystem relative cluster number X * if they want it. X */ X if (ncp) X *ncp = cn; X return 0; X} END-of-/sys/pcfs/pcfs_fat.c exit