📄 chan.c
字号:
incref(&c->ref); cname = c->name; incref(&cname->ref); mh = nil; /* * While we haven't gotten all the way down the path: * 1. step through a mount point, if any * 2. send a walk request for initial dotdot or initial prefix without dotdot * 3. move to the first mountpoint along the way. * 4. repeat. * * An invariant is that each time through the loop, c is on the undomount * side of the mount point, and c's name is cname. */ for(nhave=0; nhave<nnames; nhave+=n){ if((c->qid.type&QTDIR)==0){ if(nerror) *nerror = nhave; cnameclose(cname); cclose(c); strcpy(up->errstr, Enotdir); if(mh != nil){print("walk 1\n"); putmhead(mh);} return -1; } ntry = nnames - nhave; if(ntry > MAXWELEM) ntry = MAXWELEM; dotdot = 0; for(i=0; i<ntry; i++){ if(isdotdot(names[nhave+i])){ if(i==0) { dotdot = 1; ntry = 1; } else ntry = i; break; } } if(!dotdot && !nomount) domount(&c, &mh); type = c->type; dev = c->dev; if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){ /* try a union mount, if any */ if(mh && !nomount){ /* * mh->mount == c, so start at mh->mount->next */ rlock(&mh->lock); for(f = mh->mount->next; f; f = f->next) if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil) break; runlock(&mh->lock); if(f != nil){ type = f->to->type; dev = f->to->dev; } } if(wq == nil){ cclose(c); cnameclose(cname); if(nerror) *nerror = nhave+1; if(mh != nil) putmhead(mh); return -1; } } nmh = nil; if(dotdot) { assert(wq->nqid == 1); assert(wq->clone != nil); cname = addelem(cname, ".."); nc = undomount(wq->clone, cname); n = 1; } else { nc = nil; if(!nomount) for(i=0; i<wq->nqid && i<ntry-1; i++) if(findmount(&nc, &nmh, type, dev, wq->qid[i])) break; if(nc == nil){ /* no mount points along path */ if(wq->clone == nil){ cclose(c); cnameclose(cname); if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ if(nerror) *nerror = nhave+wq->nqid+1; strcpy(up->errstr, Edoesnotexist); }else{ if(nerror) *nerror = nhave+wq->nqid; strcpy(up->errstr, Enotdir); } free(wq); if(mh != nil) putmhead(mh); return -1; } n = wq->nqid; nc = wq->clone; }else{ /* stopped early, at a mount point */ if(wq->clone != nil){ cclose(wq->clone); wq->clone = nil; } n = i+1; } for(i=0; i<n; i++) cname = addelem(cname, names[nhave+i]); } cclose(c); c = nc; putmhead(mh); mh = nmh; free(wq); } putmhead(mh); c = cunique(c); if(c->umh != nil){ //BUG print("walk umh\n"); putmhead(c->umh); c->umh = nil; } cnameclose(c->name); c->name = cname; cclose(*cp); *cp = c; if(nerror) *nerror = 0; return 0;}/* * c is a mounted non-creatable directory. find a creatable one. */Chan*createdir(Chan *c, Mhead *m){ Chan *nc; Mount *f; rlock(&m->lock); if(waserror()) { runlock(&m->lock); nexterror(); } for(f = m->mount; f; f = f->next) { if(f->mflag&MCREATE) { nc = cclone(f->to); runlock(&m->lock); poperror(); cclose(c); return nc; } } error(Enocreate); return 0;}voidsaveregisters(void){}/* * In place, rewrite name to compress multiple /, eliminate ., and process .. */voidcleancname(Cname *n){ char *p; if(n->s[0] == '#'){ p = strchr(n->s, '/'); if(p == nil) return; cleanname(p); /* * The correct name is #i rather than #i/, * but the correct name of #/ is #/. */ if(strcmp(p, "/")==0 && n->s[1] != '/') *p = '\0'; }else cleanname(n->s); n->len = strlen(n->s);}static voidgrowparse(Elemlist *e){ char **new; int *inew; enum { Delta = 8 }; if(e->nelems % Delta == 0){ new = smalloc((e->nelems+Delta) * sizeof(char*)); memmove(new, e->elems, e->nelems*sizeof(char*)); free(e->elems); e->elems = new; inew = smalloc((e->nelems+Delta+1) * sizeof(int)); memmove(inew, e->off, e->nelems*sizeof(int)); free(e->off); e->off = inew; }}/* * The name is known to be valid. * Copy the name so slashes can be overwritten. * An empty string will set nelem=0. * A path ending in / or /. or /.//./ etc. will have * e.mustbedir = 1, so that we correctly * reject, e.g., "/adm/users/." when /adm/users is a file * rather than a directory. */static voidparsename(char *name, Elemlist *e){ char *slash; kstrdup(&e->name, name); name = e->name; e->nelems = 0; e->elems = nil; e->off = smalloc(sizeof(int)); e->off[0] = skipslash(name) - name; for(;;){ name = skipslash(name); if(*name=='\0'){ e->mustbedir = 1; break; } growparse(e); e->elems[e->nelems++] = name; slash = utfrune(name, '/'); if(slash == nil){ e->off[e->nelems] = name+strlen(name) - e->name; e->mustbedir = 0; break; } e->off[e->nelems] = slash - e->name; *slash++ = '\0'; name = slash; }}void*memrchr(void *va, int c, long n){ uchar *a, *e; a = va; for(e=a+n-1; e>a; e--) if(*e == c) return e; return nil;}/* * Turn a name into a channel. * &name[0] is known to be a valid address. It may be a kernel address. * * Opening with amode Aopen, Acreate, or Aremove guarantees * that the result will be the only reference to that particular fid. * This is necessary since we might pass the result to * devtab[]->remove(). * * Opening Atodir, Amount, or Aaccess does not guarantee this. * * Opening Aaccess can, under certain conditions, return a * correct Chan* but with an incorrect Cname attached. * Since the functions that open Aaccess (sysstat, syswstat, sys_stat) * do not use the Cname*, this avoids an unnecessary clone. */Chan*namec(char *aname, int amode, int omode, ulong perm){ int n, prefix, len, t, nomount, npath; Chan *c, *cnew; Cname *cname; Elemlist e; Rune r; Mhead *m; char *createerr, tmperrbuf[ERRMAX]; char *name; name = aname; if(name[0] == '\0') error("empty file name"); validname(name, 1); /* * Find the starting off point (the current slash, the root of * a device tree, or the current dot) as well as the name to * evaluate starting there. */ nomount = 0; switch(name[0]){ case '/': c = up->slash; incref(&c->ref); break; case '#': nomount = 1; up->genbuf[0] = '\0'; n = 0; while(*name!='\0' && (*name != '/' || n < 2)){ if(n >= sizeof(up->genbuf)-1) error(Efilename); up->genbuf[n++] = *name++; } up->genbuf[n] = '\0'; /* * noattach is sandboxing. * * the OK exceptions are: * | it only gives access to pipes you create * d this process's file descriptors * e this process's environment * the iffy exceptions are: * c time and pid, but also cons and consctl * p control of your own processes (and unfortunately * any others left unprotected) */ n = chartorune(&r, up->genbuf+1)+1; /* actually / is caught by parsing earlier */ if(utfrune("M", r)) error(Enoattach); if(up->pgrp->noattach && utfrune("|decp", r)==nil) error(Enoattach); t = devno(r, 1); if(t == -1) error(Ebadsharp); c = devtab[t]->attach(up->genbuf+n); break; default: c = up->dot; incref(&c->ref); break; } prefix = name - aname; e.name = nil; e.elems = nil; e.off = nil; e.nelems = 0; if(waserror()){ cclose(c); free(e.name); free(e.elems); free(e.off);//dumpmount(); nexterror(); } /* * Build a list of elements in the path. */ parsename(name, &e); /* * On create, .... */ if(amode == Acreate){ /* perm must have DMDIR if last element is / or /. */ if(e.mustbedir && !(perm&DMDIR)){ npath = e.nelems; strcpy(tmperrbuf, "create without DMDIR"); goto NameError; } /* don't try to walk the last path element just yet. */ if(e.nelems == 0) error(Eexist); e.nelems--; } if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){ if(npath < 0 || npath > e.nelems){ print("namec %s walk error npath=%d\n", aname, npath); nexterror(); } strcpy(tmperrbuf, up->errstr); NameError: len = prefix+e.off[npath]; if(len < ERRMAX/3 || (name=memrchr(aname, '/', len))==nil || name==aname) snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname); else snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name); snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf); nexterror(); } if(e.mustbedir && !(c->qid.type&QTDIR)){ npath = e.nelems; strcpy(tmperrbuf, "not a directory"); goto NameError; } if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){ npath = e.nelems; error("cannot exec directory"); } switch(amode){ case Aaccess: if(!nomount) domount(&c, nil); break; case Abind: m = nil; if(!nomount) domount(&c, &m); if(c->umh != nil) putmhead(c->umh); c->umh = m; break; case Aremove: case Aopen: Open: /* save the name; domount might change c */ cname = c->name; incref(&cname->ref); m = nil; if(!nomount) domount(&c, &m); /* our own copy to open or remove */ c = cunique(c); /* now it's our copy anyway, we can put the name back */ cnameclose(c->name); c->name = cname; switch(amode){ case Aremove: putmhead(m); break; case Aopen: case Acreate:if(c->umh != nil){ print("cunique umh Open\n"); putmhead(c->umh); c->umh = nil;} /* only save the mount head if it's a multiple element union */ if(m && m->mount && m->mount->next) c->umh = m; else putmhead(m); /* save registers else error() in open has wrong value of c saved */ saveregisters(); if(omode == OEXEC) c->flag &= ~CCACHE; c = devtab[c->type]->open(c, omode&~OCEXEC); if(omode & OCEXEC) c->flag |= CCEXEC; if(omode & ORCLOSE) c->flag |= CRCLOSE; break; } break; case Atodir: /* * Directories (e.g. for cd) are left before the mount point, * so one may mount on / or . and see the effect. */ if(!(c->qid.type & QTDIR)) error(Enotdir); break; case Amount: /* * When mounting on an already mounted upon directory, * one wants subsequent mounts to be attached to the * original directory, not the replacement. Don't domount. */ break; case Acreate: /* * We've already walked all but the last element. * If the last exists, try to open it OTRUNC. * If omode&OEXCL is set, just give up. */ e.nelems++; if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){ if(omode&OEXCL) error(Eexist); omode |= OTRUNC; goto Open; } /* * The semantics of the create(2) system call are that if the * file exists and can be written, it is to be opened with truncation. * On the other hand, the create(5) message fails if the file exists. * If we get two create(2) calls happening simultaneously, * they might both get here and send create(5) messages, but only * one of the messages will succeed. To provide the expected create(2) * semantics, the call with the failed message needs to try the above * walk again, opening for truncation. This correctly solves the * create/create race, in the sense that any observable outcome can * be explained as one happening before the other. * The create/create race is quite common. For example, it happens * when two rc subshells simultaneously update the same * environment variable. * * The implementation still admits a create/create/remove race: * (A) walk to file, fails * (B) walk to file, fails * (A) create file, succeeds, returns * (B) create file, fails * (A) remove file, succeeds, returns * (B) walk to file, return failure. * * This is hardly as common as the create/create race, and is really * not too much worse than what might happen if (B) got a hold of a * file descriptor and then the file was removed -- either way (B) can't do * anything with the result of the create call. So we don't care about this race. * * Applications that care about more fine-grained decision of the races * can use the OEXCL flag to get at the underlying create(5) semantics; * by default we provide the common case. * * We need to stay behind the mount point in case we * need to do the first walk again (should the create fail). * * We also need to cross the mount point and find the directory * in the union in which we should be creating. * * The channel staying behind is c, the one moving forward is cnew. */ m = nil; cnew = nil; /* is this assignment necessary? */ if(!waserror()){ /* try create */ if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid)) cnew = createdir(cnew, m); else{ cnew = c; incref(&cnew->ref); } /* * We need our own copy of the Chan because we're * about to send a create, which will move it. Once we have * our own copy, we can fix the name, which might be wrong * if findmount gave us a new Chan. */ cnew = cunique(cnew); cnameclose(cnew->name); cnew->name = c->name; incref(&cnew->name->ref); devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm); poperror(); if(omode & OCEXEC) cnew->flag |= CCEXEC; if(omode & ORCLOSE) cnew->flag |= CRCLOSE; if(m) putmhead(m); cclose(c); c = cnew; c->name = addelem(c->name, e.elems[e.nelems-1]); break; }else{ /* create failed */ cclose(cnew); if(m) putmhead(m); if(omode & OEXCL) nexterror(); /* save error */ createerr = up->errstr; up->errstr = tmperrbuf; /* note: we depend that walk does not error */ if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ up->errstr = createerr; error(createerr); /* report true error */ } up->errstr = createerr; omode |= OTRUNC; goto Open; } panic("namec: not reached"); default: panic("unknown namec access %d\n", amode); } poperror(); /* place final element in genbuf for e.g. exec */ if(e.nelems > 0) kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf); else kstrcpy(up->genbuf, ".", sizeof up->genbuf); free(e.name); free(e.elems); free(e.off); return c;}/* * name is valid. skip leading / and ./ as much as possible */char*skipslash(char *name){ while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/'))) name++; return name;}char isfrog[256]={ /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0 */ /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x08 */ /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x10 */ /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 1, /* 0x28 (1 is '/', 0x2F) */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 1, /* 0x78 (1 is DEL, 0x7F) */};/* * Check that the name * a) is in valid memory. * b) is shorter than 2^16 bytes, so it can fit in a 9P string field. * c) contains no frogs. * The first byte is known to be addressible by the requester, so the * routine works for kernel and user memory both. * The parameter slashok flags whether a slash character is an error * or a valid character. */voidvalidname(char *aname, int slashok){ char *ename, *name; int c; Rune r; name = aname;/* if(((ulong)name & KZERO) != KZERO) { p = name; t = BY2PG-((ulong)p&(BY2PG-1)); while((ename=vmemchr(p, 0, t)) == nil) { p += t; t = BY2PG; } }else*/ ename = memchr(name, 0, (1<<16)); if(ename==nil || ename-name>=(1<<16)) error("name too long"); while(*name){ /* all characters above '~' are ok */ c = *(uchar*)name; if(c >= Runeself) name += chartorune(&r, name); else{ if(isfrog[c]) if(!slashok || c!='/'){ snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname); error(up->genbuf); } name++; } }}voidisdir(Chan *c){ if(c->qid.type & QTDIR) return; error(Enotdir);}/* * This is necessary because there are many * pointers to the top of a given mount list: * * - the mhead in the namespace hash table * - the mhead in chans returned from findmount: * used in namec and then by unionread. * - the mhead in chans returned from createdir: * used in the open/create race protect, which is gone. * * The RWlock in the Mhead protects the mount list it contains. * The mount list is deleted when we cunmount. * The RWlock ensures that nothing is using the mount list at that time. * * It is okay to replace c->mh with whatever you want as * long as you are sure you have a unique reference to it. * * This comment might belong somewhere else. */voidputmhead(Mhead *m){ if(m && decref(&m->ref) == 0){ m->mount = (Mount*)0xCafeBeef; free(m); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -