jehanne/sys/src/kern/port/devshr.c

852 lines
15 KiB
C
Raw Normal View History

/* Copyright (c) 20XX 9front
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
extern Dev* devtab[];
enum {
Qroot,
Qcroot,
Qshr,
Qcshr,
Qcmpt,
};
typedef struct Ent Ent;
typedef struct Shr Shr;
typedef struct Mpt Mpt;
typedef struct Sch Sch;
struct Ent
{
Ref ref;
int id;
char *name;
char *owner;
uint32_t perm;
};
struct Shr
{
Ent;
Mhead umh;
Shr *next;
};
struct Mpt
{
Ent;
Mount m;
};
struct Sch
{
int level;
Shr *shr;
Mpt *mpt;
Chan *chan;
};
static QLock shrslk;
static Shr *shrs;
static int shrid;
static int mptid;
static Mpt*
tompt(Mount *m)
{
return (Mpt*)((char*)m - (char*)&((Mpt*)0)->m);
}
static Sch*
tosch(Chan *c)
{
Sch *sch;
if(c == nil)
error("nil chan");
sch = c->aux;
if(sch == nil)
error("nil chan aux");
if(sch->chan != c)
error("bad chan");
return sch;
}
static void
shrinit(void)
{
shrid = 1;
mptid = 1;
}
static void
putmpt(Mpt *mpt)
{
if(decref(&mpt->ref))
return;
if(mpt->m.to != nil)
cclose(mpt->m.to);
jehanne_free(mpt->name);
jehanne_free(mpt->owner);
jehanne_free(mpt);
}
static void
putshr(Shr *shr)
{
if(decref(&shr->ref))
return;
jehanne_free(shr->name);
jehanne_free(shr->owner);
jehanne_free(shr);
}
static Qid
shrqid(int level, int id)
{
Qid q;
q.type = (level == Qcmpt) ? QTFILE : QTDIR;
q.path = (uint64_t)id<<4 | level;
q.vers = 0;
return q;
}
static Chan*
shrattach(Chan *c, Chan *ac, char *spec, int flags)
{
Sch *sch;
if(!(spec[0] == 'c' && spec[1] == 0 || spec[0] == 0))
error(Enoattach);
c = devattach(L'σ', spec);
sch = smalloc(sizeof(*sch));
sch->level = spec[0] == 'c' ? Qcroot : Qroot;
sch->mpt = nil;
sch->shr = nil;
sch->chan = c;
c->aux = sch;
c->qid = shrqid(sch->level, 0);
return c;
}
static Chan*
shrclone(Chan *c)
{
Chan *nc;
Sch *sch, *och;
och = tosch(c);
nc = devclone(c);
nc->dev = c->dev; // devclone does not fill ->dev
// this is a plan9-9k specific behaviour, but why?
// many related places are marked with XDYNX
sch = smalloc(sizeof(*sch));
jehanne_memmove(sch, och, sizeof(*sch));
if(sch->shr != nil)
incref(&sch->shr->ref);
if(sch->mpt != nil)
incref(&sch->mpt->ref);
sch->chan = nc;
nc->aux = sch;
return nc;
}
static void
shrclunk(Chan *c)
{
Sch *sch;
sch = tosch(c);
c->aux = nil;
sch->chan = nil;
if(sch->mpt != nil)
putmpt(sch->mpt);
if(sch->shr != nil)
putshr(sch->shr);
jehanne_free(sch);
}
static Walkqid*
shrwalk(Chan *c, Chan *nc, char **name, int nname)
{
Walkqid *wq, *wq2;
int alloc, j;
char *nam;
Sch *sch;
Shr *shr;
Mpt *mpt;
Mount *m;
Mhead *h;
alloc = 0;
if(nc == nil){
nc = shrclone(c);
alloc = 1;
}
wq = smalloc(sizeof(Walkqid) + (nname - 1) * sizeof(Qid));
wq->nqid = 0;
wq->clone = nc;
if(waserror()){
if(alloc)
cclose(wq->clone);
if(wq->nqid > 0)
wq->clone = nil;
else {
jehanne_free(wq);
wq = nil;
}
return wq;
}
sch = tosch(nc);
for(j = 0; j < nname; j++){
if(nc->qid.type != QTDIR)
error(Enotdir);
nam = name[j];
if(nam[0] == '.' && nam[1] == 0) {
/* nop */
} else if(nam[0] == '.' && nam[1] == '.' && nam[2] == 0) {
switch(sch->level){
default:
error(Egreg);
case Qshr:
nc->qid = shrqid(sch->level = Qroot, 0);
break;
case Qcshr:
nc->qid = shrqid(sch->level = Qcroot, 0);
break;
}
putshr(sch->shr);
sch->shr = nil;
} else if(sch->level == Qcroot || sch->level == Qroot) {
qlock(&shrslk);
for(shr = shrs; shr != nil; shr = shr->next)
if(jehanne_strcmp(nam, shr->name) == 0){
incref(&shr->ref);
break;
}
qunlock(&shrslk);
if(shr == nil)
error(Enonexist);
sch->level = sch->level == Qcroot ? Qcshr : Qshr;
sch->shr = shr;
nc->qid = shrqid(sch->level, shr->id);
} else if(sch->level == Qcshr) {
mpt = nil;
shr = sch->shr;
h = &shr->umh;
rlock(&h->lock);
for(m = h->mount; m != nil; m = m->next){
mpt = tompt(m);
if(jehanne_strcmp(nam, mpt->name) == 0){
incref(&mpt->ref);
break;
}
}
runlock(&h->lock);
if(m == nil)
error(Enonexist);
sch->mpt = mpt;
nc->qid = shrqid(sch->level = Qcmpt, mpt->id);
} else if(sch->level == Qshr) {
shr = sch->shr;
h = &shr->umh;
wq2 = nil;
rlock(&h->lock);
for(m = h->mount; m != nil && wq2 == nil; m = m->next){
if(m->to == nil)
continue;
if(waserror())
continue;
wq2 = m->to->dev->walk(m->to, nil, name + j, nname - j);
poperror();
}
runlock(&h->lock);
if(wq2 == nil)
error(Enonexist);
jehanne_memmove(wq->qid + wq->nqid, wq2->qid, wq2->nqid);
wq->nqid += wq2->nqid;
if(alloc)
cclose(wq->clone);
wq->clone = wq2->clone;
jehanne_free(wq2);
poperror();
return wq;
} else
error(Egreg);
wq->qid[wq->nqid++] = nc->qid;
}
poperror();
return wq;
}
static int
shrgen(Chan *c, char* _, Dirtab* __, int ___, int s, Dir *dp)
{
Mpt *mpt;
Sch *sch;
Shr *shr;
Mhead *h;
Mount *m;
sch = tosch(c);
switch(sch->level){
default:
return -1;
case Qroot:
case Qcroot:
qlock(&shrslk);
for(shr = shrs; shr != nil && s > 0; shr = shr->next)
s--;
if(shr == nil){
qunlock(&shrslk);
return -1;
}
kstrcpy(up->genbuf, shr->name, sizeof up->genbuf);
if(sch->level == Qroot)
devdir(c, shrqid(Qshr, shr->id), up->genbuf, 0, shr->owner,
shr->perm & ~0222, dp);
else
devdir(c, shrqid(Qcshr, shr->id), up->genbuf, 0, shr->owner,
shr->perm, dp);
qunlock(&shrslk);
return 1;
case Qcshr:
shr = sch->shr;
h = &shr->umh;
rlock(&h->lock);
for(m = h->mount; m != nil && s > 0; m = m->next)
s--;
if(m == nil){
runlock(&h->lock);
return -1;
}
mpt = tompt(m);
kstrcpy(up->genbuf, mpt->name, sizeof up->genbuf);
devdir(c, shrqid(Qcmpt, mpt->id), up->genbuf, 0, mpt->owner, mpt->perm, dp);
runlock(&h->lock);
return 1;
}
}
static long
shrstat(Chan *c, uint8_t *db, long n)
{
Sch *sch;
Dir dir;
int rc;
sch = tosch(c);
switch(sch->level){
default:
error(Egreg);
case Qroot:
devdir(c, c->qid, "#σ", 0, eve, 0555, &dir);
break;
case Qcroot:
devdir(c, c->qid, "#σc", 0, eve, 0777, &dir);
break;
case Qshr:
devdir(c, c->qid, sch->shr->name, 0, sch->shr->owner, sch->shr->perm & ~0222, &dir);
break;
case Qcshr:
devdir(c, c->qid, sch->shr->name, 0, sch->shr->owner, sch->shr->perm, &dir);
break;
case Qcmpt:
devdir(c, c->qid, sch->mpt->name, 0, sch->mpt->owner, sch->mpt->perm, &dir);
break;
}
rc = jehanne_convD2M(&dir, db, n);
if(rc == 0)
error(Ebadarg);
return rc;
}
static Chan*
shropen(Chan *c, unsigned long omode)
{
Chan *nc;
Sch *sch;
Shr *shr;
Mpt *mpt;
int mode;
if(c->qid.type == QTDIR && omode != OREAD)
error(Eisdir);
mode = openmode(omode);
sch = tosch(c);
switch(sch->level){
default:
error(Egreg);
case Qroot:
case Qcroot:
break;
case Qshr:
case Qcshr:
shr = sch->shr;
devpermcheck(shr->owner, shr->perm, mode);
break;
case Qcmpt:
if(omode&OTRUNC)
error(Eexist);
shr = sch->shr;
mpt = sch->mpt;
devpermcheck(mpt->owner, mpt->perm, mode);
rlock(&shr->umh.lock);
if(mpt->m.to == nil || mpt->m.to->mchan == nil){
runlock(&shr->umh.lock);
error(Eshutdown);
}
nc = mpt->m.to->mchan;
incref(&nc->r);
runlock(&shr->umh.lock);
if(mode != nc->mode){
cclose(nc);
error(Eperm);
}
cclose(c);
return nc;
}
c->mode = mode;
c->flag |= COPEN;
c->offset = 0;
return c;
}
/* chan.c */
Chan* createdir(Chan *c, Mhead *m);
static Chan*
shrcreate(Chan *c, char *name, unsigned long omode, unsigned long perm)
{
Sch *sch;
Shr *shr;
Mpt *mpt;
Mhead *h;
Mount *m;
Chan *nc;
int mode;
mode = openmode(omode);
sch = tosch(c);
switch(sch->level){
case Qcroot:
case Qcshr:
if(jehanne_strcmp(up->user, "none") == 0)
error(Eperm);
}
switch(sch->level){
default:
error(Eperm);
case Qshr:
incref(&c->r);
if(waserror()){
cclose(c);
nexterror();
}
nc = createdir(c, &sch->shr->umh);
poperror();
if(waserror()){
cclose(nc);
nexterror();
}
nc->dev->create(nc, name, omode, perm);
poperror();
cclose(c);
return nc;
case Qcroot:
if(up->pgrp->noattach)
error(Enoattach);
if((perm & DMDIR) == 0 || mode != OREAD)
error(Eperm);
if(jehanne_strlen(name) >= sizeof(up->genbuf))
error(Etoolong);
qlock(&shrslk);
if(waserror()){
qunlock(&shrslk);
nexterror();
}
for(shr = shrs; shr != nil; shr = shr->next)
if(jehanne_strcmp(name, shr->name) == 0)
error(Eexist);
shr = smalloc(sizeof(*shr));
incref(&shr->ref);
shr->id = shrid++;
kstrdup(&shr->name, name);
kstrdup(&shr->owner, up->user);
shr->perm = perm;
incref(&shr->ref);
shr->next = shrs;
shrs = shr;
poperror();
qunlock(&shrslk);
c->qid = shrqid(sch->level = Qcshr, shr->id);
sch->shr = shr;
break;
case Qcshr:
if(up->pgrp->noattach)
error(Enoattach);
if((perm & DMDIR) != 0 || mode != OWRITE)
error(Eperm);
shr = sch->shr;
if(jehanne_strcmp(shr->owner, eve) == 0 && !iseve())
error(Eperm);
devpermcheck(shr->owner, shr->perm, ORDWR);
if(jehanne_strlen(name) >= sizeof(up->genbuf))
error(Etoolong);
h = &shr->umh;
wlock(&h->lock);
if(waserror()){
wunlock(&h->lock);
nexterror();
}
for(m = h->mount; m != nil; m = m->next){
mpt = tompt(m);
if(jehanne_strcmp(name, mpt->name) == 0)
error(Eexist);
}
mpt = smalloc(sizeof(*mpt));
incref(&mpt->ref);
mpt->id = mptid++;
kstrdup(&mpt->name, name);
kstrdup(&mpt->owner, up->user);
mpt->perm = perm;
incref(&mpt->ref);
mpt->m.mflag = (h->mount == nil) ? MCREATE : 0;
mpt->m.next = h->mount;
h->mount = &mpt->m;
poperror();
wunlock(&h->lock);
c->qid = shrqid(sch->level = Qcmpt, mpt->id);
sch->mpt = mpt;
break;
}
c->flag |= COPEN;
c->mode = mode;
return c;
}
static void
shrremove(Chan *c)
{
Mount *m, **ml;
Shr *shr, **sl;
Sch *sch;
Mpt *mpt;
Mhead *h;
sch = tosch(c);
if(waserror()){
shrclunk(c);
nexterror();
}
switch(sch->level){
default:
error(Eperm);
case Qcshr:
case Qcmpt:
shr = sch->shr;
if(!iseve()){
if(jehanne_strcmp(shr->owner, eve) == 0)
error(Eperm);
devpermcheck(shr->owner, shr->perm, ORDWR);
}
}
switch(sch->level){
case Qcshr:
h = &shr->umh;
qlock(&shrslk);
rlock(&h->lock);
if(h->mount != nil){
runlock(&h->lock);
qunlock(&shrslk);
error("directory not empty");
}
runlock(&h->lock);
for(sl = &shrs; *sl != nil; sl = &((*sl)->next))
if(*sl == shr){
*sl = shr->next;
shr->next = nil;
putshr(shr);
break;
}
qunlock(&shrslk);
break;
case Qcmpt:
mpt = sch->mpt;
m = &mpt->m;
h = &shr->umh;
wlock(&h->lock);
for(ml = &h->mount; *ml != nil; ml = &((*ml)->next))
if(*ml == m){
*ml = m->next;
m->next = nil;
putmpt(mpt);
break;
}
wunlock(&h->lock);
break;
}
poperror();
shrclunk(c);
}
static long
shrwstat(Chan *c, uint8_t *dp, long n)
{
char *strs;
Mhead *h;
Sch *sch;
Ent *ent;
Dir d;
strs = smalloc(n);
if(waserror()){
jehanne_free(strs);
nexterror();
}
n = jehanne_convM2D(dp, n, &d, strs);
if(n == 0)
error(Eshortstat);
h = nil;
sch = tosch(c);
switch(sch->level){
default:
error(Eperm);
case Qcshr:
ent = sch->shr;
qlock(&shrslk);
if(waserror()){
qunlock(&shrslk);
nexterror();
}
break;
case Qcmpt:
ent = sch->mpt;
h = &sch->shr->umh;
wlock(&h->lock);
if(waserror()){
wunlock(&h->lock);
nexterror();
}
break;
}
if(jehanne_strcmp(ent->owner, up->user) && !iseve())
error(Eperm);
if(d.name != nil && *d.name && jehanne_strcmp(ent->name, d.name) != 0) {
if(jehanne_strchr(d.name, '/') != nil)
error(Ebadchar);
if(jehanne_strlen(d.name) >= sizeof(up->genbuf))
error(Etoolong);
kstrdup(&ent->name, d.name);
}
if(d.uid != nil && *d.uid)
kstrdup(&ent->owner, d.uid);
if(d.mode != ~0U)
ent->perm = d.mode & 0777;
switch(sch->level){
case Qcshr:
poperror();
qunlock(&shrslk);
break;
case Qcmpt:
poperror();
wunlock(&h->lock);
break;
}
poperror();
jehanne_free(strs);
return n;
}
static long
shrread(Chan *c, void *va, long n, int64_t _)
{
Mhead *omh;
Sch *sch;
sch = tosch(c);
switch(sch->level){
default:
error(Egreg);
case Qroot:
case Qcroot:
case Qcshr:
return devdirread(c, va, n, 0, 0, shrgen);
case Qshr:
omh = c->umh;
c->umh = &sch->shr->umh;
if(waserror()){
c->umh = omh;
nexterror();
}
n = unionread(c, va, n);
poperror();
c->umh = omh;
return n;
}
}
static long
shrwrite(Chan *c, void *va, long n, int64_t _)
{
Sch *sch;
char *buf, *p, *aname;
int fd;
Chan *bc, *c0;
Mhead *h;
Mount *m;
if(up->pgrp->noattach)
error(Enoattach);
sch = tosch(c);
if(sch->level != Qcmpt)
error(Egreg);
buf = smalloc(n+1);
if(waserror()){
jehanne_free(buf);
nexterror();
}
jehanne_memmove(buf, va, n);
buf[n] = 0;
fd = jehanne_strtol(buf, &p, 10);
if(p == buf || (*p != 0 && *p != '\n'))
error(Ebadarg);
if(*p == '\n' && *(p+1) != 0)
aname = p + 1;
else
aname = nil;
bc = fdtochan(fd, ORDWR, 0, 1);
if(waserror()) {
cclose(bc);
nexterror();
}
c0 = mntattach(bc, nil, aname, 0);
poperror();
cclose(bc);
poperror();
jehanne_free(buf);
if(c0 == nil)
error(Egreg);
m = &sch->mpt->m;
h = &sch->shr->umh;
wlock(&h->lock);
bc = m->to;
m->to = c0;
wunlock(&h->lock);
if(bc != nil)
cclose(bc);
return n;
}
static void
shrclose(Chan *c)
{
if(c->flag & CRCLOSE)
shrremove(c);
else
shrclunk(c);
}
Dev shrdevtab = {
L'σ',
"shr",
devreset,
shrinit,
devshutdown,
shrattach,
shrwalk,
shrstat,
shropen,
shrcreate,
shrclose,
shrread,
devbread,
shrwrite,
devbwrite,
shrremove,
shrwstat,
};
static void
chowner(Ent *ent, char *old, char *new)
{
if(ent->owner != nil && jehanne_strcmp(old, ent->owner) == 0)
kstrdup(&ent->owner, new);
}
void
shrrenameuser(char *old, char *new)
{
Shr *shr;
Mount *m;
qlock(&shrslk);
for(shr = shrs; shr != nil; shr = shr->next){
wlock(&shr->umh.lock);
for(m = shr->umh.mount; m != nil; m = m->next)
chowner(tompt(m), old, new);
wunlock(&shr->umh.lock);
chowner(shr, old, new);
}
qunlock(&shrslk);
}