#define UNICODE#define Unknown win_Unknown#include <windows.h>#include <winbase.h>#undef Unknown#undef Sleep#include "dat.h"#include "fns.h"#include "error.h"#include <lm.h> /* TODO: try using / in place of \ in path names */ enum{ MAX_SID = sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD), ACL_ROCK = sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID), SD_ROCK = SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK, MAXCOMP = 128,}; typedef struct User User;typedef struct Gmem Gmem;typedef struct Stat Stat;typedef struct Fsinfo Fsinfo;typedef WIN32_FIND_DATA Fsdir; #ifndef INVALID_SET_FILE_POINTER#define INVALID_SET_FILE_POINTER ((DWORD)-1)#endif struct Fsinfo{ int uid; int gid; int mode; int fd; vlong offset; QLock oq; char* spec; Rune* srv; Cname* name; /* Windows' idea of the file name */ ushort usesec; ushort checksec; Fsdir* de; /* non-nil for saved entry from last dirread at offset */};#define FS(c) ((Fsinfo*)(c)->aux) /* * info about a user or group * there are two ways to specify a user: * by sid, a unique identifier * by user and domain names * this structure is used to convert between the two, * as well as figure out which groups a users belongs to. * the user information never gets thrown away, * but the group information gets refreshed with each setid. */struct User{ QLock lk; /* locks the gotgroup and group fields */ SID *sid; Rune *name; Rune *dom; int type; /* the type of sid, ie SidTypeUser, SidTypeAlias, ... */ int gotgroup; /* tried to add group */ Gmem *group; /* global and local groups to which this user or group belongs. */ User *next;}; struct Gmem{ User *user; Gmem *next;}; /* * intermediate stat information */struct Stat{ User *owner; User *group; ulong mode;}; /* * some "well-known" sids */static SID *creatorowner;static SID *creatorgroup;static SID *everyone;static SID *ntignore;static SID *ntroot; /* user who is supposed to run emu as a server */ /* * all users we ever see end up in this table * users are never deleted, but we should update * group information for users sometime */static struct{ QLock lk; User *u;}users; /* * conversion from inferno permission modes to nt access masks * is this good enough? this is what nt sets, except for NOMODE */#define NOMODE (READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES)#define RMODE (READ_CONTROL|SYNCHRONIZE\ |FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)#define XMODE (READ_CONTROL|SYNCHRONIZE\ |FILE_EXECUTE|FILE_READ_ATTRIBUTES)#define WMODE (DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\ |FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\ |FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES) static intmodetomask[] ={ NOMODE, XMODE, WMODE, WMODE|XMODE, RMODE, RMODE|XMODE, RMODE|WMODE, RMODE|WMODE|XMODE,}; extern DWORD PlatformId; char rootdir[MAXROOT] = "\\inferno"; Rune rootname[] = L"inferno-server";static Qid rootqid;static User *fsnone;static User *fsuser;static Rune *ntsrv;static int usesec;static int checksec;static int isserver;static int file_share_delete;static uchar isntfrog[256]; static void fsremove(Chan*); wchar_t *widen(char *s); char *narrowen(wchar_t *ws); int widebytes(wchar_t *ws); static char Etoolong[] = "file name too long"; /* * these lan manager functions are not supplied * on windows95, so we have to load the dll by hand */static struct { NET_API_STATUS (NET_API_FUNCTION *UserGetLocalGroups)( LPWSTR servername, LPWSTR username, DWORD level, DWORD flags, LPBYTE *bufptr, DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries); NET_API_STATUS (NET_API_FUNCTION *UserGetGroups)( LPWSTR servername, LPWSTR username, DWORD level, LPBYTE *bufptr, DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries); NET_API_STATUS (NET_API_FUNCTION *GetAnyDCName)( LPCWSTR ServerName, LPCWSTR DomainName, LPBYTE *Buffer); NET_API_STATUS (NET_API_FUNCTION *ApiBufferFree)(LPVOID Buffer);} net; extern int nth2fd(HANDLE);extern HANDLE ntfd2h(int);static int cnisroot(Cname*);static int fsisroot(Chan*);static int okelem(char*, int);static int fsexist(char*, Qid*);static char* fspath(Cname*, char*, char*, char*);static Cname* fswalkpath(Cname*, char*, int);static char* fslastelem(Cname*);static long fsdirread(Chan*, uchar*, int, vlong);static ulong fsqidpath(char*);static int fsomode(int);static int fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir);static int fsdirsize(WIN32_FIND_DATA*, char*, Chan*);static void fssettime(char*, long, long);static long unixtime(FILETIME);static FILETIME wintime(ulong);static void secinit(void);static int secstat(Dir*, char*, Rune*);static int secsize(char*, Rune*);static void seccheck(char*, ulong, Rune*);static int sechasperm(char*, ulong, Rune*);static SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]);static int secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune*);static int secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune*);static SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int);static SID *dupsid(SID*);static int ismembersid(Rune*, User*, SID*);static int ismember(User*, User*);static User *sidtouser(Rune*, SID*);static User *domnametouser(Rune*, Rune*, Rune*);static User *nametouser(Rune*, Rune*);static User *unametouser(Rune*, char*);static void addgroups(User*, int);static User *mkuser(SID*, int, Rune*, Rune*);static Rune *domsrv(Rune *, Rune[MAX_PATH]);static Rune *filesrv(char*);static int fsacls(char*);static User *secuser(void); int runeslen(Rune*); Rune* runesdup(Rune*); Rune* utftorunes(Rune*, char*, int); char* runestoutf(char*, Rune*, int); int runescmp(Rune*, Rune*); intwinfilematch(char *path, WIN32_FIND_DATA *data){ char *p; wchar_t *wpath; int r; p = path+strlen(path); while(p > path && p[-1] != '\\') --p; wpath = widen(p); r = (data->cFileName[0] == '.' && runeslen(data->cFileName) == 1) || runescmp(data->cFileName, wpath) == 0; free(wpath); return r;} intwinfileclash(char *path){ HANDLE h; WIN32_FIND_DATA data; wchar_t *wpath; wpath = widen(path); h = FindFirstFile(wpath, &data); free(wpath); if (h != INVALID_HANDLE_VALUE) { FindClose(h); return !winfilematch(path, &data); } return 0;} /* * this gets called to set up the environment when we switch users */voidsetid(char *name, int owner){ User *u; if(owner && !iseve()) return; kstrdup(&up->env->user, name); if(!usesec) return; u = unametouser(ntsrv, up->env->user); if(u == nil) u = fsnone; else { qlock(&u->lk); addgroups(u, 1); qunlock(&u->lk); } if(u == nil) panic("setid: user nil\n"); up->env->ui = u;} static voidfsfree(Chan *c){ cnameclose(FS(c)->name); if(FS(c)->de != nil) free(FS(c)->de); free(FS(c));} voidfsinit(void){ int n, isvol; ulong attr; char *p, tmp[MAXROOT]; wchar_t *wp, *wpath, *last; wchar_t wrootdir[MAXROOT]; isntfrog['/'] = 1; isntfrog['\\'] = 1; isntfrog[':'] = 1; isntfrog['*'] = 1; isntfrog['?'] = 1; isntfrog['"'] = 1; isntfrog['<'] = 1; isntfrog['>'] = 1; /* * vet the root */ strcpy(tmp, rootdir); for(p = tmp; *p; p++) if(*p == '/') *p = '\\'; if(tmp[0] != 0 && tmp[1] == ':') { if(tmp[2] == 0) { tmp[2] = '\\'; tmp[3] = 0; } else if(tmp[2] != '\\') { /* don't allow c:foo - only c:\foo */ panic("illegal root pathX"); } } wrootdir[0] = '\0'; wpath = widen(tmp); for(wp = wpath; *wp; wp++) { if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':')) panic("illegal root path"); } n = GetFullPathName(wpath, MAXROOT, wrootdir, &last); free(wpath); runestoutf(rootdir, wrootdir, MAXROOT); if(n >= MAXROOT || n == 0) panic("illegal root path"); /* get rid of trailing \ */ while(rootdir[n-1] == '\\') { if(n <= 2) { panic("illegal root path"); } rootdir[--n] = '\0'; } isvol = 0; if(rootdir[1] == ':' && rootdir[2] == '\0') isvol = 1; else if(rootdir[0] == '\\' && rootdir[1] == '\\') { p = strchr(&rootdir[2], '\\'); if(p == nil) panic("inferno root can't be a server"); isvol = strchr(p+1, '\\') == nil; } if(strchr(rootdir, '\\') == nil) strcat(rootdir, "\\."); attr = GetFileAttributes(wrootdir); if(attr == 0xFFFFFFFF) panic("root path '%s' does not exist", narrowen(wrootdir)); rootqid.path = fsqidpath(rootdir); if(attr & FILE_ATTRIBUTE_DIRECTORY) rootqid.type |= QTDIR; rootdir[n] = '\0'; rootqid.vers = time(0); /* * set up for nt file security checking */ ntsrv = filesrv(rootdir); usesec = PlatformId == VER_PLATFORM_WIN32_NT; /* true for NT and 2000 */ if(usesec){ file_share_delete = FILE_SHARE_DELETE; /* sensible handling of shared files by delete and rename */ secinit(); if(!fsacls(rootdir)) usesec = 0; } checksec = usesec && isserver;} Chan*fsattach(char *spec){ Chan *c; static int devno; static Lock l; char *drive = (char *)spec; if (!emptystr(drive) && (drive[1] != ':' || drive[2] != '\0')) error(Ebadspec); c = devattach('U', spec); lock(&l); c->dev = devno++; unlock(&l); c->qid = rootqid; c->aux = smalloc(sizeof(Fsinfo)); FS(c)->srv = ntsrv; if(!emptystr(spec)) { char *s = smalloc(strlen(spec)+1); strcpy(s, spec); FS(c)->spec = s; FS(c)->srv = filesrv(spec); FS(c)->usesec = fsacls(spec); FS(c)->checksec = FS(c)->usesec && isserver; c->qid.path = fsqidpath(spec); c->qid.type = QTDIR; c->qid.vers = 0; }else{ FS(c)->usesec = usesec; FS(c)->checksec = checksec; } FS(c)->name = newcname("/"); return c;} Walkqid*fswalk(Chan *c, Chan *nc, char **name, int nname){ int j, alloc; Walkqid *wq; char path[MAX_PATH], *p; Cname *ph; Cname *current, *next; if(nname > 0) isdir(c); alloc = 0; current = nil; wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); if(waserror()){ if(alloc && wq->clone != nil) cclose(wq->clone); cnameclose(current); free(wq); return nil; } if(nc == nil){ nc = devclone(c); nc->type = 0; alloc = 1; } wq->clone = nc; current = FS(c)->name; if(current != nil) incref(¤t->r); for(j = 0; j < nname; j++){ if(!(nc->qid.type&QTDIR)){ if(j==0) error(Enotdir); break; } if(!okelem(name[j], 0)){ if(j == 0) error(Efilename); break; } p = fspath(current, name[j], path, FS(c)->spec); if(FS(c)->checksec) { *p = '\0'; if(!sechasperm(path, XMODE, FS(c)->srv)){ if(j == 0) error(Eperm); break; } *p = '\\'; } if(strcmp(name[j], "..") == 0) { if(fsisroot(c)) nc->qid = rootqid; else{ ph = fswalkpath(current, "..", 1); if(cnisroot(ph)){ nc->qid = rootqid; current = ph; if(current != nil) incref(¤t->r); } else { fspath(ph, 0, path, FS(c)->spec); if(!fsexist(path, &nc->qid)){ cnameclose(ph); if(j == 0) error(Enonexist);Lines 1–500 of 2509. Load more
#define UNICODE #define Unknown win_Unknown #include <windows.h> #include <winbase.h> #undef Unknown #undef Sleep #include "dat.h" #include "fns.h" #include "error.h" #include <lm.h> /* TODO: try using / in place of \ in path names */ enum { MAX_SID = sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD), ACL_ROCK = sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID), SD_ROCK = SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK, MAXCOMP = 128, }; typedef struct User User; typedef struct Gmem Gmem; typedef struct Stat Stat; typedef struct Fsinfo Fsinfo; typedef WIN32_FIND_DATA Fsdir; #ifndef INVALID_SET_FILE_POINTER #define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif struct Fsinfo { int uid; int gid; int mode; int fd; vlong offset; QLock oq; char* spec; Rune* srv; Cname* name; /* Windows' idea of the file name */ ushort usesec; ushort checksec; Fsdir* de; /* non-nil for saved entry from last dirread at offset */ }; #define FS(c) ((Fsinfo*)(c)->aux) /* * info about a user or group * there are two ways to specify a user: * by sid, a unique identifier * by user and domain names * this structure is used to convert between the two, * as well as figure out which groups a users belongs to. * the user information never gets thrown away, * but the group information gets refreshed with each setid. */ struct User { QLock lk; /* locks the gotgroup and group fields */ SID *sid; Rune *name; Rune *dom; int type; /* the type of sid, ie SidTypeUser, SidTypeAlias, ... */ int gotgroup; /* tried to add group */ Gmem *group; /* global and local groups to which this user or group belongs. */ User *next; }; struct Gmem { User *user; Gmem *next; }; /* * intermediate stat information */ struct Stat { User *owner; User *group; ulong mode; }; /* * some "well-known" sids */ static SID *creatorowner; static SID *creatorgroup; static SID *everyone; static SID *ntignore; static SID *ntroot; /* user who is supposed to run emu as a server */ /* * all users we ever see end up in this table * users are never deleted, but we should update * group information for users sometime */ static struct { QLock lk; User *u; }users; /* * conversion from inferno permission modes to nt access masks * is this good enough? this is what nt sets, except for NOMODE */ #define NOMODE (READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES) #define RMODE (READ_CONTROL|SYNCHRONIZE\ |FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES) #define XMODE (READ_CONTROL|SYNCHRONIZE\ |FILE_EXECUTE|FILE_READ_ATTRIBUTES) #define WMODE (DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\ |FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\ |FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES) static int modetomask[] = { NOMODE, XMODE, WMODE, WMODE|XMODE, RMODE, RMODE|XMODE, RMODE|WMODE, RMODE|WMODE|XMODE, }; extern DWORD PlatformId; char rootdir[MAXROOT] = "\\inferno"; Rune rootname[] = L"inferno-server"; static Qid rootqid; static User *fsnone; static User *fsuser; static Rune *ntsrv; static int usesec; static int checksec; static int isserver; static int file_share_delete; static uchar isntfrog[256]; static void fsremove(Chan*); wchar_t *widen(char *s); char *narrowen(wchar_t *ws); int widebytes(wchar_t *ws); static char Etoolong[] = "file name too long"; /* * these lan manager functions are not supplied * on windows95, so we have to load the dll by hand */ static struct { NET_API_STATUS (NET_API_FUNCTION *UserGetLocalGroups)( LPWSTR servername, LPWSTR username, DWORD level, DWORD flags, LPBYTE *bufptr, DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries); NET_API_STATUS (NET_API_FUNCTION *UserGetGroups)( LPWSTR servername, LPWSTR username, DWORD level, LPBYTE *bufptr, DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries); NET_API_STATUS (NET_API_FUNCTION *GetAnyDCName)( LPCWSTR ServerName, LPCWSTR DomainName, LPBYTE *Buffer); NET_API_STATUS (NET_API_FUNCTION *ApiBufferFree)(LPVOID Buffer); } net; extern int nth2fd(HANDLE); extern HANDLE ntfd2h(int); static int cnisroot(Cname*); static int fsisroot(Chan*); static int okelem(char*, int); static int fsexist(char*, Qid*); static char* fspath(Cname*, char*, char*, char*); static Cname* fswalkpath(Cname*, char*, int); static char* fslastelem(Cname*); static long fsdirread(Chan*, uchar*, int, vlong); static ulong fsqidpath(char*); static int fsomode(int); static int fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir); static int fsdirsize(WIN32_FIND_DATA*, char*, Chan*); static void fssettime(char*, long, long); static long unixtime(FILETIME); static FILETIME wintime(ulong); static void secinit(void); static int secstat(Dir*, char*, Rune*); static int secsize(char*, Rune*); static void seccheck(char*, ulong, Rune*); static int sechasperm(char*, ulong, Rune*); static SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]); static int secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune*); static int secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune*); static SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int); static SID *dupsid(SID*); static int ismembersid(Rune*, User*, SID*); static int ismember(User*, User*); static User *sidtouser(Rune*, SID*); static User *domnametouser(Rune*, Rune*, Rune*); static User *nametouser(Rune*, Rune*); static User *unametouser(Rune*, char*); static void addgroups(User*, int); static User *mkuser(SID*, int, Rune*, Rune*); static Rune *domsrv(Rune *, Rune[MAX_PATH]); static Rune *filesrv(char*); static int fsacls(char*); static User *secuser(void); int runeslen(Rune*); Rune* runesdup(Rune*); Rune* utftorunes(Rune*, char*, int); char* runestoutf(char*, Rune*, int); int runescmp(Rune*, Rune*); int winfilematch(char *path, WIN32_FIND_DATA *data) { char *p; wchar_t *wpath; int r; p = path+strlen(path); while(p > path && p[-1] != '\\') --p; wpath = widen(p); r = (data->cFileName[0] == '.' && runeslen(data->cFileName) == 1) || runescmp(data->cFileName, wpath) == 0; free(wpath); return r; } int winfileclash(char *path) { HANDLE h; WIN32_FIND_DATA data; wchar_t *wpath; wpath = widen(path); h = FindFirstFile(wpath, &data); free(wpath); if (h != INVALID_HANDLE_VALUE) { FindClose(h); return !winfilematch(path, &data); } return 0; } /* * this gets called to set up the environment when we switch users */ void setid(char *name, int owner) { User *u; if(owner && !iseve()) return; kstrdup(&up->env->user, name); if(!usesec) return; u = unametouser(ntsrv, up->env->user); if(u == nil) u = fsnone; else { qlock(&u->lk); addgroups(u, 1); qunlock(&u->lk); } if(u == nil) panic("setid: user nil\n"); up->env->ui = u; } static void fsfree(Chan *c) { cnameclose(FS(c)->name); if(FS(c)->de != nil) free(FS(c)->de); free(FS(c)); } void fsinit(void) { int n, isvol; ulong attr; char *p, tmp[MAXROOT]; wchar_t *wp, *wpath, *last; wchar_t wrootdir[MAXROOT]; isntfrog['/'] = 1; isntfrog['\\'] = 1; isntfrog[':'] = 1; isntfrog['*'] = 1; isntfrog['?'] = 1; isntfrog['"'] = 1; isntfrog['<'] = 1; isntfrog['>'] = 1; /* * vet the root */ strcpy(tmp, rootdir); for(p = tmp; *p; p++) if(*p == '/') *p = '\\'; if(tmp[0] != 0 && tmp[1] == ':') { if(tmp[2] == 0) { tmp[2] = '\\'; tmp[3] = 0; } else if(tmp[2] != '\\') { /* don't allow c:foo - only c:\foo */ panic("illegal root pathX"); } } wrootdir[0] = '\0'; wpath = widen(tmp); for(wp = wpath; *wp; wp++) { if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':')) panic("illegal root path"); } n = GetFullPathName(wpath, MAXROOT, wrootdir, &last); free(wpath); runestoutf(rootdir, wrootdir, MAXROOT); if(n >= MAXROOT || n == 0) panic("illegal root path"); /* get rid of trailing \ */ while(rootdir[n-1] == '\\') { if(n <= 2) { panic("illegal root path"); } rootdir[--n] = '\0'; } isvol = 0; if(rootdir[1] == ':' && rootdir[2] == '\0') isvol = 1; else if(rootdir[0] == '\\' && rootdir[1] == '\\') { p = strchr(&rootdir[2], '\\'); if(p == nil) panic("inferno root can't be a server"); isvol = strchr(p+1, '\\') == nil; } if(strchr(rootdir, '\\') == nil) strcat(rootdir, "\\."); attr = GetFileAttributes(wrootdir); if(attr == 0xFFFFFFFF) panic("root path '%s' does not exist", narrowen(wrootdir)); rootqid.path = fsqidpath(rootdir); if(attr & FILE_ATTRIBUTE_DIRECTORY) rootqid.type |= QTDIR; rootdir[n] = '\0'; rootqid.vers = time(0); /* * set up for nt file security checking */ ntsrv = filesrv(rootdir); usesec = PlatformId == VER_PLATFORM_WIN32_NT; /* true for NT and 2000 */ if(usesec){ file_share_delete = FILE_SHARE_DELETE; /* sensible handling of shared files by delete and rename */ secinit(); if(!fsacls(rootdir)) usesec = 0; } checksec = usesec && isserver; } Chan* fsattach(char *spec) { Chan *c; static int devno; static Lock l; char *drive = (char *)spec; if (!emptystr(drive) && (drive[1] != ':' || drive[2] != '\0')) error(Ebadspec); c = devattach('U', spec); lock(&l); c->dev = devno++; unlock(&l); c->qid = rootqid; c->aux = smalloc(sizeof(Fsinfo)); FS(c)->srv = ntsrv; if(!emptystr(spec)) { char *s = smalloc(strlen(spec)+1); strcpy(s, spec); FS(c)->spec = s; FS(c)->srv = filesrv(spec); FS(c)->usesec = fsacls(spec); FS(c)->checksec = FS(c)->usesec && isserver; c->qid.path = fsqidpath(spec); c->qid.type = QTDIR; c->qid.vers = 0; }else{ FS(c)->usesec = usesec; FS(c)->checksec = checksec; } FS(c)->name = newcname("/"); return c; } Walkqid* fswalk(Chan *c, Chan *nc, char **name, int nname) { int j, alloc; Walkqid *wq; char path[MAX_PATH], *p; Cname *ph; Cname *current, *next; if(nname > 0) isdir(c); alloc = 0; current = nil; wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); if(waserror()){ if(alloc && wq->clone != nil) cclose(wq->clone); cnameclose(current); free(wq); return nil; } if(nc == nil){ nc = devclone(c); nc->type = 0; alloc = 1; } wq->clone = nc; current = FS(c)->name; if(current != nil) incref(¤t->r); for(j = 0; j < nname; j++){ if(!(nc->qid.type&QTDIR)){ if(j==0) error(Enotdir); break; } if(!okelem(name[j], 0)){ if(j == 0) error(Efilename); break; } p = fspath(current, name[j], path, FS(c)->spec); if(FS(c)->checksec) { *p = '\0'; if(!sechasperm(path, XMODE, FS(c)->srv)){ if(j == 0) error(Eperm); break; } *p = '\\'; } if(strcmp(name[j], "..") == 0) { if(fsisroot(c)) nc->qid = rootqid; else{ ph = fswalkpath(current, "..", 1); if(cnisroot(ph)){ nc->qid = rootqid; current = ph; if(current != nil) incref(¤t->r); } else { fspath(ph, 0, path, FS(c)->spec); if(!fsexist(path, &nc->qid)){ cnameclose(ph); if(j == 0) error(Enonexist);