Add UFS2 support.

This commit is contained in:
rsc 2005-08-11 16:45:39 +00:00
parent 8ecefa411d
commit e060bc5227
3 changed files with 219 additions and 103 deletions

View file

@ -6,6 +6,8 @@
#include <diskfs.h> #include <diskfs.h>
#include "ffs.h" #include "ffs.h"
#define BADBNO ((u64int)~0ULL)
#define checkcg 0 #define checkcg 0
#define debug 0 #define debug 0
@ -57,7 +59,7 @@ error:
} }
static Cgblk* static Cgblk*
ffscylgrp(Ffs *fs, int i, Block **pb) ffscylgrp(Ffs *fs, u32int i, Block **pb)
{ {
Block *b; Block *b;
Cgblk *cg; Cgblk *cg;
@ -82,6 +84,7 @@ static int
ffssync(Fsys *fsys) ffssync(Fsys *fsys)
{ {
int i; int i;
int off[] = { SBOFF, SBOFF2, SBOFFPIGGY };
Block *b, *cgb; Block *b, *cgb;
Cgblk *cgblk; Cgblk *cgblk;
Cylgrp *cg; Cylgrp *cg;
@ -95,12 +98,18 @@ ffssync(Fsys *fsys)
/* /*
* Read super block. * Read super block.
*/ */
if((b = diskread(disk, SBSIZE, SBOFF)) == nil) for(i=0; i<nelem(off); i++){
goto error; if((b = diskread(disk, SBSIZE, off[i])) == nil)
fsblk = (Fsblk*)b->data; goto error;
if(checkfsblk(fsblk) < 0) fsblk = (Fsblk*)b->data;
goto error; fprint(2, "offset of magic: %d\n", offsetof(Fsblk, magic));
if((fs->ufs = checkfsblk(fsblk)) > 0)
goto okay;
blockput(b);
}
goto error;
okay:
fs->blocksize = fsblk->blocksize; fs->blocksize = fsblk->blocksize;
fs->nblock = (fsblk->nfrag+fsblk->fragsperblock-1) / fsblk->fragsperblock; fs->nblock = (fsblk->nfrag+fsblk->fragsperblock-1) / fsblk->fragsperblock;
fs->fragsize = fsblk->fragsize; fs->fragsize = fsblk->fragsize;
@ -111,21 +120,31 @@ ffssync(Fsys *fsys)
fs->nfrag = fsblk->nfrag; fs->nfrag = fsblk->nfrag;
fs->ndfrag = fsblk->ndfrag; fs->ndfrag = fsblk->ndfrag;
fs->blockspergroup = (u64int)fsblk->cylspergroup * /*
fsblk->secspercyl * BYTESPERSEC / fsblk->blocksize; * used to use
* fs->blockspergroup = (u64int)fsblk->_cylspergroup *
* fsblk->secspercyl * BYTESPERSEC / fsblk->blocksize;
* for UFS1, but this should work for both UFS1 and UFS2
*/
fs->blockspergroup = (u64int)fsblk->fragspergroup / fsblk->fragsperblock;
fs->ncg = fsblk->ncg; fs->ncg = fsblk->ncg;
fsys->blocksize = fs->blocksize; fsys->blocksize = fs->blocksize;
fsys->nblock = fs->nblock; fsys->nblock = fs->nblock;
if(0) fprint(2, "ffs %d %d-byte blocks, %d cylinder groups\n", if(debug) fprint(2, "ffs %lld %d-byte blocks, %d cylinder groups\n",
fs->nblock, fs->blocksize, fs->ncg); fs->nblock, fs->blocksize, fs->ncg);
if(debug) fprint(2, "\tinospergroup %d perblock %d blockspergroup %lld\n",
fs->inospergroup, fs->inosperblock, fs->blockspergroup);
if(fs->cg == nil) if(fs->cg == nil)
fs->cg = emalloc(fs->ncg*sizeof(Cylgrp)); fs->cg = emalloc(fs->ncg*sizeof(Cylgrp));
for(i=0; i<fs->ncg; i++){ for(i=0; i<fs->ncg; i++){
cg = &fs->cg[i]; cg = &fs->cg[i];
cg->bno = fs->blockspergroup*i + fsblk->cgoffset * (i & ~fsblk->cgmask); if(fs->ufs == 2)
cg->bno = (u64int)fs->blockspergroup*i;
else
cg->bno = fs->blockspergroup*i + fsblk->_cgoffset * (i & ~fsblk->_cgmask);
cg->cgblkno = cg->bno + fsblk->cfragno/fs->fragsperblock; cg->cgblkno = cg->bno + fsblk->cfragno/fs->fragsperblock;
cg->ibno = cg->bno + fsblk->ifragno/fs->fragsperblock; cg->ibno = cg->bno + fsblk->ifragno/fs->fragsperblock;
cg->dbno = cg->bno + fsblk->dfragno/fs->fragsperblock; cg->dbno = cg->bno + fsblk->dfragno/fs->fragsperblock;
@ -172,12 +191,20 @@ ffsclose(Fsys *fsys)
static int static int
checkfsblk(Fsblk *super) checkfsblk(Fsblk *super)
{ {
if(super->magic != FSMAGIC){ fprint(2, "ffs magic 0x%ux\n", super->magic);
werrstr("bad super block"); if(super->magic == FSMAGIC){
return -1; super->time = super->_time;
super->nfrag = super->_nfrag;
super->ndfrag = super->_ndfrag;
super->flags = super->_flags;
return 1;
}
if(super->magic == FSMAGIC2){
return 2;
} }
return 0; werrstr("bad super block");
return -1;
} }
static int static int
@ -198,11 +225,10 @@ int nskipx;
static Block* static Block*
ffsblockread(Fsys *fsys, u64int bno) ffsblockread(Fsys *fsys, u64int bno)
{ {
u32int i, o; int i, o;
u8int *fmap; u8int *fmap;
int frag, fsize, avail; int frag, fsize, avail;
Block *b; Block *b;
// Cylgrp *cg;
Cgblk *cgblk; Cgblk *cgblk;
Ffs *fs; Ffs *fs;
@ -211,10 +237,6 @@ ffsblockread(Fsys *fsys, u64int bno)
o = bno % fs->blockspergroup; o = bno % fs->blockspergroup;
if(i >= fs->ncg) if(i >= fs->ncg)
return nil; return nil;
// cg = &fs->cg[i];
// if(o >= cg->nblock)
// return nil;
if((cgblk = ffscylgrp(fs, i, &b)) == nil) if((cgblk = ffscylgrp(fs, i, &b)) == nil)
return nil; return nil;
@ -257,7 +279,7 @@ nskipx++;
} }
static Block* static Block*
ffsdatablock(Ffs *fs, u32int bno, int size) ffsdatablock(Ffs *fs, u64int bno, int size)
{ {
int fsize; int fsize;
u64int diskaddr; u64int diskaddr;
@ -290,25 +312,26 @@ ffsdatablock(Ffs *fs, u32int bno, int size)
return b; return b;
} }
static u32int static u64int
ifetch(Ffs *fs, u32int bno, u32int off) ifetch(Ffs *fs, u64int bno, u32int off)
{ {
u32int *a;
Block *b; Block *b;
if(bno == ~0) if(bno == BADBNO)
return ~0; return BADBNO;
b = ffsdatablock(fs, bno, fs->blocksize); b = ffsdatablock(fs, bno, fs->blocksize);
if(b == nil) if(b == nil)
return ~0; return BADBNO;
a = (u32int*)b->data; if(fs->ufs == 2)
bno = a[off]; bno = ((u64int*)b->data)[off];
else
bno = ((u32int*)b->data)[off];
blockput(b); blockput(b);
return bno; return bno;
} }
static u32int static u64int
ffsfileblockno(Ffs *fs, Inode *ino, u32int bno) ffsfileblockno(Ffs *fs, Inode *ino, u64int bno)
{ {
int ppb; int ppb;
@ -329,16 +352,15 @@ ffsfileblockno(Ffs *fs, Inode *ino, u32int bno)
if(bno/ppb/ppb/ppb == 0) /* bno < ppb*ppb*ppb w/o overflow */ if(bno/ppb/ppb/ppb == 0) /* bno < ppb*ppb*ppb w/o overflow */
return ifetch(fs, ifetch(fs, ifetch(fs, ino->ib[2], bno/ppb/ppb), (bno/ppb)%ppb), bno%ppb); return ifetch(fs, ifetch(fs, ifetch(fs, ino->ib[2], bno/ppb/ppb), (bno/ppb)%ppb), bno%ppb);
bno -= ppb*ppb*ppb;
fprint(2, "ffsfileblock %lud: way too big\n", (ulong)bno+NDADDR); fprint(2, "ffsfileblock %llud: way too big\n", bno+NDADDR+ppb+ppb*ppb);
return ~0; return BADBNO;
} }
static Block* static Block*
ffsfileblock(Ffs *fs, Inode *ino, u32int bno, int size) ffsfileblock(Ffs *fs, Inode *ino, u64int bno, int size)
{ {
u32int b; u64int b;
b = ffsfileblockno(fs, ino, bno); b = ffsfileblockno(fs, ino, bno);
if(b == ~0) if(b == ~0)
@ -365,7 +387,33 @@ byte2u32(uchar *p)
return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
} }
static u64int iaddr; static u64int lastiaddr; /* debugging */
static void
inode1to2(Inode1 *i1, Inode *i2)
{
int i;
memset(i2, 0, sizeof *i2);
i2->mode = i1->mode;
i2->nlink = i1->nlink;
i2->size = i1->size;
i2->atime = i1->atime;
i2->atimensec = i1->atimensec;
i2->mtime = i1->mtime;
i2->mtimensec = i1->mtimensec;
i2->ctime = i1->ctime;
i2->ctimensec = i1->ctimensec;
for(i=0; i<NDADDR; i++)
i2->db[i] = i1->db[i];
for(i=0; i<NIADDR; i++)
i2->ib[i] = i1->ib[i];
i2->flags = i1->flags;
i2->nblock = i1->nblock;
i2->gen = i1->gen;
i2->uid = i1->uid;
i2->gid = i1->gid;
}
static Nfs3Status static Nfs3Status
handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino) handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
@ -373,8 +421,10 @@ handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
int i; int i;
u32int ioff; u32int ioff;
u32int inum; u32int inum;
u64int iaddr;
Block *b; Block *b;
Cylgrp *cg; Cylgrp *cg;
Inode1 ino1;
if(h->len != 4) if(h->len != 4)
return Nfs3ErrBadHandle; return Nfs3ErrBadHandle;
@ -390,20 +440,21 @@ handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
if(i >= fs->ncg) if(i >= fs->ncg)
return Nfs3ErrBadHandle; return Nfs3ErrBadHandle;
cg = &fs->cg[i]; cg = &fs->cg[i];
/*
if(ioff >= cg->nino)
return Nfs3ErrBadHandle;
*/
if(debug) print("cg->ibno %d...", cg->ibno); if(debug) print("cg->ibno %lld ufs %d...", cg->ibno, fs->ufs);
if((b = diskread(fs->disk, fs->blocksize, iaddr = (cg->ibno+ioff/fs->inosperblock)*(vlong)fs->blocksize;
(cg->ibno+ioff/fs->inosperblock)*(vlong)fs->blocksize)) == nil) ioff = ioff%fs->inosperblock;
if((b = diskread(fs->disk, fs->blocksize, iaddr)) == nil)
return Nfs3ErrIo; return Nfs3ErrIo;
iaddr = (cg->ibno+ioff/fs->inosperblock)*(vlong)fs->blocksize if(fs->ufs == 2){
+ (ioff%fs->inosperblock)*sizeof(Inode); *ino = ((Inode*)b->data)[ioff];
*ino = ((Inode*)b->data)[ioff%fs->inosperblock]; lastiaddr = iaddr+ioff*sizeof(Inode);
}else{
ino1 = ((Inode1*)b->data)[ioff];
inode1to2(&ino1, ino);
lastiaddr = iaddr+ioff*sizeof(Inode1);
}
blockput(b); blockput(b);
return Nfs3Ok; return Nfs3Ok;
} }
@ -717,7 +768,7 @@ ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cook
static u64int static u64int
ffsxfileblock(Fsys *fsys, Nfs3Handle *h, u64int offset) ffsxfileblock(Fsys *fsys, Nfs3Handle *h, u64int offset)
{ {
u32int bno; u64int bno;
Inode ino; Inode ino;
Nfs3Status ok; Nfs3Status ok;
Ffs *fs; Ffs *fs;
@ -727,8 +778,8 @@ ffsxfileblock(Fsys *fsys, Nfs3Handle *h, u64int offset)
nfs3errstr(ok); nfs3errstr(ok);
return 0; return 0;
} }
if(offset == 1) if(offset == 1) /* clumsy hack for debugging */
return iaddr; return lastiaddr;
if(offset >= ino.size){ if(offset >= ino.size){
werrstr("beyond end of file"); werrstr("beyond end of file");
return 0; return 0;
@ -820,7 +871,7 @@ ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
len = ino.size; len = ino.size;
if(ino.nblock != 0){ if(ino.nblock != 0){
/* BUG: assumes symlink fits in one block */ /* assumes symlink fits in one block */
b = ffsfileblock(fs, &ino, 0, len); b = ffsfileblock(fs, &ino, 0, len);
if(b == nil) if(b == nil)
return Nfs3ErrIo; return Nfs3ErrIo;

View file

@ -19,8 +19,10 @@
typedef struct Cgblk Cgblk; typedef struct Cgblk Cgblk;
typedef struct Cylgrp Cylgrp; typedef struct Cylgrp Cylgrp;
typedef struct Cylsum Cylsum; typedef struct Cylsum Cylsum;
typedef struct Cylsumtotal Cylsumtotal;
typedef struct Ffs Ffs; typedef struct Ffs Ffs;
typedef struct Fsblk Fsblk; typedef struct Fsblk Fsblk;
typedef struct Inode1 Inode1;
typedef struct Inode Inode; typedef struct Inode Inode;
typedef struct Dirent Dirent; typedef struct Dirent Dirent;
@ -30,9 +32,13 @@ enum
/* constants for Fsblk */ /* constants for Fsblk */
FSMAXMNTLEN = 512, FSMAXMNTLEN = 512,
FSNOCSPTRS = 128 / sizeof(void*) - 3, FSMAXMNTLEN2 = 468,
FSMAXVOLLEN = 32, /* UFS2 */
FSNOCSPTRSLEN = 128-12,
FSNOCSPTRSLEN2 = 128-16, /* UFS2 */
FSMAXSNAP = 20, FSMAXSNAP = 20,
FSMAGIC = 0x011954, FSMAGIC = 0x011954,
FSMAGIC2 = 0x19540119,
FSCHECKSUM = 0x7c269d38, FSCHECKSUM = 0x7c269d38,
/* Fsblk.inodefmt */ /* Fsblk.inodefmt */
@ -45,6 +51,8 @@ enum
/* offset and size of first super block */ /* offset and size of first super block */
SBOFF = BBOFF+BBSIZE, SBOFF = BBOFF+BBSIZE,
SBOFF2 = BBOFF+65536, /* UFS2 */
SBOFFPIGGY = BBOFF+262144, /* UFS2 */
SBSIZE = 8192, SBSIZE = 8192,
/* minimum block size */ /* minimum block size */
@ -60,6 +68,7 @@ enum
ROOTINODE = 2, ROOTINODE = 2,
WHITEOUT = 1, WHITEOUT = 1,
NXADDR = 2, /* UFS2 */
NDADDR = 12, NDADDR = 12,
NIADDR = 3, NIADDR = 3,
@ -102,6 +111,17 @@ struct Cylsum
u32int nffree; u32int nffree;
}; };
struct Cylsumtotal
{
u64int ndir;
u64int nbfree;
u64int nifree;
u64int nffree;
u64int numclusters;
u64int unused[3];
};
/* Fields beginning with underscore are deprecated in UFS2 */
struct Fsblk struct Fsblk
{ {
u32int unused0; u32int unused0;
@ -110,11 +130,11 @@ struct Fsblk
daddr_t cfragno; /* fragment address if cylinder block in file system */ daddr_t cfragno; /* fragment address if cylinder block in file system */
daddr_t ifragno; /* fragment offset of inode blocks in file system */ daddr_t ifragno; /* fragment offset of inode blocks in file system */
daddr_t dfragno; /* fragment offset of data blocks in cg */ daddr_t dfragno; /* fragment offset of data blocks in cg */
u32int cgoffset; /* block (maybe fragment?) offset of Cgblk in cylinder */ u32int _cgoffset; /* block (maybe fragment?) offset of Cgblk in cylinder */
u32int cgmask; u32int _cgmask;
time_t time; time_t _time;
u32int nfrag; /* number of blocks in fs * fragsperblock */ u32int _nfrag; /* number of blocks in fs * fragsperblock */
u32int ndfrag; u32int _ndfrag;
u32int ncg; /* number of cylinder groups in fs */ u32int ncg; /* number of cylinder groups in fs */
u32int blocksize; /* block size in fs */ u32int blocksize; /* block size in fs */
u32int fragsize; /* frag size in fs */ u32int fragsize; /* frag size in fs */
@ -130,58 +150,73 @@ struct Fsblk
u32int maxbpg; u32int maxbpg;
u32int fragshift; u32int fragshift;
u32int fsbtodbshift; u32int fsbtodbshift;
u32int sbsize; /* size of super block */ u32int sbsize; /* size of super block */
u32int unused2; /* more stuff we don't use ... */ u32int unused2; /* more stuff we don't use ... */
u32int unused3; u32int unused3;
u32int nindir; u32int nindir;
u32int inosperblock; /* inodes per block */ u32int inosperblock; /* inodes per block */
u32int nspf; u32int _nspf;
u32int optim; u32int optim;
u32int npsect; u32int _npsect;
u32int interleave; u32int _interleave;
u32int trackskew; u32int _trackskew;
u32int id[2]; u32int id[2];
daddr_t csaddr; /* blk addr of cyl grp summary area */ daddr_t _csaddr; /* blk addr of cyl grp summary area */
u32int cssize; /* size of cyl grp summary area */ u32int cssize; /* size of cyl grp summary area */
u32int cgsize; /* cylinder group size */ u32int cgsize; /* cylinder group size */
u32int trackspercyl; /* tracks per cylinder */ u32int _trackspercyl; /* tracks per cylinder */
u32int secspertrack; /* sectors per track */ u32int _secspertrack; /* sectors per track */
u32int secspercyl; /* sectors per cylinder */ u32int _secspercyl; /* sectors per cylinder */
u32int ncyl; /* cylinders in fs */ u32int _ncyl; /* cylinders in fs */
u32int cylspergroup; /* cylinders per group */ u32int _cylspergroup; /* cylinders per group */
u32int inospergroup; /* inodes per group */ u32int inospergroup; /* inodes per group */
u32int fragspergroup; /* data blocks per group * fragperblock */ u32int fragspergroup; /* data blocks per group * fragperblock */
Cylsum cstotal; /* more unused... */ Cylsum _cstotal; /* more unused... */
u8int fmod; u8int fmod;
u8int clean; u8int clean;
u8int ronly; u8int ronly;
u8int flags; u8int _flags;
char fsmnt[FSMAXMNTLEN]; /* char fsmnt[512]; in UFS1 */
char fsmnt[FSMAXMNTLEN2];
char volname[FSMAXVOLLEN];
u64int swuid;
u32int pad;
u32int cgrotor; u32int cgrotor;
void* ocsp[FSNOCSPTRS]; uchar ocsp[FSNOCSPTRSLEN]; /* last 4 bytes is contigdirs in UFS2 */
u8int* contigdirs; u32int contigdirs; /* csp in UFS2 */
Cylsum* csp; u32int csp; /* maxcluster in UFS2 */
u32int* maxcluster; u32int maxcluster; /* active in UFS2 */
u32int cpc; u32int _cpc;
u16int opostbl[16][8]; /* u16int opostbl[16][8]; in UFS1 */
u32int maxbsize;
u64int spare64[17];
u64int sblockloc;
Cylsumtotal cstotal;
u64int time;
u64int nfrag;
u64int ndfrag;
u64int csaddr;
u64int pendingblocks;
u32int pendinginodes;
u32int snapinum[FSMAXSNAP]; u32int snapinum[FSMAXSNAP];
u32int avgfilesize; u32int avgfilesize;
u32int avgfpdir; u32int avgfpdir;
u32int sparecon[26]; /* u32int sparecon[26], pendingblocks, pendinginodes; in UFS1 */
u32int pendingblocks; u32int savecgsize;
u32int pendinginodes; u32int sparecon[26];
u32int flags;
u32int contigsumsize; u32int contigsumsize;
u32int maxsymlinklen; u32int maxsymlinklen;
u32int inodefmt; /* format of on-disk inodes */ u32int _inodefmt; /* format of on-disk inodes */
u64int maxfilesize; /* maximum representable file size */ u64int maxfilesize; /* maximum representable file size */
u64int qbmask; u64int qbmask;
u64int qfmask; u64int qfmask;
u32int state; u32int state;
u32int postblformat; u32int _postblformat;
u32int nrpos; u32int _nrpos;
u32int postbloff; u32int _postbloff;
u32int rotbloff; u32int _rotbloff;
u32int magic; /* FS_MAGIC */ u32int magic; /* FSMAGIC or FSMAGIC2 */
}; };
/* /*
@ -190,12 +225,12 @@ struct Fsblk
struct Cgblk struct Cgblk
{ {
u32int unused0; u32int unused0;
u32int magic; /* CGMAGIC */ u32int magic; /* CGMAGIC */
u32int time; /* time last written */ u32int time; /* time last written */
u32int num; /* we are cg #cgnum */ u32int num; /* we are cg #cgnum */
u16int ncyl; /* number of cylinders in gp */ u16int ncyl; /* number of cylinders in gp */
u16int nino; /* number of inodes */ u16int nino; /* number of inodes */
u32int nfrag; /* number of fragments */ u32int nfrag; /* number of fragments */
Cylsum csum; Cylsum csum;
u32int rotor; u32int rotor;
u32int frotor; u32int frotor;
@ -215,16 +250,16 @@ struct Cgblk
struct Cylgrp struct Cylgrp
{ {
/* these are block numbers not fragment numbers */ /* these are block numbers not fragment numbers */
u32int bno; /* disk block address of start of cg */ u64int bno; /* disk block address of start of cg */
u32int ibno; /* disk block address of first inode */ u64int ibno; /* disk block address of first inode */
u32int dbno; /* disk block address of first data */ u64int dbno; /* disk block address of first data */
u32int cgblkno; u64int cgblkno;
}; };
/* /*
* this is the on-disk structure * this is the on-disk structure
*/ */
struct Inode struct Inode1
{ {
u16int mode; u16int mode;
u16int nlink; u16int nlink;
@ -247,6 +282,33 @@ struct Inode
u32int spare[2]; u32int spare[2];
}; };
struct Inode
{
u16int mode;
u16int nlink;
u32int uid;
u32int gid;
u32int blksize;
u64int size;
u64int nblock;
u64int atime;
u64int mtime;
u64int ctime;
u64int btime;
u32int atimensec;
u32int mtimensec;
u32int ctimensec;
u32int btimensec;
u32int gen;
u32int kernflags;
u32int flags;
u32int extsize;
u64int ext[NXADDR];
u64int db[NDADDR];
u64int ib[NIADDR];
u64int spare[3];
};
struct Dirent struct Dirent
{ {
u32int ino; u32int ino;
@ -261,17 +323,18 @@ struct Dirent
*/ */
struct Ffs struct Ffs
{ {
int ufs;
int blocksize; int blocksize;
int nblock; u64int nblock;
int fragsize; int fragsize;
int fragsperblock; int fragsperblock;
int inosperblock; int inosperblock;
int blockspergroup; u64int blockspergroup;
int fragspergroup; u64int fragspergroup;
int inospergroup; int inospergroup;
u32int nfrag; u64int nfrag;
u32int ndfrag; u64int ndfrag;
int ncg; int ncg;
Cylgrp *cg; Cylgrp *cg;

View file

@ -29,3 +29,5 @@ CFLAGS=$CFLAGS
%.acid: %.$O %.c %.acid: %.$O %.c
$CC $CFLAGS -a $stem.c >$stem.acid $CC $CFLAGS -a $stem.c >$stem.acid
ffs.$O: ffs.h