349 lines
7.2 KiB
C
349 lines
7.2 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)pc_alloc.c 1.1 94/10/31 SMI";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* Routines to allocate and deallocate data blocks on the disk
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/time.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/vnode.h>
|
|
#include <pcfs/pc_label.h>
|
|
#include <pcfs/pc_fs.h>
|
|
#include <pcfs/pc_dir.h>
|
|
#include <pcfs/pc_node.h>
|
|
|
|
/*
|
|
* internal routines
|
|
*/
|
|
static short pc_getcluster(); /* get the next cluster number */
|
|
|
|
/*
|
|
* Convert file logical block (cluster) numbers to disk block numbers.
|
|
* Also return read ahead block if asked for.
|
|
* Used for reading only. Use pc_balloc for writing.
|
|
*/
|
|
int
|
|
pc_bmap(pcp, lcn, dbnp, rabnp)
|
|
register struct pcnode *pcp; /* pcnode for file */
|
|
register daddr_t lcn; /* logical cluster no */
|
|
daddr_t *dbnp; /* ptr to phys block no */
|
|
daddr_t *rabnp; /* ptr to read ahead phys block */
|
|
/* may be zero if not wanted */
|
|
{
|
|
register struct pcfs *fsp; /* pcfs that file is in */
|
|
struct vnode * vp;
|
|
|
|
PCFSDEBUG(7)
|
|
printf("pc_bmap: pcp=0x%x, lcn=%d\n", pcp, lcn);
|
|
|
|
vp = PCTOV(pcp);
|
|
fsp = VFSTOPCFS(vp->v_vfsp);
|
|
if (lcn < 0)
|
|
return (ENOENT);
|
|
if (vp->v_flag & VROOT) {
|
|
register daddr_t lbn; /* logical (disk) block number */
|
|
|
|
lbn = pc_cltodb(fsp, lcn);
|
|
if (lbn >= fsp->pcfs_rdirsec)
|
|
return (ENOENT);
|
|
*dbnp = fsp->pcfs_rdirstart + lbn;
|
|
if (rabnp != (daddr_t *)0) {
|
|
if ((lbn + 1) < fsp->pcfs_rdirsec)
|
|
*rabnp = *dbnp + 1;
|
|
else
|
|
*rabnp = (daddr_t)0;
|
|
}
|
|
} else {
|
|
register short cn; /* current cluster number */
|
|
register short ncn; /* next cluster number */
|
|
|
|
if (lcn >= fsp->pcfs_ncluster)
|
|
return (ENOENT);
|
|
if (vp->v_type == VREG &&
|
|
(pcp->pc_size == 0 ||
|
|
lcn >= howmany(pcp->pc_size, fsp->pcfs_clsize))) {
|
|
return (ENOENT);
|
|
}
|
|
ncn = pcp->pc_scluster;
|
|
do {
|
|
cn = ncn;
|
|
if (!pc_validcl(fsp, cn)) {
|
|
if (cn >= PCF_LASTCLUSTER &&
|
|
vp->v_type == VDIR) {
|
|
return (ENOENT);
|
|
} else {
|
|
pc_badfs(fsp);
|
|
return (EIO);
|
|
}
|
|
}
|
|
ncn = pc_getcluster(fsp, cn);
|
|
} while (--lcn >= 0);
|
|
*dbnp = pc_cldaddr(fsp, cn);
|
|
if (rabnp != (daddr_t *)0) {
|
|
if (ncn >= PCF_LASTCLUSTER) {
|
|
*rabnp = (daddr_t)0;
|
|
} else {
|
|
if (!pc_validcl(fsp, ncn)) {
|
|
pc_badfs(fsp);
|
|
return (EIO);
|
|
}
|
|
*rabnp = pc_cldaddr(fsp, ncn);
|
|
}
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Allocate file logical blocks (clusters).
|
|
* Return disk address of corresponidng cluster.
|
|
*/
|
|
int
|
|
pc_balloc(pcp, lcn, dbnp)
|
|
register struct pcnode *pcp; /* pcnode for file */
|
|
register daddr_t lcn; /* logical cluster no */
|
|
daddr_t *dbnp; /* ptr to phys block no */
|
|
{
|
|
register struct pcfs *fsp; /* pcfs that file is in */
|
|
struct vnode * vp;
|
|
|
|
PCFSDEBUG(7)
|
|
printf("pc_balloc: pcp=0x%x, lcn=%d\n", pcp, lcn);
|
|
|
|
vp = PCTOV(pcp);
|
|
fsp = VFSTOPCFS(vp -> v_vfsp);
|
|
|
|
if (lcn < 0) {
|
|
return (EFBIG);
|
|
}
|
|
|
|
if (vp->v_flag & VROOT) {
|
|
register daddr_t lbn;
|
|
|
|
lbn = pc_cltodb(fsp, lcn);
|
|
if (lbn >= fsp->pcfs_rdirsec)
|
|
return (ENOSPC);
|
|
*dbnp = fsp->pcfs_rdirstart + lbn;
|
|
} else {
|
|
register short cn; /* current cluster number */
|
|
register short ncn; /* next cluster number */
|
|
|
|
if (lcn >= fsp->pcfs_ncluster)
|
|
return (ENOSPC);
|
|
if ((vp->v_type == VREG && pcp->pc_size == 0) ||
|
|
(vp->v_type == VDIR && lcn == 0)) {
|
|
switch (cn = pc_alloccluster(fsp)) {
|
|
case PCF_FREECLUSTER:
|
|
return (ENOSPC);
|
|
case PCF_ERRORCLUSTER:
|
|
return (EIO);
|
|
}
|
|
pcp->pc_scluster = cn;
|
|
} else {
|
|
cn = pcp->pc_scluster;
|
|
if (!pc_validcl(fsp, cn)) {
|
|
pc_badfs(fsp);
|
|
return (EIO);
|
|
}
|
|
}
|
|
while (lcn-- > 0) {
|
|
ncn = pc_getcluster(fsp, cn);
|
|
if (ncn >= PCF_LASTCLUSTER) {
|
|
/*
|
|
* Extend file (no holes).
|
|
*/
|
|
switch (ncn = pc_alloccluster(fsp)) {
|
|
case PCF_FREECLUSTER:
|
|
return (ENOSPC);
|
|
case PCF_ERRORCLUSTER:
|
|
return (EIO);
|
|
}
|
|
pc_setcluster(fsp, cn, ncn);
|
|
} else if (!pc_validcl(fsp, ncn)) {
|
|
pc_badfs(fsp);
|
|
return (EIO);
|
|
}
|
|
cn = ncn;
|
|
}
|
|
*dbnp = pc_cldaddr(fsp, cn);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Free file cluster chain after the first skipcl clusters.
|
|
*/
|
|
int
|
|
pc_bfree(pcp, skipcl)
|
|
struct pcnode *pcp;
|
|
register short skipcl;
|
|
{
|
|
register struct pcfs *fsp;
|
|
register short cn;
|
|
register short ncn;
|
|
register int n;
|
|
struct vnode * vp;
|
|
|
|
vp = PCTOV(pcp);
|
|
if (vp -> v_flag & VROOT) {
|
|
panic("pc_bfree");
|
|
}
|
|
|
|
PCFSDEBUG(7)
|
|
printf("pc_bfree: pcp=0x%x, after first %d clusters\n", pcp, skipcl);
|
|
|
|
if (pcp -> pc_size == 0 && vp -> v_type == VREG) {
|
|
return (0);
|
|
}
|
|
fsp = VFSTOPCFS(vp -> v_vfsp);
|
|
if (vp -> v_type == VREG) {
|
|
n = howmany(pcp->pc_size, fsp->pcfs_clsize);
|
|
if (n > fsp->pcfs_ncluster) {
|
|
pc_badfs(fsp);
|
|
return (EIO);
|
|
}
|
|
} else {
|
|
n = fsp->pcfs_ncluster;
|
|
}
|
|
cn = pcp->pc_scluster;
|
|
if (skipcl == 0) {
|
|
pcp->pc_scluster = PCF_LASTCLUSTER;
|
|
}
|
|
while (n--) {
|
|
if (!pc_validcl(fsp, cn)) {
|
|
pc_badfs(fsp);
|
|
return (EIO);
|
|
}
|
|
ncn = pc_getcluster(fsp, cn);
|
|
if (skipcl == 0)
|
|
pc_setcluster(fsp, cn, PCF_FREECLUSTER);
|
|
else
|
|
skipcl--;
|
|
if (ncn >= PCF_LASTCLUSTER && vp -> v_type == VDIR)
|
|
break;
|
|
cn = ncn;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Return the number of free blocks in the filesystem.
|
|
*/
|
|
int
|
|
pc_freeclusters(fsp)
|
|
register struct pcfs *fsp;
|
|
{
|
|
register short cn;
|
|
register int free;
|
|
|
|
/*
|
|
* make sure the fat is in core
|
|
*/
|
|
free = 0;
|
|
for (cn = PCF_FIRSTCLUSTER;
|
|
cn < fsp->pcfs_ncluster + PCF_FIRSTCLUSTER; cn++) {
|
|
if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
|
|
free++;
|
|
}
|
|
}
|
|
return (free);
|
|
}
|
|
|
|
/*
|
|
* Cluster manipulation routines.
|
|
* Fat must be resident.
|
|
*/
|
|
|
|
/*
|
|
* Get the next cluster in the file cluster chain.
|
|
*/
|
|
static short
|
|
pc_getcluster(fsp, cn)
|
|
register struct pcfs *fsp;
|
|
register short cn; /* current cluster number in chain */
|
|
{
|
|
register unsigned char *fp;
|
|
|
|
if (fsp->pcfs_fatp == (u_char *)0 || !pc_validcl(fsp, cn))
|
|
panic("pc_getcluster");
|
|
fp = fsp->pcfs_fatp + (cn + (cn >> 1));
|
|
if (cn & 01) {
|
|
cn = ((*fp++ & 0xf0) >> 4);
|
|
cn += (*fp << 4);
|
|
} else {
|
|
cn = *fp++;
|
|
cn += ((*fp & 0x0f) << 8);
|
|
}
|
|
return (cn);
|
|
}
|
|
|
|
/*
|
|
* Set a cluster in the fat to a value.
|
|
*/
|
|
void
|
|
pc_setcluster(fsp, cn, ncn)
|
|
register struct pcfs *fsp;
|
|
register short cn; /* fat cluster no to be set */
|
|
register short ncn; /* new value */
|
|
{
|
|
register unsigned char *fp;
|
|
|
|
if (fsp->pcfs_fatp == (u_char *)0 || !pc_validcl(fsp, cn))
|
|
panic("pc_setcluster");
|
|
fsp->pcfs_flags |= PCFS_FATMOD;
|
|
fp = fsp->pcfs_fatp + (cn + (cn >> 1));
|
|
if (cn & 01) {
|
|
*fp = (*fp & 0x0f) | ((ncn << 4) & 0xf0);
|
|
fp++;
|
|
*fp = (ncn >> 4) & 0xff;
|
|
} else {
|
|
*fp++ = ncn & 0xff;
|
|
*fp = (*fp & 0xf0) | ((ncn >> 8) & 0x0f);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Allocate a new cluster.
|
|
*/
|
|
short
|
|
pc_alloccluster(fsp)
|
|
register struct pcfs *fsp; /* file sys to allocate in */
|
|
{
|
|
register short cn;
|
|
|
|
if (fsp->pcfs_fatp == (u_char *)0)
|
|
panic("pc_addcluster: no fat");
|
|
for (cn = PCF_FIRSTCLUSTER;
|
|
cn < fsp->pcfs_ncluster + PCF_FIRSTCLUSTER; cn++) {
|
|
if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
|
|
register struct buf *bp;
|
|
|
|
pc_setcluster(fsp, cn, PCF_LASTCLUSTER);
|
|
/*
|
|
* zero the new cluster
|
|
*/
|
|
bp = getblk(fsp->pcfs_devvp,
|
|
pc_cldaddr(fsp, cn), fsp->pcfs_clsize);
|
|
clrbuf(bp);
|
|
bwrite(bp);
|
|
if (bp -> b_flags & B_ERROR) {
|
|
pc_diskchanged(fsp);
|
|
return (PCF_ERRORCLUSTER);
|
|
}
|
|
PCFSDEBUG(7)
|
|
printf("pc_alloccluster: new cluster = %d\n", cn);
|
|
return (cn);
|
|
}
|
|
}
|
|
return (PCF_FREECLUSTER);
|
|
}
|