Skip to content

Instantly share code, notes, and snippets.

@sam-falvo
Created November 9, 2014 04:16
Show Gist options
  • Save sam-falvo/f8920f8a7c91fad4b601 to your computer and use it in GitHub Desktop.
Save sam-falvo/f8920f8a7c91fad4b601 to your computer and use it in GitHub Desktop.
A quick and dirty prototype of the Kestrel OS, STS 2. This implementation takes on a distinctly more Unix- or Plan-9-like flavor to the user and coder.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/****** fsobj.h ******/
struct scb;
struct fsobjepv {
struct fsobj *(*step)(struct fsobj *, char *);
void (*flush)(struct fsobj *, struct fsobj *);
void (*open)(struct fsobj *, struct scb *);
void (*close)(struct fsobj *, struct scb *);
int (*read)(struct fsobj *, struct scb *, char *, int);
};
struct fsobj {
struct fsobjepv *epv;
int refcnt;
struct fsobj *parent;
};
struct components {
char *workspace;
char **names;
int nnames;
};
struct fsobj *walk(char *);
void componentize(char *, struct components *);
void freecomponents(struct components *);
struct fsobj *newfsobj(struct fsobjepv *, struct fsobj *);
void unreffsobj(struct fsobj **);
#define reffsobj(o) (o)->refcnt++;
/****** scb.h ******/
struct scb {
int refcnt;
struct fsobj *fsobj;
char *bufbase;
int bufoffset;
int bufcap;
};
struct scb *newscb(struct fsobj *);
void unrefscb(struct scb ** );
#define refscb(s) (s)->refcnt++;
/****** process.h ******/
enum {
MAX_HANDLES = 64,
};
struct process {
int error; // This process' error from the last system call.
struct fsobj *slash; // This process' root directory.
struct scb *handles[MAX_HANDLES];
};
enum {
EOK = 0,
ENOMEM = 1,
ENOTIMPL = 2,
EBADPATH = 3,
ENULL = 4,
ENOTFOUND = 5,
ENOTENOUGH = 6,
EHANDLE = 7,
};
struct process *current(void);
struct process *newprocess(struct fsobj *);
int errcode(void);
void seterror(int);
/****** process.c ******/
static struct process *_current = 0;
struct process *newprocess(struct fsobj *slash) {
struct process *p = (struct process *)(malloc(sizeof(struct process)));
if(p){
memset(p, 0, sizeof(struct process));
p->slash = slash;
}
return p;
}
void initprocess(struct fsobj *slash) {
if(_current) return;
_current = newprocess(slash);
}
struct process *current(void) {
return _current;
}
int errcode(void) {
return current()->error;
}
void seterror(int code) {
current()->error = code;
}
int newhandle(struct scb *scb) {
struct process *p = current();
int h;
if(!scb) {
seterror(ENULL);
return -1;
}
for(h = 0; h < MAX_HANDLES; h++) {
if(!p->handles[h]) {
p->handles[h] = scb;
reffsobj(scb);
return h;
}
}
seterror(ENOTENOUGH);
return -1;
}
void disposehandle(int h) {
struct process *p = current();
if(p->handles[h]) {
unrefscb(&(p->handles[h]));
assert(!p->handles[h]);
}
}
struct scb *handlescb(int h) {
struct process *p = current();
return p->handles[h];
}
/****** fsobj.c ******/
struct fsobj *newfsobj(struct fsobjepv *epv, struct fsobj *parent) {
struct fsobj *o = (struct fsobj *)(malloc(sizeof(struct fsobj)));
if(o) {
memset(o, 0, sizeof(struct fsobj));
o->refcnt = 1;
o->epv = epv;
o->parent = parent;
if(parent) reffsobj(parent);
}
return o;
}
void unreffsobj(struct fsobj **o) {
struct fsobj *obj = *o;
if(!obj) return;
if(obj->refcnt == 1) {
printf("Freeing object at %p\n", obj);
if(obj->parent) {
obj->parent->epv->flush(obj->parent, obj);
unreffsobj(&(obj->parent));
}
free(obj);
*o = 0;
return;
}
obj->refcnt--;
}
void freecomponents(struct components *cs) {
if(cs->names) free(cs->names);
if(cs->workspace) free(cs->workspace);
memset(cs, 0, sizeof(struct components));
}
void componentize(char *path, struct components *cs) {
int nn, nnsz;
char *pch;
char **ppn;
memset(cs, 0, sizeof(struct components));
// Ignore leading slashes, if any.
// Precondition: NUL-terminated string.
while(path[0] == '/') path++;
// If no path to parse, raise an error.
if(!path[0]) {
seterror(EBADPATH);
return;
}
// Count the slashes. This value, plus one, is the worst-case size for the names vector.
for(pch=path, nn = 1; *pch; pch++) {
if(*pch == '/') nn++;
}
assert(nn >= 1);
nnsz = sizeof(char *) * nn;
cs->names = (char **)(malloc(nnsz));
if(!cs->names) {
seterror(ENOMEM);
return;
}
memset(cs->names, 0, nnsz);
// Duplicate the path and reuse it as a working buffer for tokenization.
// We iterate through each character in the path, converting each slash to a NUL byte.
// We keep pointers to each component in the cs->names vector along the way.
// Precondition: the path does not contain adjacent slashes. These will yield empty entries.
// Precondition: . and .. do not exist in the provided path. These are not given any special treatment.
cs->workspace = strdup(path);
if(!cs->workspace) {
freecomponents(cs);
seterror(ENOMEM);
return;
}
cs->names[0] = cs->workspace;
cs->nnames = 1;
for(pch=cs->workspace, ppn=&cs->names[1]; *pch; pch++) {
if(*pch == '/') {
*ppn = pch+1;
*pch = 0;
cs->nnames++;
ppn++;
}
}
}
void step(struct fsobj **obj, char *name) {
struct fsobj *old = *obj;
*obj = old->epv->step(old, name);
unreffsobj(&old);
if(!errcode() && !*obj) seterror(ENULL);
}
struct fsobj *walk(char *path) {
int i;
struct components cs;
struct fsobj *obj;
// Precondition: the provided path is absolute.
componentize(path, &cs);
if(errcode()) return;
// Starting at the root, we traverse each level one component at a time.
// BUG: Dangling references inside the various handlers upon unreferencing each filesystem object.
obj = current()->slash;
assert(obj != 0);
reffsobj(obj);
for(i = 0; i < cs.nnames; i++) {
step(&obj, cs.names[i]);
if(errcode()) {
unreffsobj(&obj);
freecomponents(&cs);
return 0;
}
}
freecomponents(&cs);
return obj;
}
/****** scb.c ******/
struct scb *newscb(struct fsobj *o) {
struct scb *scb = (struct scb *)(malloc(sizeof(struct scb)));
if(scb) {
memset(scb, 0, sizeof(struct scb));
scb->refcnt = 1;
scb->fsobj = o;
reffsobj(scb->fsobj);
}
return scb;
}
void unrefscb(struct scb **scb) {
struct scb *s = *scb;
if(!s) return;
if(s->refcnt > 1) {
s->refcnt--;
*scb = 0;
return;
}
unreffsobj(&s->fsobj);
free(s);
*scb = 0;
}
/****** fs.c ******/
int Open(char *name) {
struct fsobj *o;
int h;
struct scb *scb, *s;
o = walk(name);
if(errcode()) return -1;
// s and scb both refer to the same SCB.
// scb tracks ownership of the SCB; we unrefscb() it as soon as we hand it off to the handle.
// s continues to refer to the SCB afterward (since unrefscb() will zero scb), so we can continue to work with it.
s = scb = newscb(o);
unreffsobj(&o);
if(errcode()) goto no_scb;
h = newhandle(scb);
unrefscb(&scb);
if(errcode()) goto no_handle;
o->epv->open(o, s);
if(errcode()) goto no_open;
return h;
no_open:
disposehandle(h);
no_handle:
no_scb:
return -1;
}
void Close(int handle) {
if((handle < 0) || (handle >= MAX_HANDLES)) {
seterror(EHANDLE);
return;
}
disposehandle(handle);
}
int Read(int handle, char *out, int expected) {
struct scb *scb;
if((handle < 0) || (handle >= MAX_HANDLES)) {
seterror(EHANDLE);
return -1;
}
scb = handlescb(handle);
if(!scb) {
seterror(EHANDLE);
return -1;
}
return scb->fsobj->epv->read(scb->fsobj, scb, out, expected);
}
/****** dev_bar.c ******/
struct fsobj *_bar_step(struct fsobj *obj, char *n) {
seterror(ENOTIMPL);
return 0;
}
void _bar_flush(struct fsobj *obj, struct fsobj *which) {
// we have nothing to do, since we're not a directory object.
}
void _bar_open(struct fsobj *obj, struct scb *scb) {
static char *fixed_content = "Hello world, this is bar.";
scb->bufbase = fixed_content;
scb->bufoffset = 0;
scb->bufcap = strlen(fixed_content)+1;
}
void _bar_close(struct fsobj *obj, struct scb *scb) {
scb->bufbase = 0;
scb->bufoffset = scb->bufcap = 0;
}
int _bar_read(struct fsobj *obj, struct scb *scb, char *out, int expected) {
int actual = 0;
for(;;) {
if(scb->bufoffset >= scb->bufcap) return actual;
if(!expected) return actual;
*out++ = scb->bufbase[scb->bufoffset++];
expected--;
actual++;
}
}
static struct fsobjepv _barepv = {
_bar_step,
_bar_flush,
_bar_open,
_bar_close,
_bar_read,
};
/****** dev_foo.c ******/
static struct fsobj *barObj = 0;
struct fsobj *_foo_step(struct fsobj *obj, char *n) {
if(!strcmp(n, "bar")) {
if(barObj) return barObj;
barObj = newfsobj(&_barepv, obj);
// This is a weak reference, so responsibility for releasing the reference falls to the caller.
printf("barObj gets address %p\n", barObj);
return barObj;
}
seterror(ENOTFOUND);
return 0;
}
void _foo_flush(struct fsobj *obj, struct fsobj *which) {
if(which != barObj) return;
barObj = 0;
}
void _foo_open(struct fsobj *obj, struct scb *scb) {
seterror(ENOTIMPL);
}
int _foo_read(struct fsobj *obj, struct scb *scb, char *out, int len) {
seterror(ENOTIMPL);
return -1;
}
static struct fsobjepv _fooepv = {
_foo_step,
_foo_flush,
_foo_open,
_foo_open, // same behavior for both open and close
_foo_read,
};
/****** dev_root.c ******/
static struct fsobj *fooObj = 0;
struct fsobj *_root_step(struct fsobj *obj, char *n) {
if(!strcmp(n, "foo")) {
if(fooObj) return fooObj;
fooObj = newfsobj(&_fooepv, obj);
printf("fooObj gets address %p\n", fooObj);
return fooObj;
}
seterror(ENOTFOUND);
return 0;
}
void _root_flush(struct fsobj *obj, struct fsobj *which) {
if(which != fooObj) return;
fooObj = 0;
}
void _root_open(struct fsobj *obj, struct scb *scb) {
seterror(ENOTIMPL);
}
int _root_read(struct fsobj *obj, struct scb *scb, char *out, int len) {
seterror(ENOTIMPL);
return -1;
}
static struct fsobjepv _rootepv = {
_root_step,
_root_flush,
_root_open,
_root_open,
_root_read,
};
/****** main.c ******/
struct fsobj *_sysroot;
void fatal(char *msg) {
fprintf(stderr, "%s (process error %d)\n", msg, errcode());
abort();
}
void initsts(void) {
_sysroot = newfsobj(&_rootepv, 0);
printf("sysroot at %p\n", _sysroot);
initprocess(_sysroot);
}
void stopsts(void) {
if(_sysroot) unreffsobj(&_sysroot);
if(_current) free(_current);
}
void runtest(void) {
struct fsobj *o;
int h;
int actual;
char buf[256];
o = walk("/foo/bar");
if(errcode()) {
fatal("Error encountered");
}
unreffsobj(&o);
h = Open("/foo/bar");
if(h < 0) {
fprintf(stderr, "Couldn't open /foo/bar\n");
goto skipio;
}
actual = Read(h, buf, 11);
if(actual != 11) {
fprintf(stderr, "Read failed; expected 11 characters, got %d\n", actual);
goto doneio;
}
buf[11] = 0;
if(strcmp(buf, "Hello world")) {
fprintf(stderr, "Expected buffer='Hello world'; got %s\n", buf);
goto doneio;
}
actual = Read(h, buf, 255);
buf[255] = 0;
if(actual != 15) {
fprintf(stderr, "Read failure; expected 15 characters, got %d\n", actual);
goto doneio;
}
if(strncmp(buf, ", this is bar.", 15)) {
fprintf(stderr, "Expected buffer=', this is bar.'; got %s\n", buf);
goto doneio;
}
doneio:
Close(h);
skipio:
h = 0;
}
int main(int argc, char *argv[]) {
struct sigaction sa;
sigaction(SIGABRT, NULL, &sa);
sa.sa_handler = (void(*)(int))stopsts;
sigaction(SIGABRT, &sa, NULL);
initsts();
runtest();
stopsts();
}
@mmcdaris
Copy link

Hi sam! Thank you for sharing! if you rename this to gistfile.c then github will highlight all the syntax in this one.
I cloned it and will take some notes in the form of comments in the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment