kernel: ensure that all accesses to Mhead.mount is done with Mhead.lock acquired
The Mhead structures have two sources of references to them: - from Pgrp.mnthash hash-table - from a channels Chan.umh pointer as returned by namec() for a union directory Unless one holds the Mhead.lock RWLock, the Mhead.mount chain can be mutated by eigther cmount(), cunmount() or closepgrp(). Readers, skipping acquiering the lock where: mountfix(): responsible for rewriting directory entries for union directory reads; was walking the Mhead.mount chain to detect if the passed channel itself appears in the mount list. cmount(): had a check and copy when "new" chan was a union itself and if the MCREATE flag is set and would copy the mount table. All this needs to be done with Mhead read-locked while copying the mount entries. devproc(): in the handler for reading /proc/n/ns file. namec(): while checking if the Chan->umh should be initialized. In addition to this, cmount() is changed to do the mountfree() of the original mount chain when MREPL is done after releasing the locks. Also, some cosmetic changes...
This commit is contained in:
parent
b638114186
commit
55c3138c64
5 changed files with 105 additions and 100 deletions
|
@ -608,6 +608,7 @@ newmhead(Chan *from)
|
|||
mh->ref = 1;
|
||||
mh->from = from;
|
||||
incref(from);
|
||||
setmalloctag(mh, getcallerpc(&from));
|
||||
return mh;
|
||||
}
|
||||
|
||||
|
@ -633,36 +634,45 @@ newmhead(Chan *from)
|
|||
void
|
||||
putmhead(Mhead *m)
|
||||
{
|
||||
if(m != nil && decref(m) == 0){
|
||||
if(m == nil)
|
||||
return;
|
||||
if(decref(m))
|
||||
return;
|
||||
assert(m->mount == nil);
|
||||
cclose(m->from);
|
||||
free(m);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
cmount(Chan **newp, Chan *old, int flag, char *spec)
|
||||
cmount(Chan *new, Chan *old, int flag, char *spec)
|
||||
{
|
||||
int order, flg;
|
||||
Chan *new;
|
||||
int order;
|
||||
Mhead *m, **l, *mh;
|
||||
Mount *nm, *f, *um, **h;
|
||||
Mount *nm, *f, *um;
|
||||
Pgrp *pg;
|
||||
|
||||
if(QTDIR & (old->qid.type^(*newp)->qid.type))
|
||||
error(Emount);
|
||||
|
||||
if(old->umh != nil)
|
||||
print("cmount: unexpected umh, caller %#p\n", getcallerpc(&newp));
|
||||
print("cmount: unexpected umh, caller %#p\n", getcallerpc(&new));
|
||||
|
||||
if(QTDIR & (old->qid.type^new->qid.type))
|
||||
error(Emount);
|
||||
|
||||
order = flag&MORDER;
|
||||
|
||||
if((old->qid.type&QTDIR) == 0 && order != MREPL)
|
||||
error(Emount);
|
||||
|
||||
new = *newp;
|
||||
nm = newmount(new, flag, spec);
|
||||
mh = new->umh;
|
||||
|
||||
if(mh != nil) {
|
||||
rlock(&mh->lock);
|
||||
if(waserror()) {
|
||||
runlock(&mh->lock);
|
||||
mountfree(nm);
|
||||
nexterror();
|
||||
}
|
||||
um = mh->mount;
|
||||
if(um != nil){
|
||||
/*
|
||||
* Not allowed to bind when the old directory is itself a union.
|
||||
* (Maybe it should be allowed, but I don't see what the semantics
|
||||
|
@ -681,80 +691,67 @@ cmount(Chan **newp, Chan *old, int flag, char *spec)
|
|||
* This is far more complicated than it should be, but I don't
|
||||
* see an easier way at the moment.
|
||||
*/
|
||||
if((flag&MCREATE) != 0 && mh != nil && mh->mount != nil
|
||||
&& (mh->mount->next != nil || (mh->mount->mflag&MCREATE) == 0))
|
||||
if((flag&MCREATE) != 0 && (um->next != nil || (um->mflag&MCREATE) == 0))
|
||||
error(Emount);
|
||||
|
||||
/*
|
||||
* copy a union when binding it onto a directory
|
||||
*/
|
||||
f = nm;
|
||||
for(um = um->next; um != nil; um = um->next){
|
||||
f->next = newmount(um->to, order==MREPL? MAFTER: order, um->spec);
|
||||
f = f->next;
|
||||
}
|
||||
}
|
||||
runlock(&mh->lock);
|
||||
poperror();
|
||||
}
|
||||
|
||||
pg = up->pgrp;
|
||||
wlock(&pg->ns);
|
||||
|
||||
l = &MOUNTH(pg, old->qid);
|
||||
for(m = *l; m != nil; m = m->hash){
|
||||
if(eqchan(m->from, old, 1))
|
||||
break;
|
||||
l = &m->hash;
|
||||
}
|
||||
|
||||
if(m == nil){
|
||||
/*
|
||||
* nothing mounted here yet. create a mount
|
||||
* head and add to the hash table.
|
||||
*/
|
||||
m = newmhead(old);
|
||||
*l = m;
|
||||
|
||||
/*
|
||||
* if this is a union mount, add the old
|
||||
* node to the mount chain.
|
||||
*/
|
||||
if(order != MREPL)
|
||||
m->mount = newmount(old, 0, nil);
|
||||
*l = m;
|
||||
}
|
||||
wlock(&m->lock);
|
||||
if(waserror()){
|
||||
wunlock(&m->lock);
|
||||
nexterror();
|
||||
}
|
||||
wunlock(&pg->ns);
|
||||
|
||||
nm = newmount(new, flag, spec);
|
||||
if(mh != nil && mh->mount != nil){
|
||||
/*
|
||||
* copy a union when binding it onto a directory
|
||||
*/
|
||||
flg = order;
|
||||
if(order == MREPL)
|
||||
flg = MAFTER;
|
||||
h = &nm->next;
|
||||
um = mh->mount;
|
||||
for(um = um->next; um != nil; um = um->next){
|
||||
f = newmount(um->to, flg, um->spec);
|
||||
*h = f;
|
||||
h = &f->next;
|
||||
}
|
||||
}
|
||||
|
||||
if(m->mount != nil && order == MREPL){
|
||||
mountfree(m->mount);
|
||||
m->mount = nil;
|
||||
}
|
||||
|
||||
if(flag & MCREATE)
|
||||
nm->mflag |= MCREATE;
|
||||
|
||||
if(m->mount != nil && order == MAFTER){
|
||||
for(f = m->mount; f->next != nil; f = f->next)
|
||||
um = m->mount;
|
||||
if(um != nil && order == MAFTER){
|
||||
for(f = um; f->next != nil; f = f->next)
|
||||
;
|
||||
f->next = nm;
|
||||
}else{
|
||||
um = nil;
|
||||
} else {
|
||||
if(order != MREPL){
|
||||
for(f = nm; f->next != nil; f = f->next)
|
||||
;
|
||||
f->next = m->mount;
|
||||
f->next = um;
|
||||
um = nil;
|
||||
}
|
||||
m->mount = nm;
|
||||
}
|
||||
order = nm->mountid;
|
||||
wunlock(&m->lock);
|
||||
poperror();
|
||||
return nm->mountid;
|
||||
wunlock(&pg->ns);
|
||||
|
||||
mountfree(um);
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -857,13 +854,13 @@ findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid)
|
|||
rlock(&pg->ns);
|
||||
for(m = MOUNTH(pg, qid); m != nil; m = m->hash){
|
||||
if(eqchantdqid(m->from, type, dev, qid, 1)){
|
||||
rlock(&m->lock);
|
||||
runlock(&pg->ns);
|
||||
if(mp != nil)
|
||||
incref(m);
|
||||
rlock(&m->lock);
|
||||
to = m->mount->to;
|
||||
incref(to);
|
||||
runlock(&m->lock);
|
||||
runlock(&pg->ns);
|
||||
if(mp != nil){
|
||||
putmhead(*mp);
|
||||
*mp = m;
|
||||
|
@ -1078,7 +1075,7 @@ walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
|
|||
n = wq->nqid;
|
||||
nc = wq->clone;
|
||||
}else{ /* stopped early, at a mount point */
|
||||
didmount = 1;
|
||||
assert(didmount);
|
||||
if(wq->clone != nil){
|
||||
cclose(wq->clone);
|
||||
wq->clone = nil;
|
||||
|
@ -1459,10 +1456,16 @@ namec(char *aname, int amode, int omode, ulong perm)
|
|||
case Aopen:
|
||||
case Acreate:
|
||||
/* only save the mount head if it's a multiple element union */
|
||||
if(m != nil && m->mount != nil && m->mount->next != nil)
|
||||
if(m != nil) {
|
||||
rlock(&m->lock);
|
||||
if(m->mount != nil && m->mount->next != nil) {
|
||||
c->umh = m;
|
||||
else
|
||||
runlock(&m->lock);
|
||||
} else {
|
||||
runlock(&m->lock);
|
||||
putmhead(m);
|
||||
}
|
||||
}
|
||||
|
||||
/* save registers else error() in open has wrong value of c saved */
|
||||
saveregisters();
|
||||
|
|
|
@ -667,6 +667,7 @@ readns1(Chan *c, Proc *p, char *buf, int nbuf)
|
|||
cm = nil;
|
||||
for(i = 0; i < MNTHASH; i++) {
|
||||
for(f = pg->mnthash[i]; f != nil; f = f->hash) {
|
||||
rlock(&f->lock);
|
||||
for(t = f->mount; t != nil; t = t->next) {
|
||||
if(t->mountid >= minid && t->mountid < bestmid) {
|
||||
bestmid = t->mountid;
|
||||
|
@ -674,6 +675,7 @@ readns1(Chan *c, Proc *p, char *buf, int nbuf)
|
|||
mh = f;
|
||||
}
|
||||
}
|
||||
runlock(&f->lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,16 +62,12 @@ closepgrp(Pgrp *p)
|
|||
free(p);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
pgrpinsert(Mount **order, Mount *m)
|
||||
{
|
||||
Mount *f;
|
||||
|
||||
m->order = nil;
|
||||
if(*order == nil) {
|
||||
*order = m;
|
||||
return;
|
||||
}
|
||||
for(f = *order; f != nil; f = f->order) {
|
||||
if(m->mountid < f->mountid) {
|
||||
m->order = f;
|
||||
|
@ -90,15 +86,14 @@ void
|
|||
pgrpcpy(Pgrp *to, Pgrp *from)
|
||||
{
|
||||
Mount *n, *m, **link, *order;
|
||||
Mhead *f, **tom, **l, *mh;
|
||||
Mhead *f, **l, *mh;
|
||||
int i;
|
||||
|
||||
wlock(&to->ns);
|
||||
rlock(&from->ns);
|
||||
order = nil;
|
||||
tom = to->mnthash;
|
||||
for(i = 0; i < MNTHASH; i++) {
|
||||
l = tom++;
|
||||
l = &to->mnthash[i];
|
||||
for(f = from->mnthash[i]; f != nil; f = f->hash) {
|
||||
rlock(&f->lock);
|
||||
mh = newmhead(f->from);
|
||||
|
@ -248,7 +243,7 @@ newmount(Chan *to, int flag, char *spec)
|
|||
m->mflag = flag;
|
||||
if(spec != nil)
|
||||
kstrdup(&m->spec, spec);
|
||||
|
||||
setmalloctag(m, getcallerpc(&to));
|
||||
return m;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ void closepgrp(Pgrp*);
|
|||
void closergrp(Rgrp*);
|
||||
long clrfpintr(void);
|
||||
void cmderror(Cmdbuf*, char*);
|
||||
int cmount(Chan**, Chan*, int, char*);
|
||||
int cmount(Chan*, Chan*, int, char*);
|
||||
void confinit(void);
|
||||
int consactive(void);
|
||||
void (*consdebug)(void);
|
||||
|
|
|
@ -583,9 +583,14 @@ mountfix(Chan *c, uchar *op, long n, long maxn)
|
|||
* If it's a union directory and the original is
|
||||
* in the union, don't rewrite anything.
|
||||
*/
|
||||
for(m = mh->mount; m != nil; m = m->next)
|
||||
if(eqchantdqid(m->to, d.type, d.dev, d.qid, 1))
|
||||
rlock(&mh->lock);
|
||||
for(m = mh->mount; m != nil; m = m->next){
|
||||
if(eqchantdqid(m->to, d.type, d.dev, d.qid, 1)){
|
||||
runlock(&mh->lock);
|
||||
goto Norewrite;
|
||||
}
|
||||
}
|
||||
runlock(&mh->lock);
|
||||
|
||||
name = dirname(p, &nname);
|
||||
/*
|
||||
|
@ -594,8 +599,8 @@ mountfix(Chan *c, uchar *op, long n, long maxn)
|
|||
* what can we do? Nothing, really. Might as well skip it.
|
||||
*/
|
||||
if(buf == nil){
|
||||
buf = smalloc(4096);
|
||||
nbuf = 4096;
|
||||
buf = smalloc(nbuf);
|
||||
}
|
||||
if(waserror())
|
||||
goto Norewrite;
|
||||
|
@ -1025,7 +1030,7 @@ syschdir(va_list list)
|
|||
return 0;
|
||||
}
|
||||
|
||||
long
|
||||
static int
|
||||
bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, int flag, char* spec)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1080,7 +1085,7 @@ bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, int flag, char*
|
|||
nexterror();
|
||||
}
|
||||
|
||||
ret = cmount(&c0, c1, flag, spec);
|
||||
ret = cmount(c0, c1, flag, spec);
|
||||
|
||||
poperror();
|
||||
cclose(c1);
|
||||
|
|
Loading…
Reference in a new issue