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

852 lines
15 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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);
}