📄 chan.c
字号:
e->off[e->nelems] = name+strlen(name) - e->name; 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; } if(0 && chandebug){ int i; print("parsename %s:", e->name); for(i=0; i<=e->nelems; i++) print(" %d", e->off[i]); print("\n"); }}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;}voidnamelenerror(char *aname, int len, char *err){ char *ename, *name, *next; int i, errlen; /* * If the name is short enough, just use the whole thing. */ errlen = strlen(err); if(len < ERRMAX/3 || len+errlen < 2*ERRMAX/3) snprint(up->genbuf, sizeof up->genbuf, "%.*s", utfnlen(aname, len), aname); else{ /* * Print a suffix of the name, but try to get a little info. */ ename = aname+len; next = ename; do{ name = next; next = memrchr(aname, '/', name-aname); if(next == nil) next = aname; len = ename-next; }while(len < ERRMAX/3 || len + errlen < 2*ERRMAX/3); /* * If the name is ridiculously long, chop it. */ if(name == ename){ name = ename-ERRMAX/4; if(name <= aname) panic("bad math in namelenerror"); /* walk out of current UTF sequence */ for(i=0; (*name&0xC0)==0x80 && i<3; i++) name++; } snprint(up->genbuf, sizeof up->genbuf, "...%.*s", utfnlen(name, ename-name), name); } snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, err); nexterror();}voidnameerror(char *name, char *err){ namelenerror(name, strlen(name), err);}/* * 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, Aremove, or Aaccess 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 or Amount does not guarantee this. * * Under certain circumstances, opening Aaccess will cause * an unnecessary clone in order to get a cunique Chan so it * can attach the correct name. Sysstat and sys_stat need the * correct name so they can rewrite the stat info. */Chan*namec(char *aname, int amode, int omode, ulong perm){ int len, n, t, nomount; Chan *c, *cnew; Path *path; Elemlist e; Rune r; Mhead *m; char *createerr, tmperrbuf[ERRMAX]; char *name; if(aname[0] == '\0') error("empty file name"); aname = validnamedup(aname, 1); if(waserror()){ free(aname); nexterror(); }if(tracesyscalls) print("\tnamec %s\n", aname); DBG("namec %s %d %d\n", aname, amode, omode); name = aname; /* * 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; } e.aname = aname; e.prefix = name - aname; e.name = nil; e.elems = nil; e.off = nil; e.nelems = 0; e.nerror = 0; if(waserror()){ cclose(c); free(e.name); free(e.elems); /* * Prepare nice error, showing first e.nerror elements of name. */ if(e.nerror == 0) nexterror(); strcpy(tmperrbuf, up->errstr); if(e.off[e.nerror]==0) print("nerror=%d but off=%d\n", e.nerror, e.off[e.nerror]); if(0 && chandebug) print("showing %d+%d/%d (of %d) of %s (%d %d)\n", e.prefix, e.off[e.nerror], e.nerror, e.nelems, aname, e.off[0], e.off[1]); len = e.prefix+e.off[e.nerror]; free(e.off); namelenerror(aname, len, tmperrbuf); } /* * Build a list of elements in the name. */ parsename(name, &e); /* * On create, .... */ if(amode == Acreate){ /* perm must have DMDIR if last element is / or /. */ if(e.mustbedir && !(perm&DMDIR)){ e.nerror = e.nelems; error("create without DMDIR"); } /* 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, &e.nerror) < 0){ if(e.nerror < 0 || e.nerror > e.nelems){ print("namec %s walk error nerror=%d\n", aname, e.nerror); e.nerror = 0; } nexterror(); } if(e.mustbedir && !(c->qid.type&QTDIR)) error("not a directory"); if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)) error("cannot exec directory"); switch(amode){ case Abind: /* no need to maintain path - cannot dotdot an Abind */ m = nil; if(!nomount) domount(&c, &m, nil); if(c->umh != nil) putmhead(c->umh); c->umh = m; break; case Aaccess: case Aremove: case Aopen: Open: /* save&update the name; domount might change c */ path = c->path; incref(&path->ref); m = nil; if(!nomount) domount(&c, &m, &path); /* our own copy to open or remove */ c = cunique(c); /* now it's our copy anyway, we can put the name back */ pathclose(c->path); c->path = path; /* record whether c is on a mount point */ c->ismtpt = m!=nil; switch(amode){ case Aaccess: 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++; e.nerror++; 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); pathclose(cnew->path); cnew->path = c->path; incref(&cnew->path->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->path = addelem(c->path, e.elems[e.nelems-1], nil); break; } /* 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; default: panic("unknown namec access %d\n", amode); } /* 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); poperror(); /* e c */ free(aname); poperror(); /* aname */ 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, /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, ['/'] 1, [0x7f] 1,};/* * 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. * * The parameter dup flags whether the string should be copied * out of user space before being scanned the second time. * (Otherwise a malicious thread could remove the NUL, causing us * to access unchecked addresses.) */static char*validname0(char *aname, int slashok, int dup, ulong pc){ char *p, *ename, *name, *s; uint t; int c, n; Rune r; name = aname; if(isuaddr(name)){ if(!dup) print("warning: validname called from %lux with user pointer", pc); 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"); s = nil; if(dup){ n = ename-name; s = smalloc(n+1); memmove(s, name, n); s[n] = 0; aname = s; name = s; setmalloctag(s, pc); } 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); free(s); error(up->genbuf); } name++; } } return s;}voidvalidname(char *aname, int slashok){ validname0(aname, slashok, 0, getcallerpc(&aname));}char*validnamedup(char *aname, int slashok){ return validname0(aname, slashok, 1, getcallerpc(&aname));}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 + -