#include <u.h>
#include <libc.h>
#define NODEFINE
#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/fcall.h>
#include <9pm/ns.h>
typedef struct Uchan Uchan;
struct Uchan
{
int sysfd;
Cname* syspath;
};
/*
* Bug: should perturb the qids to include dev, type.
*/
static PmQid
fsqid(Dir *d)
{
return *(PmQid*)&d->qid;
}
static char*
fspath(Chan *c, char *elem)
{
char *s;
Uchan *uc;
uc = c->aux;
s = pm_smalloc(strlen(uc->syspath->s)+1+strlen(elem)+10);
strcpy(s, uc->syspath->s);
if(s[strlen(s)-1] != '/')
strcat(s, "/");
strcat(s, elem);
return s;
}
static Chan*
pm_sysfd2chan(int sysfd, int mode, PmQid qid, char *buf)
{
Chan *c;
Uchan *uc;
c = pm_newchan();
c->name = pm_newcname(buf);
c->mode = mode;
c->flag |= COPEN;
c->type = pm_devno('U', 1);
c->qid = qid;
uc = pm_smalloc(sizeof *uc);
uc->syspath = pm_newcname(buf);
uc->sysfd = sysfd;
c->aux = uc;
return c;
}
static Chan*
syspath2chan(PmQid qid, char *buf)
{
Chan *c;
Uchan *uc;
c = pm_newchan();
c->name = pm_newcname(buf);
c->type = pm_devno('U', 1);
c->qid = qid;
uc = pm_smalloc(sizeof *uc);
uc->syspath = pm_newcname(buf);
c->aux = uc;
return c;
}
static void
fsreset(void)
{
char buf[128];
int fd;
Chan *c;
Dir *d;
Proc *p;
PmQid q;
p = pm_getproc();
for(fd=0; fd<3; fd++){
if(fd2path(fd, buf, sizeof buf) < 0)
continue;
if((d = dirfstat(fd)) == nil)
memset(&q, 0, sizeof q);
else{
q = *(PmQid*)&d->qid;
free(d);
}
c = pm_sysfd2chan(fd, fd==0?OREAD:OWRITE, q, buf);
if(pm_newfdx(c, fd) < 0){
fprint(2, "pm_newfdx %d: %s\n", fd, p->err);
pm_panic("pm_fdinit");
}
}
fd = open(".", OREAD);
if(fd < 0)
strcpy(buf, ".");
else
fd2path(fd, buf, sizeof buf);
if((d = dirfstat(fd)) == nil)
memset(&q, 0, sizeof q);
else{
q = *(PmQid*)&d->qid;
free(d);
}
if(fd >= 0)
close(fd);
p->dot = syspath2chan(q, buf);
}
static Chan*
fsattach(char *spec)
{
Chan *c;
Dir *d;
PmQid q;
Uchan *uc;
static Lock lk;
static int devno;
if(spec && spec[0])
pm_error(PmEbadspec);
if((d = dirstat(pm_conf.rootdir)) == nil){
pm_oserror();
pm_nexterror();
}
q = fsqid(d);
free(d);
c = pm_devattach('U', "");
c->qid = q;
uc = pm_smalloc(sizeof *uc);
uc->sysfd = -1;
uc->syspath = pm_newcname(pm_conf.rootdir);
c->aux = uc;
lock(&lk);
c->dev = devno++;
unlock(&lk);
return c;
}
Walkqid*
fswalk(Chan *c, Chan *nc, char **name, int nname)
{
char *path;
int i;
int volatile alloc;
Cname *p;
Dir *d;
Uchan *uc;
Walkqid *wq;
if(nname > MAXWELEM)
pm_error("devfs: too many name elements");
alloc = 0;
wq = pm_smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
if(pm_waserror()){
if(alloc && wq->clone!=nil)
pm_cclose(wq->clone);
pm_free(wq);
return nil;
}
if(nc == nil){
nc = pm_devclone(c);
uc = pm_smalloc(sizeof *uc);
*uc = *(Uchan*)c->aux;
nc->aux = uc;
pm_incref(&uc->syspath->ref);
alloc = 1;
}
wq->clone = nc;
uc = nc->aux;
for(i = 0; i < nname; i++){
path = fspath(nc, name[i]);
if((d = dirstat(path)) == nil){
pm_free(path);
if(alloc)
pm_cclose(nc);
wq->clone = nil;
if(i == 0) {
pm_free(wq);
pm_poperror();
return nil;
}
break;
}
wq->qid[i] = fsqid(d);
free(d);
pm_free(path);
p = pm_addelem(uc->syspath, name[i]);
if(strcmp(name[i], "..") == 0)
pm_cleancname(p);
uc->syspath = p;
}
pm_poperror();
if(i > 0 && wq->clone != nil)
wq->clone->qid = wq->qid[i-1];
wq->nqid = i;
return wq;
}
static Chan*
fsopen(Chan *c, int mode)
{
Dir *d;
Uchan *uc;
uc = c->aux;
if((uc->sysfd = open(uc->syspath->s, mode)) < 0){
pm_oserror();
pm_nexterror();
}
if((d = dirfstat(uc->sysfd)) == nil){
pm_oserror();
close(uc->sysfd);
pm_nexterror();
uc->sysfd = -1;
}
c->mode = pm_openmode(mode);
c->offset = 0;
c->qid = fsqid(d);
c->flag |= COPEN;
free(d);
return c;
}
static void
fscreate(Chan *c, char *name, int mode, ulong perm)
{
char *path;
Dir *d;
Uchan *uc;
uc = c->aux;
path = fspath(c, name);
if((uc->sysfd = create(path, mode, perm)) < 0){
pm_free(path);
pm_oserror();
pm_nexterror();
}
pm_free(path);
if((d = dirfstat(uc->sysfd)) == nil){
pm_oserror();
close(uc->sysfd);
uc->sysfd = -1;
pm_nexterror();
}
c->mode = pm_openmode(mode);
c->offset = 0;
c->qid = fsqid(d);
c->flag |= COPEN;
free(d);
}
static void fsremove(Chan*);
static void
fsclose(Chan *c)
{
Uchan *uc;
uc = c->aux;
if(c->flag & COPEN){
if(c->flag & CRCLOSE){
fsremove(c);
return;
}
close(uc->sysfd);
uc->sysfd = -1;
}
pm_cnameclose(uc->syspath);
pm_free(uc);
}
static long
fsread(Chan *c, void *a, long n, vlong o)
{
Uchan *uc;
uc = c->aux;
n = pread(uc->sysfd, a, n, o);
if(n < 0){
pm_oserror();
pm_nexterror();
}
if(c->qid.type&PM_QTDIR)
pm_mntdirreadfix(a, n, c);
return n;
}
static long
fswrite(Chan *c, void *a, long n, vlong o)
{
Uchan *uc;
uc = c->aux;
n = pwrite(uc->sysfd, a, n, o);
if(n < 0){
pm_oserror();
pm_nexterror();
}
if(c->qid.type&PM_QTDIR)
pm_mntdirreadfix(a, n, c);
return n;
}
static void
fsremove(Chan *c)
{
int err;
Uchan *uc;
uc = c->aux;
err = 0;
if(remove(uc->syspath->s) < 0){
pm_oserror();
err = 1;
}
if(c->flag & COPEN){
close(uc->sysfd);
uc->sysfd = -1;
}
pm_cnameclose(uc->syspath);
pm_free(uc);
if(err)
pm_nexterror();
return;
}
static int
fsstat(Chan *c, uchar *buf, int n)
{
Uchan *uc;
uc = c->aux;
if(uc->sysfd >= 0)
n = fstat(uc->sysfd, buf, n);
else
n = stat(uc->syspath->s, buf, n);
if(n < 0){
pm_oserror();
pm_nexterror();
}
return n;
}
static int
fswstat(Chan *c, uchar *edir, int nedir)
{
Uchan *uc;
uc = c->aux;
if((nedir = fwstat(uc->sysfd, edir, nedir)) < 0){
pm_oserror();
pm_nexterror();
}
return nedir;
}
static void
fschdir(Chan *c)
{
Uchan *uc;
uc = c->aux;
if(chdir(uc->syspath->s) < 0){
pm_oserror();
pm_nexterror();
}
}
Dev devfs =
{
(Rune)'U',
"fs",
fsreset,
fsattach,
fswalk,
fsstat,
fsopen,
fscreate,
fsclose,
fsread,
pm_devbread,
fswrite,
pm_devbwrite,
fsremove,
fswstat,
fschdir,
};
syntax highlighted by Code2HTML, v. 0.9.1