Added README, LICENSE, Makefile for future PDP-11 Unix FUSE fs driver project.
[pdp11-unix-fusefs] / v6.c
#include "fs.h"
/* NB: not portable! assumes little endian (and probably other things) */
#define NIPB (512/sizeof(DInode))
#define NDPB (512/sizeof(Dirent))
/* v6 superblock */
typedef struct Filsys Filsys;
struct Filsys
{
int16 s_isize; /* size in blocks of I list */
int16 s_fsize; /* size in blocks of entire volume */
int16 s_nfree; /* number of in core free blocks (0-100) */
int16 s_free[100]; /* in core free blocks */
int16 s_ninode; /* number of in core I nodes (0-100) */
int16 s_inode[100]; /* in core free I nodes */
uint8 s_flock; /* lock during free list manipulation */
uint8 s_ilock; /* lock during I list manipulation */
uint8 s_fmod; /* super block modified flag */
uint8 s_ronly; /* mounted read-only flag */
int16 s_time[2]; /* current date of last update */
int16 pad[48];
};
/* v6 and PWB/1.0 disk inode */
struct DInode
{
uint16 i_mode;
int8 i_nlink;
uint8 i_uid;
uint8 i_gid;
uint8 i_size0;
uint16 i_size1;
uint16 i_addr[8];
uint16 i_atime[2];
uint16 i_mtime[2];
};
/* modes */
#define IALLOC 0100000
#define IFMT 060000
#define IFDIR 040000
#define IFCHR 020000
#define IFBLK 060000
#define ILARG 010000
#define ISUID 04000
#define ISGID 02000
#define ISVTX 01000
#define IREAD 0400
#define IWRITE 0200
#define IEXEC 0100
typedef struct Dirent Dirent;
struct Dirent
{
uint16 inode;
char name[14];
};
Filsys *filesys;
DInode *dinodes;
Inode *inodes;
#define ISIZE(ip) ((ip)->i_size0<<16 | (ip)->i_size1)
int32
pdplong(void *p)
{
uint8 *up;
uint32 ui;
up = p;
ui = up[1]<<24 | up[0]<<16 | up[3]<<8 | up[2];
return *(int32*)&ui;
}
void
setpdplong(void *p, int32 i)
{
uint8 *up;
uint32 ui;
up = p;
ui = *(uint32*)&i;
up[1] = ui>>24;
up[0] = ui>>16;
up[3] = ui>>8;
up[2] = ui;
}
void
setint24(void *p, int32 i)
{
uint8 *up;
uint32 ui;
up = p;
ui = *(uint32*)&i;
up[0] = ui>>16;
up[2] = ui>>8;
up[1] = ui;
}
void *bget(int n) { return &fsdata[n*512]; }
/* returns a free block or 0 */
int
balloc(void)
{
int i, bno;
uint16 *b;
filesys->s_nfree--;
if(filesys->s_nfree < 0 || filesys->s_nfree >= 100){
printf("bad free count\n");
return 0;
}
bno = filesys->s_free[filesys->s_nfree];
filesys->s_free[filesys->s_nfree] = 0;
if(bno == 0)
return 0;
if(bno < filesys->s_isize+2 || bno >= filesys->s_fsize){
printf("bad free block (%d)\n", bno);
return 0;
}
if(filesys->s_nfree <= 0){
b = bget(bno);
filesys->s_nfree = b[0];
for(i = 0; i < 100; i++)
filesys->s_free[i] = b[i+1];
}
return bno;
}
void
bfree(int bno)
{
int i;
uint16 *b;
printf("freeing block\n");
if(filesys->s_nfree >= 100){
b = bget(bno);
b[0] = filesys->s_nfree;
for(i = 0; i < 100; i++)
b[i+1] = filesys->s_free[i];
filesys->s_nfree = 0;
}
filesys->s_free[filesys->s_nfree] = bno;
filesys->s_nfree++;
}
int
zalloc(void)
{
int bn;
bn = balloc();
if(bn)
memset(bget(bn), 0, 512);
return bn;
}
/* returns a free inode or 0 */
int
ialloc(void)
{
int ino;
DInode *ip;
int i, j;
if(filesys->s_ninode <= 0){
ino = 0;
for(i = 0; i < filesys->s_isize; i++)
for(j = 0; j < NIPB; j++){
ino++;
ip = &dinodes[ino];
if(ip->i_mode)
continue;
filesys->s_inode[filesys->s_ninode++] = ino;
if(filesys->s_ninode >= 100)
goto brk;
}
}
brk:
if(filesys->s_ninode > 0){
ino = filesys->s_inode[--filesys->s_ninode];
return ino;
}
return 0;
}
void
ifree(int ino)
{
printf("freeing inode\n");
if(filesys->s_ninode >= 100)
return;
filesys->s_inode[filesys->s_ninode++] = ino;
}
void
itrunc(DInode *ip)
{
int i;
uint16 *bp, *cp, *dp, *ep;
if(ip->i_mode & (IFCHR|IFBLK))
return;
for(i = 7; i >= 0; i--)
if(ip->i_addr[i]){
if(ip->i_mode & ILARG){
bp = bget(ip->i_addr[i]);
for(cp = &bp[255]; cp >= bp; cp--)
if(*cp){
if(i == 7){
dp = bget(*cp);
for(ep = &dp[255]; ep >= dp; ep--)
if(*ep)
bfree(*ep);
}
bfree(*cp);
}
}
bfree(ip->i_addr[i]);
ip->i_addr[i] = 0;
}
ip->i_mode &= ~ILARG;
setint24(&ip->i_size0, 0);
}
void
dcheck(void)
{
Filsys fs;
int i;
fs = *filesys;
i = 0;
while(balloc())
i++;
printf("free: %d\n", i);
*filesys = fs;
}
int
bmap(DInode *ip, uint bn)
{
uint i;
uint nb;
uint16 *b;
if(bn & ~077777)
return 0;
if((ip->i_mode & ILARG) == 0){
/* small file, direct fetch */
if(bn & ~7){
/* convert to large */
nb = zalloc();
if(nb == 0)
return 0;
b = bget(nb);
for(i = 0; i < 8; i++){
b[i] = ip->i_addr[i];
ip->i_addr[i] = 0;
}
ip->i_addr[0] = nb;
ip->i_mode |= ILARG;
goto large;
}
nb = ip->i_addr[bn];
if(nb == 0){
nb = zalloc();
if(nb == 0)
return 0;
ip->i_addr[bn] = nb;
}
return nb;
}
/* large file, 7 indirect blocks */
large:
i = bn>>8;
if(i > 7)
i = 7;
nb = ip->i_addr[i];
if(nb == 0){
nb = zalloc();
if(nb == 0)
return 0;
ip->i_addr[bn] = nb;
}
b = bget(nb);
if(i == 7){
/* huge file, double indirect last block */
i = (bn>>8) - 7;
nb = b[i&0377];
if(nb == 0){
nb = zalloc();
if(nb == 0)
return 0;
b[i&0377] = nb;
}
b = bget(nb);
}
nb = b[bn&0377];
return nb;
};
uint8*
getblock(DInode *ip, uint bn)
{
bn = bmap(ip, bn);
if(bn == 0)
return nil;
return bget(bn);
}
void
fs_init(void)
{
int i, ni;
filesys = bget(1);
dinodes = (DInode*)bget(2) - 1;
ni = filesys->s_isize*NIPB + 1;
inodes = malloc(ni*sizeof(Inode));
for(i = 0; i < ni; i++){
inodes[i].ino = i;
inodes[i].count = 0;
inodes[i].i = &dinodes[i];
}
}
int
fs_open(uint ino, int flags)
{
DInode *ip;
ip = &dinodes[ino];
if((ip->i_mode & IFMT) == IFDIR)
return EISDIR;
// if((flags & 3) != O_RDONLY)
// return EACCES;
return 0;
}
int
fs_stat(uint ino, struct stat *stbuf)
{
DInode *ip;
ip = &dinodes[ino];
stbuf->st_ino = ino;
if((ip->i_mode & IFMT) == IFDIR)
stbuf->st_mode = S_IFDIR;
else if((ip->i_mode & IFMT) == IFCHR)
stbuf->st_mode = S_IFCHR;
else if((ip->i_mode & IFMT) == IFBLK)
stbuf->st_mode = S_IFBLK;
else
stbuf->st_mode = S_IFREG;
stbuf->st_mode |= ip->i_mode & 0777;
stbuf->st_nlink = ip->i_nlink;
stbuf->st_size = ISIZE(ip);
if((ip->i_mode & IFMT) == IFCHR ||
(ip->i_mode & IFMT) == IFBLK)
stbuf->st_rdev = makedev(ip->i_addr[0]>>8 & 0xFF, ip->i_addr[0]&0xFF);
stbuf->st_uid = ip->i_uid;
stbuf->st_gid = ip->i_gid;
stbuf->st_atime = pdplong(ip->i_atime);
stbuf->st_mtime = pdplong(ip->i_mtime);
return 0;
}
int
fs_atime(uint ino, int32 time)
{
DInode *ip;
ip = &dinodes[ino];
setpdplong(ip->i_atime, time);
return 0;
}
int
fs_mtime(uint ino, int32 time)
{
DInode *ip;
ip = &dinodes[ino];
setpdplong(ip->i_mtime, time);
return 0;
}
int
fs_uid(uint ino, int32 uid)
{
DInode *ip;
ip = &dinodes[ino];
ip->i_uid = uid;
return 0;
}
int
fs_gid(uint ino, int32 gid)
{
DInode *ip;
ip = &dinodes[ino];
ip->i_gid = gid;
return 0;
}
int
fs_read(uint ino, void *vdst, int offset, int len)
{
DInode *ip;
int bn;
uint8 *b;
int isize;
int n;
uint8 *dst;
ip = &dinodes[ino];
dst = vdst;
isize = ISIZE(ip);
if(offset + len > isize)
len = isize - offset;
if(len <= 0)
return 0;
bn = offset/512;
offset %= 512;
b = getblock(ip, bn);
if(b == nil)
/* TODO: handle gracefully */
panic("no block");
if(len < 512-offset){
/* smaller than block */
memcpy(dst, b+offset, len);
return len;
}
/* at least one block */
memcpy(dst, b+offset, 512-offset);
n = 512-offset;
len -= n;
dst += n;
while(len > 0){
bn++;
b = getblock(ip, bn);
if(b == nil)
/* TODO: handle gracefully */
panic("no block");
memcpy(dst, b, len > 512 ? 512 : len);
n += len > 512 ? 512 : len;
len -= 512;
dst += 512;
}
return n;
}
int
fs_write(uint ino, void *vsrc, int offset, int len)
{
DInode *ip;
int bn;
uint8 *b;
int n;
int32 size;
uint8 *src;
ip = &dinodes[ino];
src = vsrc;
size = ISIZE(ip);
// TODO: make this better:
if(offset+len > size)
setint24(&ip->i_size0, offset+len);
if(len <= 0)
return 0;
bn = offset/512;
offset %= 512;
b = getblock(ip, bn);
if(b == nil)
/* TODO: handle gracefully */
panic("no block");
if(len < 512-offset){
/* smaller than block */
memcpy(b+offset, src, len);
return len;
}
/* at least one block */
memcpy(b+offset, src, 512-offset);
n = 512-offset;
len -= n;
src += n;
while(len > 0){
bn++;
b = getblock(ip, bn);
if(b == nil)
/* TODO: handle gracefully */
panic("no block");
memcpy(b, src, len > 512 ? 512 : len);
n += len > 512 ? 512 : len;
len -= 512;
src += 512;
}
return n;
}
/* return a malloc'd array of dirents for a given inode */
struct dirent*
fs_readdir(uint ino)
{
DInode *ip;
struct dirent *des, *dp;
int size;
int ndes;
Dirent de;
int offset;
ip = &dinodes[ino];
if((ip->i_mode & IFDIR) == 0)
return nil;
size = ISIZE(ip);
ndes = size/sizeof(Dirent);
des = malloc((ndes+1)*sizeof(struct dirent));
offset = 0;
dp = des;
while(fs_read(ino, &de, offset, sizeof(Dirent)) == sizeof(Dirent)){
if(de.inode){
dp->d_ino = de.inode;
memcpy(dp->d_name, de.name, 14);
dp->d_name[14] = '\0';
dp++;
}
offset += sizeof(Dirent);
}
dp->d_ino = 0;
dp->d_name[0] = '\0';
return des;
}
static int
lookup(uint ino, const char *name, Dirent *de)
{
int offset;
assert(de);
offset = 0;
while(fs_read(ino, de, offset, sizeof(Dirent)) == sizeof(Dirent)){
if(de->inode)
if(strcmp(de->name, name) == 0)
return offset;
offset += sizeof(Dirent);
}
memset(de, 0, sizeof(Dirent));
return -1;
}
static int
allocdirent(uint ino)
{
Dirent de;
int offset;
offset = 0;
while(fs_read(ino, &de, offset, sizeof(Dirent)) == sizeof(Dirent)){
if(de.inode == 0)
return offset;
offset += sizeof(Dirent);
}
memset(&de, 0, sizeof(Dirent));
fs_write(ino, &de, offset, sizeof(Dirent));
return offset;
}
int
fs_link(uint ino, uint parent, const char *name)
{
Dirent de;
int offset;
offset = allocdirent(parent);
/* TODO: can't happen right now */
if(offset < 0)
return ENOSPC;
memset(&de, 0, sizeof(Dirent));
de.inode = ino;
strncpy(de.name, name, 14);
fs_write(parent, &de, offset, sizeof(de));
fs_mtime(ino, time(nil));
fs_atime(ino, time(nil));
dinodes[ino].i_nlink++;
return 0;
}
int
fs_mknod(uint parent, const char *name, mode_t mode, dev_t rdev, uint *newino)
{
uint ino;
DInode *ip, *ipp;
int offset;
Dirent de;
USED(rdev);
// TODO: support more files
if((mode & S_IFMT) != S_IFREG &&
(mode & S_IFMT) != S_IFDIR)
return EACCES;
ino = ialloc();
if(ino == 0)
return ENOSPC;
ip = &dinodes[ino];
memset(ip, 0, sizeof(DInode));
ip->i_mode = (mode&0777) | IALLOC;
if((mode & S_IFMT) == S_IFDIR)
ip->i_mode |= IFDIR;
else if((mode & S_IFMT) == S_IFCHR)
ip->i_mode |= IFCHR;
else if((mode & S_IFMT) == S_IFBLK)
ip->i_mode |= IFBLK;
if((ip->i_mode & IFMT) == IFCHR ||
(ip->i_mode & IFMT) == IFBLK)
ip->i_addr[0] =
major(rdev)<<8 & 0xFF00 | minor(rdev) & 0xFF;
ipp = &dinodes[parent];
assert((ipp->i_mode & IFMT) == IFDIR);
offset = allocdirent(parent);
/* TODO: can't happen right now */
if(offset < 0){
ip->i_mode = 0;
return ENOSPC;
}
ip->i_nlink = 1;
memset(&de, 0, sizeof(Dirent));
de.inode = ino;
strncpy(de.name, name, 14);
fs_write(parent, &de, offset, sizeof(de));
fs_mtime(ino, time(nil));
fs_atime(ino, time(nil));
if(newino)
*newino = ino;
printf("making node %s %o\n", name, mode);
return 0;
}
int
fs_mkdir(uint parent, const char *name, mode_t mode)
{
int bn;
int ino;
int ret;
DInode *ip;
Dirent des[2];
/* Make sure we have space */
bn = balloc();
if(bn == 0)
return ENOSPC;
ret = fs_mknod(parent, name, mode | S_IFDIR, 0, &ino);
if(ret){
bfree(bn);
return ret;
}
ip = &dinodes[ino];
ip->i_addr[0] = bn;
memset(des, 0, 2*sizeof(Dirent));
des[0].inode = ino;
strcpy(des[0].name, ".");
ip->i_nlink++;
des[1].inode = parent;
strcpy(des[1].name, "..");
dinodes[parent].i_nlink++;
fs_write(ino, des, 0, 2*sizeof(Dirent));
return 0;
}
Inode*
fs_iget(uint ino)
{
Inode *ip;
ip = &inodes[ino];
ip->count++;
return ip;
}
void
fs_iput(Inode *ip)
{
assert(ip->count > 0);
ip->count--;
if(ip->count == 0)
if(ip->i->i_nlink <= 0){
printf("truncating inode %d (%d)\n", ip->ino, ip->i->i_nlink);
itrunc(ip->i);
ip->i->i_mode = 0;
ifree(ip->ino);
}
}
int
fs_unlink(uint parent, const char *name)
{
Dirent de;
int offset;
offset = lookup(parent, name, &de);
if(offset < 0)
/* shouldn't happen */
return 1;
dinodes[de.inode].i_nlink--;
de.inode = 0;
fs_write(parent, &de, offset, sizeof(Dirent));
return 0;
}