1 #define UNICODE 2 #define Unknown win_Unknown 3 #include <windows.h> 4 #include <winbase.h> 5 #undef Unknown 6 #undef Sleep 7 #include "dat.h" 8 #include "fns.h" 9 #include "error.h" 10 #include <lm.h> 11 12 /* TODO: try using / in place of \ in path names */ 13 14 #ifndef SID_MAX_SUB_AUTHORITIES 15 #define SID_MAX_SUB_AUTHORITIES 15 16 #endif 17 18 enum 19 { 20 MAX_SID = sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD), 21 ACL_ROCK = sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID), 22 SD_ROCK = SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK, 23 MAXCOMP = 128, 24 }; 25 26 typedef struct User User; 27 typedef struct Gmem Gmem; 28 typedef struct Stat Stat; 29 typedef struct Fsinfo Fsinfo; 30 typedef WIN32_FIND_DATA Fsdir; 31 32 #ifndef INVALID_SET_FILE_POINTER 33 #define INVALID_SET_FILE_POINTER ((DWORD)-1) 34 #endif 35 36 struct Fsinfo 37 { 38 int uid; 39 int gid; 40 int mode; 41 int fd; 42 vlong offset; 43 QLock oq; 44 char* spec; 45 Rune* srv; 46 Cname* name; /* Windows' idea of the file name */ 47 ushort usesec; 48 ushort checksec; 49 Fsdir* de; /* non-nil for saved entry from last dirread at offset */ 50 }; 51 #define FS(c) ((Fsinfo*)(c)->aux) 52 53 /* 54 * info about a user or group 55 * there are two ways to specify a user: 56 * by sid, a unique identifier 57 * by user and domain names 58 * this structure is used to convert between the two, 59 * as well as figure out which groups a users belongs to. 60 * the user information never gets thrown away, 61 * but the group information gets refreshed with each setid. 62 */ 63 struct User 64 { 65 QLock lk; /* locks the gotgroup and group fields */ 66 SID *sid; 67 Rune *name; 68 Rune *dom; 69 int type; /* the type of sid, ie SidTypeUser, SidTypeAlias, ... */ 70 int gotgroup; /* tried to add group */ 71 Gmem *group; /* global and local groups to which this user or group belongs. */ 72 User *next; 73 }; 74 75 struct Gmem 76 { 77 User *user; 78 Gmem *next; 79 }; 80 81 /* 82 * intermediate stat information 83 */ 84 struct Stat 85 { 86 User *owner; 87 User *group; 88 ulong mode; 89 }; 90 91 /* 92 * some "well-known" sids 93 */ 94 static SID *creatorowner; 95 static SID *creatorgroup; 96 static SID *everyone; 97 static SID *ntignore; 98 static SID *ntroot; /* user who is supposed to run emu as a server */ 99 100 /* 101 * all users we ever see end up in this table 102 * users are never deleted, but we should update 103 * group information for users sometime 104 */ 105 static struct 106 { 107 QLock lk; 108 User *u; 109 }users; 110 111 /* 112 * conversion from inferno permission modes to nt access masks 113 * is this good enough? this is what nt sets, except for NOMODE 114 */ 115 #define NOMODE (READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES) 116 #define RMODE (READ_CONTROL|SYNCHRONIZE\ 117 |FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES) 118 #define XMODE (READ_CONTROL|SYNCHRONIZE\ 119 |FILE_EXECUTE|FILE_READ_ATTRIBUTES) 120 #define WMODE (DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\ 121 |FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\ 122 |FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES) 123 124 static int 125 modetomask[] = 126 { 127 NOMODE, 128 XMODE, 129 WMODE, 130 WMODE|XMODE, 131 RMODE, 132 RMODE|XMODE, 133 RMODE|WMODE, 134 RMODE|WMODE|XMODE, 135 }; 136 137 extern DWORD PlatformId; 138 char rootdir[MAXROOT] = "\\inferno"; 139 Rune rootname[] = L"inferno-server"; 140 static Qid rootqid; 141 static User *fsnone; 142 static User *fsuser; 143 static Rune *ntsrv; 144 static int usesec; 145 static int checksec; 146 static int isserver; 147 static int file_share_delete; 148 static uchar isntfrog[256]; 149 150 static void fsremove(Chan*); 151 152 wchar_t *widen(char *s); 153 char *narrowen(wchar_t *ws); 154 int widebytes(wchar_t *ws); 155 156 static char Etoolong[] = "file name too long"; 157 158 /* 159 * these lan manager functions are not supplied 160 * on windows95, so we have to load the dll by hand 161 */ 162 static struct { 163 NET_API_STATUS (NET_API_FUNCTION *UserGetLocalGroups)( 164 LPWSTR servername, 165 LPWSTR username, 166 DWORD level, 167 DWORD flags, 168 LPBYTE *bufptr, 169 DWORD prefmaxlen, 170 LPDWORD entriesread, 171 LPDWORD totalentries); 172 NET_API_STATUS (NET_API_FUNCTION *UserGetGroups)( 173 LPWSTR servername, 174 LPWSTR username, 175 DWORD level, 176 LPBYTE *bufptr, 177 DWORD prefmaxlen, 178 LPDWORD entriesread, 179 LPDWORD totalentries); 180 NET_API_STATUS (NET_API_FUNCTION *GetAnyDCName)( 181 LPCWSTR ServerName, 182 LPCWSTR DomainName, 183 LPBYTE *Buffer); 184 NET_API_STATUS (NET_API_FUNCTION *ApiBufferFree)(LPVOID Buffer); 185 } net; 186 187 extern int nth2fd(HANDLE); 188 extern HANDLE ntfd2h(int); 189 static int cnisroot(Cname*); 190 static int fsisroot(Chan*); 191 static int okelem(char*, int); 192 static int fsexist(char*, Qid*); 193 static char* fspath(Cname*, char*, char*, char*); 194 static Cname* fswalkpath(Cname*, char*, int); 195 static char* fslastelem(Cname*); 196 static long fsdirread(Chan*, uchar*, int, vlong); 197 static ulong fsqidpath(char*); 198 static int fsomode(int); 199 static int fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir); 200 static int fsdirsize(WIN32_FIND_DATA*, char*, Chan*); 201 static void fssettime(char*, long, long); 202 static long unixtime(FILETIME); 203 static FILETIME wintime(ulong); 204 static void secinit(void); 205 static int secstat(Dir*, char*, Rune*); 206 static int secsize(char*, Rune*); 207 static void seccheck(char*, ulong, Rune*); 208 static int sechasperm(char*, ulong, Rune*); 209 static SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]); 210 static int secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune*); 211 static int secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune*); 212 static SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int); 213 static SID *dupsid(SID*); 214 static int ismembersid(Rune*, User*, SID*); 215 static int ismember(User*, User*); 216 static User *sidtouser(Rune*, SID*); 217 static User *domnametouser(Rune*, Rune*, Rune*); 218 static User *nametouser(Rune*, Rune*); 219 static User *unametouser(Rune*, char*); 220 static void addgroups(User*, int); 221 static User *mkuser(SID*, int, Rune*, Rune*); 222 static Rune *domsrv(Rune *, Rune[MAX_PATH]); 223 static Rune *filesrv(char*); 224 static int fsacls(char*); 225 static User *secuser(void); 226 227 int runeslen(Rune*); 228 Rune* runesdup(Rune*); 229 Rune* utftorunes(Rune*, char*, int); 230 char* runestoutf(char*, Rune*, int); 231 int runescmp(Rune*, Rune*); 232 233 234 int 235 winfilematch(char *path, WIN32_FIND_DATA *data) 236 { 237 char *p; 238 wchar_t *wpath; 239 int r; 240 241 p = path+strlen(path); 242 while(p > path && p[-1] != '\\') 243 --p; 244 wpath = widen(p); 245 r = (data->cFileName[0] == '.' && runeslen(data->cFileName) == 1) 246 || runescmp(data->cFileName, wpath) == 0; 247 free(wpath); 248 return r; 249 } 250 251 int 252 winfileclash(char *path) 253 { 254 HANDLE h; 255 WIN32_FIND_DATA data; 256 wchar_t *wpath; 257 258 wpath = widen(path); 259 h = FindFirstFile(wpath, &data); 260 free(wpath); 261 if (h != INVALID_HANDLE_VALUE) { 262 FindClose(h); 263 return !winfilematch(path, &data); 264 } 265 return 0; 266 } 267 268 269 /* 270 * this gets called to set up the environment when we switch users 271 */ 272 void 273 setid(char *name, int owner) 274 { 275 User *u; 276 277 if(owner && !iseve()) 278 return; 279 280 kstrdup(&up->env->user, name); 281 282 if(!usesec) 283 return; 284 285 u = unametouser(ntsrv, up->env->user); 286 if(u == nil) 287 u = fsnone; 288 else { 289 qlock(&u->lk); 290 addgroups(u, 1); 291 qunlock(&u->lk); 292 } 293 if(u == nil) 294 panic("setid: user nil\n"); 295 296 up->env->ui = u; 297 } 298 299 static void 300 fsfree(Chan *c) 301 { 302 cnameclose(FS(c)->name); 303 if(FS(c)->de != nil) 304 free(FS(c)->de); 305 free(FS(c)); 306 } 307 308 void 309 fsinit(void) 310 { 311 int n, isvol; 312 ulong attr; 313 char *p, tmp[MAXROOT]; 314 wchar_t *wp, *wpath, *last; 315 wchar_t wrootdir[MAXROOT]; 316 317 isntfrog['/'] = 1; 318 isntfrog['\\'] = 1; 319 isntfrog[':'] = 1; 320 isntfrog['*'] = 1; 321 isntfrog['?'] = 1; 322 isntfrog['"'] = 1; 323 isntfrog['<'] = 1; 324 isntfrog['>'] = 1; 325 326 /* 327 * vet the root 328 */ 329 strcpy(tmp, rootdir); 330 for(p = tmp; *p; p++) 331 if(*p == '/') 332 *p = '\\'; 333 if(tmp[0] != 0 && tmp[1] == ':') { 334 if(tmp[2] == 0) { 335 tmp[2] = '\\'; 336 tmp[3] = 0; 337 } 338 else if(tmp[2] != '\\') { 339 /* don't allow c:foo - only c:\foo */ 340 panic("illegal root pathX"); 341 } 342 } 343 wrootdir[0] = '\0'; 344 wpath = widen(tmp); 345 for(wp = wpath; *wp; wp++) { 346 if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':')) 347 panic("illegal root path"); 348 } 349 n = GetFullPathName(wpath, MAXROOT, wrootdir, &last); 350 free(wpath); 351 runestoutf(rootdir, wrootdir, MAXROOT); 352 if(n >= MAXROOT || n == 0) 353 panic("illegal root path"); 354 355 /* get rid of trailing \ */ 356 while(rootdir[n-1] == '\\') { 357 if(n <= 2) { 358 panic("illegal root path"); 359 } 360 rootdir[--n] = '\0'; 361 } 362 363 isvol = 0; 364 if(rootdir[1] == ':' && rootdir[2] == '\0') 365 isvol = 1; 366 else if(rootdir[0] == '\\' && rootdir[1] == '\\') { 367 p = strchr(&rootdir[2], '\\'); 368 if(p == nil) 369 panic("inferno root can't be a server"); 370 isvol = strchr(p+1, '\\') == nil; 371 } 372 373 if(strchr(rootdir, '\\') == nil) 374 strcat(rootdir, "\\."); 375 attr = GetFileAttributes(wrootdir); 376 if(attr == 0xFFFFFFFF) 377 panic("root path '%s' does not exist", narrowen(wrootdir)); 378 rootqid.path = fsqidpath(rootdir); 379 if(attr & FILE_ATTRIBUTE_DIRECTORY) 380 rootqid.type |= QTDIR; 381 rootdir[n] = '\0'; 382 383 rootqid.vers = time(0); 384 385 /* 386 * set up for nt file security checking 387 */ 388 ntsrv = filesrv(rootdir); 389 usesec = PlatformId == VER_PLATFORM_WIN32_NT; /* true for NT and 2000 */ 390 if(usesec){ 391 file_share_delete = FILE_SHARE_DELETE; /* sensible handling of shared files by delete and rename */ 392 secinit(); 393 if(!fsacls(rootdir)) 394 usesec = 0; 395 } 396 checksec = usesec && isserver; 397 } 398 399 Chan* 400 fsattach(char *spec) 401 { 402 Chan *c; 403 static int devno; 404 static Lock l; 405 char *drive = (char *)spec; 406 407 if (!emptystr(drive) && (drive[1] != ':' || drive[2] != '\0')) 408 error(Ebadspec); 409 410 c = devattach('U', spec); 411 lock(&l); 412 c->dev = devno++; 413 unlock(&l); 414 c->qid = rootqid; 415 c->aux = smalloc(sizeof(Fsinfo)); 416 FS(c)->srv = ntsrv; 417 if(!emptystr(spec)) { 418 char *s = smalloc(strlen(spec)+1); 419 strcpy(s, spec); 420 FS(c)->spec = s; 421 FS(c)->srv = filesrv(spec); 422 if(usesec) 423 FS(c)->usesec = fsacls(spec); 424 FS(c)->checksec = FS(c)->usesec && isserver; 425 c->qid.path = fsqidpath(spec); 426 c->qid.type = QTDIR; 427 c->qid.vers = 0; 428 }else{ 429 FS(c)->usesec = usesec; 430 FS(c)->checksec = checksec; 431 } 432 FS(c)->name = newcname("/"); 433 return c; 434 } 435 436 Walkqid* 437 fswalk(Chan *c, Chan *nc, char **name, int nname) 438 { 439 int j, alloc; 440 Walkqid *wq; 441 char path[MAX_PATH], *p; 442 Cname *ph; 443 Cname *current, *next; 444 445 if(nname > 0) 446 isdir(c); 447 448 alloc = 0; 449 current = nil; 450 wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); 451 if(waserror()){ 452 if(alloc && wq->clone != nil) 453 cclose(wq->clone); 454 cnameclose(current); 455 free(wq); 456 return nil; 457 } 458 if(nc == nil){ 459 nc = devclone(c); 460 nc->type = 0; 461 alloc = 1; 462 } 463 wq->clone = nc; 464 current = FS(c)->name; 465 if(current != nil) 466 incref(¤t->r); 467 for(j = 0; j < nname; j++){ 468 if(!(nc->qid.type&QTDIR)){ 469 if(j==0) 470 error(Enotdir); 471 break; 472 } 473 if(!okelem(name[j], 0)){ 474 if(j == 0) 475 error(Efilename); 476 break; 477 } 478 p = fspath(current, name[j], path, FS(c)->spec); 479 if(FS(c)->checksec) { 480 *p = '\0'; 481 if(!sechasperm(path, XMODE, FS(c)->srv)){ 482 if(j == 0) 483 error(Eperm); 484 break; 485 } 486 *p = '\\'; 487 } 488 489 if(strcmp(name[j], "..") == 0) { 490 if(fsisroot(c)) 491 nc->qid = rootqid; 492 else{ 493 ph = fswalkpath(current, "..", 1); 494 if(cnisroot(ph)){ 495 nc->qid = rootqid; 496 current = ph; 497 if(current != nil) 498 incref(¤t->r); 499 } 500 else { 501 fspath(ph, 0, path, FS(c)->spec); 502 if(!fsexist(path, &nc->qid)){ 503 cnameclose(ph); 504 if(j == 0) 505 error(Enonexist); 506 break; 507 } 508 } 509 next = fswalkpath(current, name[j], 1); 510 cnameclose(current); 511 current = next; 512 cnameclose(ph); 513 } 514 } 515 else{ 516 if(!fsexist(path, &nc->qid)){ 517 if(j == 0) 518 error(Enonexist); 519 break; 520 } 521 next = fswalkpath(current, name[j], 1); 522 cnameclose(current); 523 current = next; 524 } 525 wq->qid[wq->nqid++] = nc->qid; 526 } 527 poperror(); 528 if(wq->nqid < nname){ 529 cnameclose(current); 530 if(alloc) 531 cclose(wq->clone); 532 wq->clone = nil; 533 }else if(wq->clone){ 534 nc->aux = smalloc(sizeof(Fsinfo)); 535 nc->type = c->type; 536 FS(nc)->spec = FS(c)->spec; 537 FS(nc)->srv = FS(c)->srv; 538 FS(nc)->name = current; 539 FS(nc)->usesec = FS(c)->usesec; 540 FS(nc)->checksec = FS(c)->checksec; 541 } 542 return wq; 543 } 544 545 Chan* 546 fsopen(Chan *c, int mode) 547 { 548 HANDLE h; 549 int m, isdir, aflag, cflag; 550 char path[MAX_PATH]; 551 wchar_t *wpath; 552 553 isdir = c->qid.type & QTDIR; 554 if(isdir && mode != OREAD) 555 error(Eperm); 556 fspath(FS(c)->name, 0, path, FS(c)->spec); 557 558 if(FS(c)->checksec) { 559 switch(mode & (OTRUNC|3)) { 560 case OREAD: 561 seccheck(path, RMODE, FS(c)->srv); 562 break; 563 case OWRITE: 564 case OWRITE|OTRUNC: 565 seccheck(path, WMODE, FS(c)->srv); 566 break; 567 case ORDWR: 568 case ORDWR|OTRUNC: 569 case OREAD|OTRUNC: 570 seccheck(path, RMODE|WMODE, FS(c)->srv); 571 break; 572 case OEXEC: 573 seccheck(path, XMODE, FS(c)->srv); 574 break; 575 default: 576 error(Ebadarg); 577 } 578 } 579 580 c->mode = openmode(mode); 581 if(isdir) 582 FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); 583 else { 584 m = fsomode(mode & 3); 585 cflag = OPEN_EXISTING; 586 if(mode & OTRUNC) 587 cflag = TRUNCATE_EXISTING; 588 aflag = FILE_FLAG_RANDOM_ACCESS; 589 if(mode & ORCLOSE) 590 aflag |= FILE_FLAG_DELETE_ON_CLOSE; 591 if (winfileclash(path)) 592 error(Eexist); 593 wpath = widen(path); 594 h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, 0, cflag, aflag, 0); 595 free(wpath); 596 if(h == INVALID_HANDLE_VALUE) 597 oserror(); 598 FS(c)->fd = nth2fd(h); 599 } 600 601 c->offset = 0; 602 FS(c)->offset = 0; 603 c->flag |= COPEN; 604 return c; 605 } 606 607 void 608 fscreate(Chan *c, char *name, int mode, ulong perm) 609 { 610 Stat st; 611 HANDLE h; 612 int m, aflag; 613 SECURITY_ATTRIBUTES sa; 614 SECURITY_DESCRIPTOR *sd; 615 BY_HANDLE_FILE_INFORMATION hi; 616 char *p, path[MAX_PATH], sdrock[SD_ROCK]; 617 wchar_t *wpath; 618 ACL *acl; 619 620 if(!okelem(name, 1)) 621 error(Efilename); 622 623 m = fsomode(mode & 3); 624 p = fspath(FS(c)->name, name, path, FS(c)->spec); 625 acl = (ACL*)smalloc(ACL_ROCK); 626 sd = nil; 627 if(FS(c)->usesec) { 628 *p = '\0'; 629 sd = secsd(path, sdrock); 630 *p = '\\'; 631 if(sd == nil){ 632 free(acl); 633 oserror(); 634 } 635 if(FS(c)->checksec && !secsdhasperm(sd, WMODE, FS(c)->srv) 636 || !secsdstat(sd, &st, FS(c)->srv)){ 637 if(sd != (void*)sdrock) 638 free(sd); 639 free(acl); 640 error(Eperm); 641 } 642 if(sd != (void*)sdrock) 643 free(sd); 644 if(perm & DMDIR) 645 st.mode = (perm & ~0777) | (st.mode & perm & 0777); 646 else 647 st.mode = (perm & ~0666) | (st.mode & perm & 0666); 648 st.owner = up->env->ui; 649 if(!isserver) 650 st.owner = fsuser; 651 sd = secmksd(sdrock, &st, acl, perm & DMDIR); 652 if(sd == nil){ 653 free(acl); 654 oserror(); 655 } 656 } 657 sa.nLength = sizeof(sa); 658 sa.lpSecurityDescriptor = sd; 659 sa.bInheritHandle = 0; 660 661 if(perm & DMDIR) { 662 if(mode != OREAD) { 663 free(acl); 664 error(Eisdir); 665 } 666 wpath = widen(path); 667 if(!CreateDirectory(wpath, &sa) || !fsexist(path, &c->qid)) { 668 free(wpath); 669 free(acl); 670 oserror(); 671 } 672 free(wpath); 673 FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); 674 } 675 else { 676 aflag = 0; 677 if(mode & ORCLOSE) 678 aflag = FILE_FLAG_DELETE_ON_CLOSE; 679 if (winfileclash(path)) 680 error(Eexist); 681 wpath = widen(path); 682 h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, CREATE_ALWAYS, aflag, 0); 683 free(wpath); 684 if(h == INVALID_HANDLE_VALUE) { 685 free(acl); 686 oserror(); 687 } 688 FS(c)->fd = nth2fd(h); 689 c->qid.path = fsqidpath(path); 690 c->qid.type = 0; 691 c->qid.vers = 0; 692 if(GetFileInformationByHandle(h, &hi)) 693 c->qid.vers = unixtime(hi.ftLastWriteTime); 694 } 695 696 c->mode = openmode(mode); 697 c->offset = 0; 698 FS(c)->offset = 0; 699 c->flag |= COPEN; 700 FS(c)->name = fswalkpath(FS(c)->name, name, 0); 701 free(acl); 702 } 703 704 void 705 fsclose(Chan *c) 706 { 707 HANDLE h; 708 709 if(c->flag & COPEN){ 710 h = ntfd2h(FS(c)->fd); 711 if(h != INVALID_HANDLE_VALUE){ 712 if(c->qid.type & QTDIR) 713 FindClose(h); 714 else 715 CloseHandle(h); 716 } 717 } 718 if(c->flag & CRCLOSE){ 719 if(!waserror()){ 720 fsremove(c); 721 poperror(); 722 } 723 return; 724 } 725 fsfree(c); 726 } 727 728 /* 729 * 64-bit seeks, using SetFilePointer because SetFilePointerEx 730 * is not supported by NT 731 */ 732 static void 733 fslseek(HANDLE h, vlong offset) 734 { 735 LONG hi; 736 737 if(offset <= 0x7fffffff){ 738 if(SetFilePointer(h, (LONG)offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) 739 oserror(); 740 }else{ 741 hi = offset>>32; 742 if(SetFilePointer(h, (LONG)offset, &hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER && 743 GetLastError() != NO_ERROR) 744 oserror(); 745 } 746 } 747 748 long 749 fsread(Chan *c, void *va, long n, vlong offset) 750 { 751 DWORD n2; 752 HANDLE h; 753 754 qlock(&FS(c)->oq); 755 if(waserror()){ 756 qunlock(&FS(c)->oq); 757 nexterror(); 758 } 759 if(c->qid.type & QTDIR) { 760 n2 = fsdirread(c, va, n, offset); 761 } 762 else { 763 h = ntfd2h(FS(c)->fd); 764 if(FS(c)->offset != offset){ 765 fslseek(h, offset); 766 FS(c)->offset = offset; 767 } 768 if(!ReadFile(h, va, n, &n2, NULL)) 769 oserror(); 770 FS(c)->offset += n2; 771 } 772 qunlock(&FS(c)->oq); 773 poperror(); 774 return n2; 775 } 776 777 long 778 fswrite(Chan *c, void *va, long n, vlong offset) 779 { 780 DWORD n2; 781 HANDLE h; 782 783 qlock(&FS(c)->oq); 784 if(waserror()){ 785 qunlock(&FS(c)->oq); 786 nexterror(); 787 } 788 h = ntfd2h(FS(c)->fd); 789 if(FS(c)->offset != offset){ 790 fslseek(h, offset); 791 FS(c)->offset = offset; 792 } 793 if(!WriteFile(h, va, n, &n2, NULL)) 794 oserror(); 795 FS(c)->offset += n2; 796 qunlock(&FS(c)->oq); 797 poperror(); 798 return n2; 799 } 800 801 int 802 fsstat(Chan *c, uchar *buf, int n) 803 { 804 WIN32_FIND_DATA data; 805 char path[MAX_PATH]; 806 wchar_t *wpath; 807 808 /* 809 * have to fake up a data for volumes like 810 * c: and \\server\share since you can't FindFirstFile them 811 */ 812 if(fsisroot(c)){ 813 strcpy(path, rootdir); 814 if(strchr(path, '\\') == nil) 815 strcat(path, "\\."); 816 wpath = widen(path); 817 data.dwFileAttributes = GetFileAttributes(wpath); 818 free(wpath); 819 if(data.dwFileAttributes == 0xffffffff) 820 oserror(); 821 data.ftCreationTime = 822 data.ftLastAccessTime = 823 data.ftLastWriteTime = wintime(time(0)); 824 data.nFileSizeHigh = 0; 825 data.nFileSizeLow = 0; 826 utftorunes(data.cFileName, ".", MAX_PATH); 827 } else { 828 HANDLE h = INVALID_HANDLE_VALUE; 829 830 fspath(FS(c)->name, 0, path, FS(c)->spec); 831 if (c->flag & COPEN) 832 h = ntfd2h(FS(c)->fd); 833 834 if (h != INVALID_HANDLE_VALUE) { 835 BY_HANDLE_FILE_INFORMATION fi; 836 if (c->mode & OWRITE) 837 FlushFileBuffers(h); 838 if (!GetFileInformationByHandle(h, &fi)) 839 oserror(); 840 data.dwFileAttributes = fi.dwFileAttributes; 841 data.ftCreationTime = fi.ftCreationTime; 842 data.ftLastAccessTime = fi.ftLastAccessTime; 843 data.ftLastWriteTime = fi.ftLastWriteTime;; 844 data.nFileSizeHigh = fi.nFileSizeHigh; 845 data.nFileSizeLow = fi.nFileSizeLow; 846 } else { 847 wpath = widen(path); 848 h = FindFirstFile(wpath, &data); 849 free(wpath); 850 if(h == INVALID_HANDLE_VALUE) 851 oserror(); 852 if (!winfilematch(path, &data)) { 853 FindClose(h); 854 error(Enonexist); 855 } 856 FindClose(h); 857 } 858 utftorunes(data.cFileName, fslastelem(FS(c)->name), MAX_PATH); 859 } 860 861 return fsdirset(buf, n, &data, path, c, 0); 862 } 863 864 int 865 fswstat(Chan *c, uchar *buf, int n) 866 { 867 int wsd; 868 Dir dir; 869 Stat st; 870 Cname * volatile ph; 871 HANDLE h; 872 ulong attr; 873 User *ou, *gu; 874 WIN32_FIND_DATA data; 875 SECURITY_DESCRIPTOR *sd; 876 char *last, sdrock[SD_ROCK], path[MAX_PATH], newpath[MAX_PATH], strs[4*256]; 877 wchar_t wspath[MAX_PATH], wsnewpath[MAX_PATH]; 878 wchar_t *wpath; 879 int nmatch; 880 881 n = convM2D(buf, n, &dir, strs); 882 if(n == 0) 883 error(Eshortstat); 884 885 last = fspath(FS(c)->name, 0, path, FS(c)->spec); 886 utftorunes(wspath, path, MAX_PATH); 887 888 if(fsisroot(c)){ 889 if(dir.atime != ~0) 890 data.ftLastAccessTime = wintime(dir.atime); 891 if(dir.mtime != ~0) 892 data.ftLastWriteTime = wintime(dir.mtime); 893 utftorunes(data.cFileName, ".", MAX_PATH); 894 }else{ 895 h = FindFirstFile(wspath, &data); 896 if(h == INVALID_HANDLE_VALUE) 897 oserror(); 898 if (!winfilematch(path, &data)) { 899 FindClose(h); 900 error(Enonexist); 901 } 902 FindClose(h); 903 } 904 905 wsd = 0; 906 ou = nil; 907 gu = nil; 908 if(FS(c)->usesec) { 909 if(FS(c)->checksec && up->env->ui == fsnone) 910 error(Eperm); 911 912 /* 913 * find new owner and group 914 */ 915 if(!emptystr(dir.uid)){ 916 ou = unametouser(FS(c)->srv, dir.uid); 917 if(ou == nil) 918 oserror(); 919 } 920 if(!emptystr(dir.gid)){ 921 gu = unametouser(FS(c)->srv, dir.gid); 922 if(gu == nil){ 923 if(strcmp(dir.gid, "unknown") != 0 924 && strcmp(dir.gid, "deleted") != 0) 925 oserror(); 926 gu = ou; 927 } 928 } 929 930 /* 931 * find old stat info 932 */ 933 sd = secsd(path, sdrock); 934 if(sd == nil || !secsdstat(sd, &st, FS(c)->srv)){ 935 if(sd != nil && sd != (void*)sdrock) 936 free(sd); 937 oserror(); 938 } 939 if(sd != (void*)sdrock) 940 free(sd); 941 942 /* 943 * permission rules: 944 * if none, can't do anything 945 * chown => no way 946 * chgrp => current owner or group, and in new group 947 * mode/time => owner or in either group 948 * rename => write in parent 949 */ 950 if(ou == nil) 951 ou = st.owner; 952 if(FS(c)->checksec && st.owner != ou) 953 error(Eperm); 954 955 if(gu == nil) 956 gu = st.group; 957 if(st.group != gu){ 958 if(FS(c)->checksec 959 &&(!ismember(up->env->ui, ou) && !ismember(up->env->ui, gu) 960 || !ismember(up->env->ui, st.group))) 961 error(Eperm); 962 wsd = 1; 963 } 964 965 if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime 966 || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime 967 || dir.mode != ~0 && st.mode != dir.mode){ 968 if(FS(c)->checksec 969 && !ismember(up->env->ui, ou) 970 && !ismember(up->env->ui, gu) 971 && !ismember(up->env->ui, st.group)) 972 error(Eperm); 973 if(dir.mode != ~0 && st.mode != dir.mode) 974 wsd = 1; 975 } 976 } 977 wpath = widen(dir.name); 978 nmatch = runescmp(wpath, data.cFileName); 979 free(wpath); 980 if(!emptystr(dir.name) && nmatch != 0){ 981 if(!okelem(dir.name, 1)) 982 error(Efilename); 983 ph = fswalkpath(FS(c)->name, "..", 1); 984 if(waserror()){ 985 cnameclose(ph); 986 nexterror(); 987 } 988 ph = fswalkpath(ph, dir.name, 0); 989 fspath(ph, 0, newpath, FS(c)->spec); 990 utftorunes(wsnewpath, newpath, MAX_PATH); 991 if(GetFileAttributes(wpath) != 0xffffffff && !winfileclash(newpath)) 992 error("file already exists"); 993 if(fsisroot(c)) 994 error(Eperm); 995 if(FS(c)->checksec){ 996 *last = '\0'; 997 seccheck(path, WMODE, FS(c)->srv); 998 *last = '\\'; 999 } 1000 poperror(); 1001 cnameclose(ph); 1002 } 1003 1004 if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime 1005 || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime) 1006 fssettime(path, dir.atime, dir.mtime); 1007 1008 attr = data.dwFileAttributes; 1009 if(dir.mode & 0222) 1010 attr &= ~FILE_ATTRIBUTE_READONLY; 1011 else 1012 attr |= FILE_ATTRIBUTE_READONLY; 1013 if(!fsisroot(c) 1014 && attr != data.dwFileAttributes 1015 && (attr & FILE_ATTRIBUTE_READONLY)) 1016 SetFileAttributes(wspath, attr); 1017 if(FS(c)->usesec && wsd){ 1018 ACL *acl = (ACL *) smalloc(ACL_ROCK); 1019 st.owner = ou; 1020 st.group = gu; 1021 if(dir.mode != ~0) 1022 st.mode = dir.mode; 1023 sd = secmksd(sdrock, &st, acl, data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); 1024 if(sd == nil || !SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd)){ 1025 free(acl); 1026 oserror(); 1027 } 1028 free(acl); 1029 } 1030 1031 if(!fsisroot(c) 1032 && attr != data.dwFileAttributes 1033 && !(attr & FILE_ATTRIBUTE_READONLY)) 1034 SetFileAttributes(wspath, attr); 1035 1036 /* do last so path is valid throughout */ 1037 wpath = widen(dir.name); 1038 nmatch = runescmp(wpath, data.cFileName); 1039 free(wpath); 1040 if(!emptystr(dir.name) && nmatch != 0) { 1041 ph = fswalkpath(FS(c)->name, "..", 1); 1042 if(waserror()){ 1043 cnameclose(ph); 1044 nexterror(); 1045 } 1046 ph = fswalkpath(ph, dir.name, 0); 1047 fspath(ph, 0, newpath, FS(c)->spec); 1048 utftorunes(wsnewpath, newpath, MAX_PATH); 1049 /* 1050 * can't rename if it is open: if this process has it open, close it temporarily. 1051 */ 1052 if(!file_share_delete && c->flag & COPEN){ 1053 h = ntfd2h(FS(c)->fd); 1054 if(h != INVALID_HANDLE_VALUE) 1055 CloseHandle(h); /* woe betide it if ORCLOSE */ 1056 FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); 1057 } 1058 if(!MoveFile(wspath, wsnewpath)) { 1059 oserror(); 1060 } else if(!file_share_delete && c->flag & COPEN) { 1061 int aflag; 1062 SECURITY_ATTRIBUTES sa; 1063 1064 /* The move succeeded, so open new file to maintain handle */ 1065 sa.nLength = sizeof(sa); 1066 sa.lpSecurityDescriptor = sd; 1067 sa.bInheritHandle = 0; 1068 if(c->flag & CRCLOSE) 1069 aflag = FILE_FLAG_DELETE_ON_CLOSE; 1070 h = CreateFile(wsnewpath, fsomode(c->mode & 0x3), FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, OPEN_EXISTING, aflag, 0); 1071 if(h == INVALID_HANDLE_VALUE) 1072 oserror(); 1073 FS(c)->fd = nth2fd(h); 1074 } 1075 cnameclose(FS(c)->name); 1076 poperror(); 1077 FS(c)->name = ph; 1078 } 1079 return n; 1080 } 1081 1082 static void 1083 fsremove(Chan *c) 1084 { 1085 int n; 1086 char *p, path[MAX_PATH]; 1087 wchar_t wspath[MAX_PATH]; 1088 1089 if(waserror()){ 1090 fsfree(c); 1091 nexterror(); 1092 } 1093 if(fsisroot(c)) 1094 error(Eperm); 1095 p = fspath(FS(c)->name, 0, path, FS(c)->spec); 1096 utftorunes(wspath, path, MAX_PATH); 1097 if(FS(c)->checksec){ 1098 *p = '\0'; 1099 seccheck(path, WMODE, FS(c)->srv); 1100 *p = '\\'; 1101 } 1102 if(c->qid.type & QTDIR) 1103 n = RemoveDirectory(wspath); 1104 else 1105 n = DeleteFile(wspath); 1106 if (!n) { 1107 ulong attr, mode; 1108 SECURITY_DESCRIPTOR *sd = nil; 1109 char sdrock[SD_ROCK]; 1110 Stat st; 1111 int secok; 1112 attr = GetFileAttributes(wspath); 1113 if(attr != 0xFFFFFFFF) { 1114 if (FS(c)->usesec) { 1115 sd = secsd(path, sdrock); 1116 secok = (sd != nil) && secsdstat(sd, &st, FS(c)->srv); 1117 if (secok) { 1118 ACL *acl = (ACL *) smalloc(ACL_ROCK); 1119 mode = st.mode; 1120 st.mode |= 0660; 1121 sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY); 1122 if(sd != nil) { 1123 SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd); 1124 } 1125 free(acl); 1126 if(sd != nil && sd != (void*)sdrock) 1127 free(sd); 1128 sd = nil; 1129 } 1130 } 1131 SetFileAttributes(wspath, FILE_ATTRIBUTE_NORMAL); 1132 if(c->qid.type & QTDIR) 1133 n = RemoveDirectory(wspath); 1134 else 1135 n = DeleteFile(wspath); 1136 if (!n) { 1137 if (FS(c)->usesec && secok) { 1138 ACL *acl = (ACL *) smalloc(ACL_ROCK); 1139 st.mode = mode; 1140 sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY); 1141 if(sd != nil) { 1142 SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd); 1143 } 1144 free(acl); 1145 } 1146 SetFileAttributes(wspath, attr); 1147 if(sd != nil && sd != (void*)sdrock) 1148 free(sd); 1149 } 1150 } 1151 } 1152 if(!n) 1153 oserror(); 1154 poperror(); 1155 fsfree(c); 1156 } 1157 1158 /* 1159 * check elem for illegal characters /\:*?"<> 1160 * ... and relatives are also disallowed, 1161 * since they specify grandparents, which we 1162 * are not prepared to handle 1163 */ 1164 static int 1165 okelem(char *elem, int nodots) 1166 { 1167 int c, dots; 1168 1169 dots = 0; 1170 while((c = *(uchar*)elem) != 0){ 1171 if(isntfrog[c]) 1172 return 0; 1173 if(c == '.' && dots >= 0) 1174 dots++; 1175 else 1176 dots = -1; 1177 elem++; 1178 } 1179 if(nodots) 1180 return dots <= 0; 1181 return dots <= 2; 1182 } 1183 1184 static int 1185 cnisroot(Cname *c) 1186 { 1187 return strcmp(c->s, "/") == 0; 1188 } 1189 1190 static int 1191 fsisroot(Chan *c) 1192 { 1193 return strcmp(FS(c)->name->s, "/") == 0; 1194 } 1195 1196 static char* 1197 fspath(Cname *c, char *ext, char *path, char *spec) 1198 { 1199 char *p, *last, *rootd; 1200 int extlen = 0; 1201 1202 rootd = spec != nil ? spec : rootdir; 1203 if(ext) 1204 extlen = strlen(ext) + 1; 1205 if(strlen(rootd) + extlen >= MAX_PATH) 1206 error(Etoolong); 1207 strcpy(path, rootd); 1208 if(cnisroot(c)){ 1209 if(ext) { 1210 strcat(path, "\\"); 1211 strcat(path, ext); 1212 } 1213 }else{ 1214 if(*c->s != '/') { 1215 if(strlen(path) + 1 >= MAX_PATH) 1216 error(Etoolong); 1217 strcat(path, "\\"); 1218 } 1219 if(strlen(path) + strlen(c->s) + extlen >= MAX_PATH) 1220 error(Etoolong); 1221 strcat(path, c->s); 1222 if(ext){ 1223 strcat(path, "\\"); 1224 strcat(path, ext); 1225 } 1226 } 1227 last = path; 1228 for(p = path; *p != '\0'; p++){ 1229 if(*p == '/' || *p == '\\'){ 1230 *p = '\\'; 1231 last = p; 1232 } 1233 } 1234 return last; 1235 } 1236 1237 extern void cleancname(Cname*); 1238 1239 static Cname * 1240 fswalkpath(Cname *c, char *name, int dup) 1241 { 1242 if(dup) 1243 c = newcname(c->s); 1244 c = addelem(c, name); 1245 if(isdotdot(name)) 1246 cleancname(c); 1247 return c; 1248 } 1249 1250 static char * 1251 fslastelem(Cname *c) 1252 { 1253 char *p; 1254 1255 p = c->s + c->len; 1256 while(p > c->s && p[-1] != '/') 1257 p--; 1258 return p; 1259 } 1260 1261 static int 1262 fsdirbadentry(WIN32_FIND_DATA *data) 1263 { 1264 wchar_t *s; 1265 1266 s = data->cFileName; 1267 if(s[0] == 0) 1268 return 1; 1269 if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0)) 1270 return 1; 1271 1272 return 0; 1273 } 1274 1275 static Fsdir* 1276 fsdirent(Chan *c, char *path, Fsdir *data) 1277 { 1278 wchar_t *wpath; 1279 HANDLE h; 1280 1281 h = ntfd2h(FS(c)->fd); 1282 if(data == nil) 1283 data = smalloc(sizeof(*data)); 1284 if(FS(c)->offset == 0){ 1285 if(h != INVALID_HANDLE_VALUE) 1286 FindClose(h); 1287 wpath = widen(path); 1288 h = FindFirstFile(wpath, data); 1289 free(wpath); 1290 FS(c)->fd = nth2fd(h); 1291 if(h == INVALID_HANDLE_VALUE){ 1292 free(data); 1293 return nil; 1294 } 1295 if(!fsdirbadentry(data)) 1296 return data; 1297 } 1298 do{ 1299 if(!FindNextFile(h, data)){ 1300 free(data); 1301 return nil; 1302 } 1303 }while(fsdirbadentry(data)); 1304 return data; 1305 } 1306 1307 static long 1308 fsdirread(Chan *c, uchar *va, int count, vlong offset) 1309 { 1310 int i, r; 1311 char path[MAX_PATH], *p; 1312 Fsdir *de; 1313 vlong o; 1314 1315 if(count == 0 || offset < 0) 1316 return 0; 1317 p = fspath(FS(c)->name, "*.*", path, FS(c)->spec); 1318 p++; 1319 de = nil; 1320 if(FS(c)->offset != offset){ 1321 de = FS(c)->de; 1322 if(FS(c)->de != nil){ 1323 free(FS(c)->de); 1324 FS(c)->de = nil; 1325 } 1326 FS(c)->offset = 0; 1327 for(o = 0; o < offset;){ 1328 de = fsdirent(c, path, de); 1329 if(de == nil){ 1330 FS(c)->offset = o; 1331 return 0; 1332 } 1333 runestoutf(p, de->cFileName, &path[MAX_PATH]-p); 1334 path[MAX_PATH-1] = '\0'; 1335 o += fsdirsize(de, path, c); 1336 } 1337 FS(c)->offset = offset; 1338 } 1339 for(i = 0; i < count;){ 1340 if(FS(c)->de != nil){ /* left over from previous read at offset */ 1341 de = FS(c)->de; 1342 FS(c)->de = nil; 1343 }else{ 1344 de = fsdirent(c, path, de); 1345 if(de == nil) 1346 break; 1347 } 1348 runestoutf(p, de->cFileName, &path[MAX_PATH]-p); 1349 path[MAX_PATH-1] = '\0'; 1350 r = fsdirset(va+i, count-i, de, path, c, 1); 1351 if(r <= 0){ 1352 /* won't fit; save for next read at this offset */ 1353 FS(c)->de = de; 1354 break; 1355 } 1356 i += r; 1357 FS(c)->offset += r; 1358 } 1359 return i; 1360 } 1361 1362 static ulong 1363 fsqidpath(char *p) 1364 { 1365 ulong h; 1366 int c; 1367 1368 h = 0; 1369 while(*p != '\0'){ 1370 /* force case insensitive file names */ 1371 c = *p++; 1372 if(c >= 'A' && c <= 'Z') 1373 c += 'a'-'A'; 1374 h = h * 19 ^ c; 1375 } 1376 return h; 1377 } 1378 1379 /* TO DO: substitute fixed, made-up (unlikely) names for these */ 1380 static char* devf[] = { "aux", "com1", "com2", "lpt1", "nul", "prn", nil }; 1381 1382 static int 1383 devfile(char *p) 1384 { 1385 char *s, *t, *u, **ss; 1386 1387 if((u = strrchr(p, '\\')) != nil) 1388 u++; 1389 else if((u = strrchr(p, '/')) != nil) 1390 u++; 1391 else 1392 u = p; 1393 for(ss = devf; *ss != nil; ss++){ 1394 for(s = *ss, t = u; *s != '\0' && *t != '\0' && *t != '.'; s++, t++) 1395 if(*s != *t && *s != *t+'a'-'A') 1396 break; 1397 if(*s == '\0' && (*t == '\0' || *t == '.')) 1398 return 1; 1399 } 1400 return 0; 1401 } 1402 1403 /* 1404 * there are other ways to figure out 1405 * the attributes and times for a file. 1406 * perhaps they are faster 1407 */ 1408 static int 1409 fsexist(char *p, Qid *q) 1410 { 1411 HANDLE h; 1412 WIN32_FIND_DATA data; 1413 wchar_t *wpath; 1414 1415 if(devfile(p)) 1416 return 0; 1417 wpath = widen(p); 1418 h = FindFirstFile(wpath, &data); 1419 free(wpath); 1420 if(h == INVALID_HANDLE_VALUE) 1421 return 0; 1422 if (!winfilematch(p, &data)) { 1423 FindClose(h); 1424 return 0; 1425 } 1426 FindClose(h); 1427 1428 q->path = fsqidpath(p); 1429 q->type = 0; 1430 1431 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1432 q->type |= QTDIR; 1433 1434 q->vers = unixtime(data.ftLastWriteTime); 1435 1436 return 1; 1437 } 1438 1439 static int 1440 fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir) 1441 { 1442 Dir dir; 1443 static char neveryone[] = "Everyone"; 1444 1445 dir.name = narrowen(data->cFileName); 1446 dir.muid = nil; 1447 dir.qid.path = fsqidpath(path); 1448 dir.qid.vers = 0; 1449 dir.qid.type = 0; 1450 dir.mode = 0; 1451 dir.atime = unixtime(data->ftLastAccessTime); 1452 dir.mtime = unixtime(data->ftLastWriteTime); 1453 dir.qid.vers = dir.mtime; 1454 dir.length = ((uvlong)data->nFileSizeHigh<<32) | ((uvlong)data->nFileSizeLow & ~((uvlong)0xFFFFFFFF<<32)); 1455 dir.type = 'U'; 1456 dir.dev = c->dev; 1457 1458 if(!FS(c)->usesec){ 1459 /* no NT security so make something up */ 1460 dir.uid = neveryone; 1461 dir.gid = neveryone; 1462 dir.mode = 0777; 1463 }else if(!secstat(&dir, path, FS(c)->srv)) 1464 oserror(); 1465 1466 if(data->dwFileAttributes & FILE_ATTRIBUTE_READONLY) 1467 dir.mode &= ~0222; 1468 if(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ 1469 dir.qid.type |= QTDIR; 1470 dir.mode |= DMDIR; 1471 dir.length = 0; 1472 } 1473 1474 if(isdir && sizeD2M(&dir) > n) 1475 n = -1; 1476 else 1477 n = convD2M(&dir, edir, n); 1478 if(dir.uid != neveryone) 1479 free(dir.uid); 1480 if(dir.gid != neveryone) 1481 free(dir.gid); 1482 free(dir.name); 1483 return n; 1484 } 1485 1486 static int 1487 fsdirsize(WIN32_FIND_DATA *data, char *path, Chan *c) 1488 { 1489 int i, n; 1490 1491 n = widebytes(data->cFileName); 1492 if(!FS(c)->usesec) 1493 n += 8+8; 1494 else{ 1495 i = secsize(path, FS(c)->srv); 1496 if(i < 0) 1497 oserror(); 1498 n += i; 1499 } 1500 return STATFIXLEN+n; 1501 } 1502 1503 static void 1504 fssettime(char *path, long at, long mt) 1505 { 1506 HANDLE h; 1507 FILETIME atime, mtime; 1508 wchar_t *wpath; 1509 1510 wpath = widen(path); 1511 h = CreateFile(wpath, GENERIC_WRITE, 1512 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 1513 free(wpath); 1514 if(h == INVALID_HANDLE_VALUE) 1515 return; 1516 mtime = wintime(mt); 1517 atime = wintime(at); 1518 if(!SetFileTime(h, 0, &atime, &mtime)){ 1519 CloseHandle(h); 1520 oserror(); 1521 } 1522 CloseHandle(h); 1523 } 1524 1525 static int 1526 fsomode(int m) 1527 { 1528 switch(m & 0x3) { 1529 case OREAD: 1530 case OEXEC: 1531 return GENERIC_READ; 1532 case OWRITE: 1533 return GENERIC_WRITE; 1534 case ORDWR: 1535 return GENERIC_READ|GENERIC_WRITE; 1536 } 1537 error(Ebadarg); 1538 return 0; 1539 } 1540 1541 static long 1542 unixtime(FILETIME ft) 1543 { 1544 vlong t; 1545 1546 t = (vlong)ft.dwLowDateTime + ((vlong)ft.dwHighDateTime<<32); 1547 t -= (vlong)10000000*134774*24*60*60; 1548 1549 return (long)(t/10000000); 1550 } 1551 1552 static FILETIME 1553 wintime(ulong t) 1554 { 1555 FILETIME ft; 1556 vlong vt; 1557 1558 vt = (vlong)t*10000000+(vlong)10000000*134774*24*60*60; 1559 1560 ft.dwLowDateTime = vt; 1561 ft.dwHighDateTime = vt>>32; 1562 1563 return ft; 1564 } 1565 1566 /* 1567 * the sec routines manage file permissions for nt. 1568 * nt files have an associated security descriptor, 1569 * which has in it an owner, a group, 1570 * and a discretionary acces control list, or acl, 1571 * which specifies the permissions for the file. 1572 * 1573 * the strategy for mapping between inferno owner, 1574 * group, other, and mode and nt file security is: 1575 * 1576 * inferno owner == nt file owner 1577 * inferno other == nt Everyone 1578 * inferno group == first non-owner, 1579 * non-Everyone user given in the acl, 1580 * or the owner if there is no such user. 1581 * we examine the entire acl when check for permissions, 1582 * but only report a subset. 1583 * 1584 * when we write an acl, we also give all permissions to 1585 * the special user rootname, who is supposed to run emu in server mode. 1586 */ 1587 static void 1588 secinit(void) 1589 { 1590 HMODULE lib; 1591 HANDLE token; 1592 TOKEN_PRIVILEGES *priv; 1593 char privrock[sizeof(TOKEN_PRIVILEGES) + 1*sizeof(LUID_AND_ATTRIBUTES)]; 1594 SID_IDENTIFIER_AUTHORITY id = SECURITY_CREATOR_SID_AUTHORITY; 1595 SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY; 1596 SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY; 1597 1598 lib = LoadLibraryA("netapi32"); 1599 if(lib == 0) { 1600 usesec = 0; 1601 return; 1602 } 1603 1604 net.UserGetGroups = (void*)GetProcAddress(lib, "NetUserGetGroups"); 1605 if(net.UserGetGroups == 0) 1606 panic("bad netapi32 library"); 1607 net.UserGetLocalGroups = (void*)GetProcAddress(lib, "NetUserGetLocalGroups"); 1608 if(net.UserGetLocalGroups == 0) 1609 panic("bad netapi32 library"); 1610 net.GetAnyDCName = (void*)GetProcAddress(lib, "NetGetAnyDCName"); 1611 if(net.GetAnyDCName == 0) 1612 panic("bad netapi32 library"); 1613 net.ApiBufferFree = (void*)GetProcAddress(lib, "NetApiBufferFree"); 1614 if(net.ApiBufferFree == 0) 1615 panic("bad netapi32 library"); 1616 1617 if(!AllocateAndInitializeSid(&id, 1, 1618 SECURITY_CREATOR_OWNER_RID, 1619 1, 2, 3, 4, 5, 6, 7, &creatorowner) 1620 || !AllocateAndInitializeSid(&id, 1, 1621 SECURITY_CREATOR_GROUP_RID, 1622 1, 2, 3, 4, 5, 6, 7, &creatorgroup) 1623 || !AllocateAndInitializeSid(&wid, 1, 1624 SECURITY_WORLD_RID, 1625 1, 2, 3, 4, 5, 6, 7, &everyone) 1626 || !AllocateAndInitializeSid(&ntid, 1, 1627 0, 1628 1, 2, 3, 4, 5, 6, 7, &ntignore)) 1629 panic("can't initialize well-known sids"); 1630 1631 fsnone = sidtouser(ntsrv, everyone); 1632 if(fsnone == nil) 1633 panic("can't make none user"); 1634 1635 /* 1636 * see if we are running as the emu server user 1637 * if so, set up SE_RESTORE_NAME privilege, 1638 * which allows setting the owner field in a security descriptor. 1639 * other interesting privileges are SE_TAKE_OWNERSHIP_NAME, 1640 * which enables changing the ownership of a file to yourself 1641 * regardless of the permissions on the file, SE_BACKUP_NAME, 1642 * which enables reading any files regardless of permission, 1643 * and SE_CHANGE_NOTIFY_NAME, which enables walking through 1644 * directories without X permission. 1645 * SE_RESTORE_NAME and SE_BACKUP_NAME together allow writing 1646 * and reading any file data, regardless of permission, 1647 * if the file is opened with FILE_BACKUP_SEMANTICS. 1648 */ 1649 isserver = 0; 1650 fsuser = secuser(); 1651 if(fsuser == nil) 1652 fsuser = fsnone; 1653 else if(runescmp(fsuser->name, rootname) == 0 1654 && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)){ 1655 priv = (TOKEN_PRIVILEGES*)privrock; 1656 priv->PrivilegeCount = 1; 1657 priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1658 if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[0].Luid) 1659 && AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL)) 1660 isserver = 1; 1661 CloseHandle(token); 1662 } 1663 } 1664 1665 /* 1666 * get the User for the executing process 1667 */ 1668 static User* 1669 secuser(void) 1670 { 1671 DWORD need; 1672 HANDLE token; 1673 TOKEN_USER *tu; 1674 char turock[sizeof(TOKEN_USER) + MAX_SID]; 1675 1676 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) 1677 return nil; 1678 1679 tu = (TOKEN_USER*)turock; 1680 if(!GetTokenInformation(token, TokenUser, tu, sizeof(turock), &need)){ 1681 CloseHandle(token); 1682 return nil; 1683 } 1684 CloseHandle(token); 1685 return sidtouser(nil, tu->User.Sid); 1686 } 1687 1688 static int 1689 secstat(Dir *dir, char *file, Rune *srv) 1690 { 1691 int ok, n; 1692 Stat st; 1693 char sdrock[SD_ROCK]; 1694 SECURITY_DESCRIPTOR *sd; 1695 1696 sd = secsd(file, sdrock); 1697 if(sd == nil){ 1698 int e = GetLastError(); 1699 if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION){ 1700 dir->uid = strdup("unknown"); 1701 dir->gid = strdup("unknown"); 1702 if(dir->uid == nil || dir->gid == nil){ 1703 free(dir->uid); 1704 error(Enomem); /* will change to use kstrdup */ 1705 } 1706 dir->mode = 0; 1707 return 1; 1708 } 1709 return 0; 1710 } 1711 ok = secsdstat(sd, &st, srv); 1712 if(sd != (void*)sdrock) 1713 free(sd); 1714 if(ok){ 1715 dir->mode = st.mode; 1716 n = runenlen(st.owner->name, runeslen(st.owner->name)); 1717 dir->uid = smalloc(n+1); 1718 runestoutf(dir->uid, st.owner->name, n+1); 1719 n = runenlen(st.group->name, runeslen(st.group->name)); 1720 dir->gid = smalloc(n+1); 1721 runestoutf(dir->gid, st.group->name, n+1); 1722 } 1723 return ok; 1724 } 1725 1726 static int 1727 secsize(char *file, Rune *srv) 1728 { 1729 int ok; 1730 Stat st; 1731 char sdrock[SD_ROCK]; 1732 SECURITY_DESCRIPTOR *sd; 1733 1734 sd = secsd(file, sdrock); 1735 if(sd == nil){ 1736 int e = GetLastError(); 1737 if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION) 1738 return 7+7; 1739 return -1; 1740 } 1741 ok = secsdstat(sd, &st, srv); 1742 if(sd != (void*)sdrock) 1743 free(sd); 1744 if(ok) 1745 return runenlen(st.owner->name, runeslen(st.owner->name))+runenlen(st.group->name, runeslen(st.group->name)); 1746 return -1; 1747 } 1748 1749 /* 1750 * verify that u had access to file 1751 */ 1752 static void 1753 seccheck(char *file, ulong access, Rune *srv) 1754 { 1755 if(!sechasperm(file, access, srv)) 1756 error(Eperm); 1757 } 1758 1759 static int 1760 sechasperm(char *file, ulong access, Rune *srv) 1761 { 1762 int ok; 1763 char sdrock[SD_ROCK]; 1764 SECURITY_DESCRIPTOR *sd; 1765 1766 /* 1767 * only really needs dacl info 1768 */ 1769 sd = secsd(file, sdrock); 1770 if(sd == nil) 1771 return 0; 1772 ok = secsdhasperm(sd, access, srv); 1773 if(sd != (void*)sdrock) 1774 free(sd); 1775 return ok; 1776 } 1777 1778 static SECURITY_DESCRIPTOR* 1779 secsd(char *file, char sdrock[SD_ROCK]) 1780 { 1781 DWORD need; 1782 SECURITY_DESCRIPTOR *sd; 1783 char *path, pathrock[6]; 1784 wchar_t *wpath; 1785 1786 path = file; 1787 if(path[0] != '\0' && path[1] == ':' && path[2] == '\0'){ 1788 path = pathrock; 1789 strcpy(path, "?:\\."); 1790 path[0] = file[0]; 1791 } 1792 sd = (SECURITY_DESCRIPTOR*)sdrock; 1793 need = 0; 1794 wpath = widen(path); 1795 if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, SD_ROCK, &need)) { 1796 free(wpath); 1797 return sd; 1798 } 1799 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { 1800 free(wpath); 1801 return nil; 1802 } 1803 sd = malloc(need); 1804 if(sd == nil) { 1805 free(wpath); 1806 error(Enomem); 1807 } 1808 if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, need, &need)) { 1809 free(wpath); 1810 return sd; 1811 } 1812 free(wpath); 1813 free(sd); 1814 return nil; 1815 } 1816 1817 static int 1818 secsdstat(SECURITY_DESCRIPTOR *sd, Stat *st, Rune *srv) 1819 { 1820 ACL *acl; 1821 BOOL hasacl, b; 1822 ACE_HEADER *aceh; 1823 User *owner, *group; 1824 SID *sid, *osid, *gsid; 1825 ACCESS_ALLOWED_ACE *ace; 1826 int i, allow, deny, *p, m; 1827 ACL_SIZE_INFORMATION size; 1828 1829 st->mode = 0; 1830 1831 osid = nil; 1832 gsid = nil; 1833 if(!GetSecurityDescriptorOwner(sd, &osid, &b) 1834 || !GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b)) 1835 return 0; 1836 1837 if(acl == 0) 1838 size.AceCount = 0; 1839 else if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation)) 1840 return 0; 1841 1842 /* 1843 * first pass through acl finds group 1844 */ 1845 for(i = 0; i < size.AceCount; i++){ 1846 if(!GetAce(acl, i, &aceh)) 1847 continue; 1848 if(aceh->AceFlags & INHERIT_ONLY_ACE) 1849 continue; 1850 1851 if(aceh->AceType != ACCESS_ALLOWED_ACE_TYPE 1852 && aceh->AceType != ACCESS_DENIED_ACE_TYPE) 1853 continue; 1854 1855 ace = (ACCESS_ALLOWED_ACE*)aceh; 1856 sid = (SID*)&ace->SidStart; 1857 if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup)) 1858 continue; 1859 1860 if(EqualSid(sid, everyone)) 1861 ; 1862 else if(EqualSid(sid, osid)) 1863 ; 1864 else if(EqualPrefixSid(sid, ntignore)) 1865 continue; /* boring nt accounts */ 1866 else{ 1867 gsid = sid; 1868 break; 1869 } 1870 } 1871 if(gsid == nil) 1872 gsid = osid; 1873 1874 owner = sidtouser(srv, osid); 1875 if(owner == nil) 1876 return 0; 1877 group = sidtouser(srv, gsid); 1878 if(group == nil) 1879 return 0; 1880 1881 /* no acl means full access */ 1882 allow = 0; 1883 if(acl == 0) 1884 allow = 0777; 1885 deny = 0; 1886 for(i = 0; i < size.AceCount; i++){ 1887 if(!GetAce(acl, i, &aceh)) 1888 continue; 1889 if(aceh->AceFlags & INHERIT_ONLY_ACE) 1890 continue; 1891 1892 if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE) 1893 p = &allow; 1894 else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE) 1895 p = &deny; 1896 else 1897 continue; 1898 1899 ace = (ACCESS_ALLOWED_ACE*)aceh; 1900 sid = (SID*)&ace->SidStart; 1901 if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup)) 1902 continue; 1903 1904 m = 0; 1905 if(ace->Mask & FILE_EXECUTE) 1906 m |= 1; 1907 if(ace->Mask & FILE_WRITE_DATA) 1908 m |= 2; 1909 if(ace->Mask & FILE_READ_DATA) 1910 m |= 4; 1911 1912 if(ismembersid(srv, owner, sid)) 1913 *p |= (m << 6) & ~(allow|deny) & 0700; 1914 if(ismembersid(srv, group, sid)) 1915 *p |= (m << 3) & ~(allow|deny) & 0070; 1916 if(EqualSid(everyone, sid)) 1917 *p |= m & ~(allow|deny) & 0007; 1918 } 1919 1920 st->mode = allow & ~deny; 1921 st->owner = owner; 1922 st->group = group; 1923 return 1; 1924 } 1925 1926 static int 1927 secsdhasperm(SECURITY_DESCRIPTOR *sd, ulong access, Rune *srv) 1928 { 1929 User *u; 1930 ACL *acl; 1931 BOOL hasacl, b; 1932 ACE_HEADER *aceh; 1933 SID *sid, *osid, *gsid; 1934 int i, allow, deny, *p, m; 1935 ACCESS_ALLOWED_ACE *ace; 1936 ACL_SIZE_INFORMATION size; 1937 1938 u = up->env->ui; 1939 allow = 0; 1940 deny = 0; 1941 osid = nil; 1942 gsid = nil; 1943 if(!GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b)) 1944 return 0; 1945 1946 /* no acl means full access */ 1947 if(acl == 0) 1948 return 1; 1949 if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation)) 1950 return 0; 1951 for(i = 0; i < size.AceCount; i++){ 1952 if(!GetAce(acl, i, &aceh)) 1953 continue; 1954 if(aceh->AceFlags & INHERIT_ONLY_ACE) 1955 continue; 1956 1957 if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE) 1958 p = &allow; 1959 else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE) 1960 p = &deny; 1961 else 1962 continue; 1963 1964 ace = (ACCESS_ALLOWED_ACE*)aceh; 1965 sid = (SID*)&ace->SidStart; 1966 if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup)) 1967 continue; 1968 1969 m = ace->Mask; 1970 1971 if(ismembersid(srv, u, sid)) 1972 *p |= m & ~(allow|deny); 1973 } 1974 1975 allow &= ~deny; 1976 1977 return (allow & access) == access; 1978 } 1979 1980 static SECURITY_DESCRIPTOR* 1981 secmksd(char *sdrock, Stat *st, ACL *dacl, int isdir) 1982 { 1983 int m; 1984 1985 ulong mode; 1986 ACE_HEADER *aceh; 1987 SECURITY_DESCRIPTOR *sd; 1988 1989 sd = (SECURITY_DESCRIPTOR*)sdrock; 1990 if(!InitializeAcl(dacl, ACL_ROCK, ACL_REVISION)) 1991 return nil; 1992 1993 mode = st->mode; 1994 if(st->owner == st->group){ 1995 mode |= (mode >> 3) & 0070; 1996 mode |= (mode << 3) & 0700; 1997 } 1998 1999 2000 m = modetomask[(mode>>6) & 7]; 2001 if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->owner->sid)) 2002 return nil; 2003 2004 if(isdir && !AddAccessAllowedAce(dacl, ACL_REVISION, m, creatorowner)) 2005 return nil; 2006 2007 m = modetomask[(mode>>3) & 7]; 2008 if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->group->sid)) 2009 return nil; 2010 2011 m = modetomask[(mode>>0) & 7]; 2012 if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, everyone)) 2013 return nil; 2014 2015 if(isdir){ 2016 /* hack to add inherit flags */ 2017 if(!GetAce(dacl, 1, &aceh)) 2018 return nil; 2019 aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; 2020 if(!GetAce(dacl, 2, &aceh)) 2021 return nil; 2022 aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; 2023 if(!GetAce(dacl, 3, &aceh)) 2024 return nil; 2025 aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; 2026 } 2027 2028 /* 2029 * allow server user to access any file 2030 */ 2031 if(isserver){ 2032 if(!AddAccessAllowedAce(dacl, ACL_REVISION, RMODE|WMODE|XMODE, fsuser->sid)) 2033 return nil; 2034 if(isdir){ 2035 if(!GetAce(dacl, 4, &aceh)) 2036 return nil; 2037 aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; 2038 } 2039 } 2040 2041 if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) 2042 return nil; 2043 if(!SetSecurityDescriptorDacl(sd, 1, dacl, 0)) 2044 return nil; 2045 // if(isserver && !SetSecurityDescriptorOwner(sd, st->owner->sid, 0)) 2046 // return nil; 2047 return sd; 2048 } 2049 2050 /* 2051 * the user manipulation routines 2052 * just make it easier to deal with user identities 2053 */ 2054 static User* 2055 sidtouser(Rune *srv, SID *s) 2056 { 2057 SID_NAME_USE type; 2058 Rune aname[100], dname[100]; 2059 DWORD naname, ndname; 2060 User *u; 2061 2062 qlock(&users.lk); 2063 for(u = users.u; u != 0; u = u->next) 2064 if(EqualSid(s, u->sid)) 2065 break; 2066 qunlock(&users.lk); 2067 2068 if(u != 0) 2069 return u; 2070 2071 naname = sizeof(aname); 2072 ndname = sizeof(dname); 2073 2074 if(!LookupAccountSidW(srv, s, aname, &naname, dname, &ndname, &type)) 2075 return mkuser(s, SidTypeUnknown, L"unknown", L"unknown"); 2076 return mkuser(s, type, aname, dname); 2077 } 2078 2079 static User* 2080 domnametouser(Rune *srv, Rune *name, Rune *dom) 2081 { 2082 User *u; 2083 2084 qlock(&users.lk); 2085 for(u = users.u; u != 0; u = u->next) 2086 if(runescmp(name, u->name) == 0 && runescmp(dom, u->dom) == 0) 2087 break; 2088 qunlock(&users.lk); 2089 if(u == 0) 2090 u = nametouser(srv, name); 2091 return u; 2092 } 2093 2094 static User* 2095 nametouser(Rune *srv, Rune *name) 2096 { 2097 char sidrock[MAX_SID]; 2098 SID *sid; 2099 SID_NAME_USE type; 2100 Rune dom[MAX_PATH]; 2101 DWORD nsid, ndom; 2102 2103 sid = (SID*)sidrock; 2104 nsid = sizeof(sidrock); 2105 ndom = sizeof(dom); 2106 if(!LookupAccountNameW(srv, name, sid, &nsid, dom, &ndom, &type)) 2107 return nil; 2108 2109 return mkuser(sid, type, name, dom); 2110 } 2111 2112 /* 2113 * this mapping could be cached 2114 */ 2115 static User* 2116 unametouser(Rune *srv, char *name) 2117 { 2118 Rune rname[MAX_PATH]; 2119 2120 utftorunes(rname, name, MAX_PATH); 2121 return nametouser(srv, rname); 2122 } 2123 2124 /* 2125 * make a user structure and add it to the global cache. 2126 */ 2127 static User* 2128 mkuser(SID *sid, int type, Rune *name, Rune *dom) 2129 { 2130 User *u; 2131 2132 qlock(&users.lk); 2133 for(u = users.u; u != 0; u = u->next){ 2134 if(EqualSid(sid, u->sid)){ 2135 qunlock(&users.lk); 2136 return u; 2137 } 2138 } 2139 2140 switch(type) { 2141 default: 2142 break; 2143 case SidTypeDeletedAccount: 2144 name = L"deleted"; 2145 break; 2146 case SidTypeInvalid: 2147 name = L"invalid"; 2148 break; 2149 case SidTypeUnknown: 2150 name = L"unknown"; 2151 break; 2152 } 2153 2154 u = malloc(sizeof(User)); 2155 if(u == nil){ 2156 qunlock(&users.lk); 2157 return 0; 2158 } 2159 u->next = nil; 2160 u->group = nil; 2161 u->sid = dupsid(sid); 2162 u->type = type; 2163 u->name = nil; 2164 if(name != nil) 2165 u->name = runesdup(name); 2166 u->dom = nil; 2167 if(dom != nil) 2168 u->dom = runesdup(dom); 2169 2170 u->next = users.u; 2171 users.u = u; 2172 2173 qunlock(&users.lk); 2174 return u; 2175 } 2176 2177 /* 2178 * check if u is a member of gsid, 2179 * which might be a group. 2180 */ 2181 static int 2182 ismembersid(Rune *srv, User *u, SID *gsid) 2183 { 2184 User *g; 2185 2186 if(EqualSid(u->sid, gsid)) 2187 return 1; 2188 2189 g = sidtouser(srv, gsid); 2190 if(g == 0) 2191 return 0; 2192 return ismember(u, g); 2193 } 2194 2195 static int 2196 ismember(User *u, User *g) 2197 { 2198 Gmem *grps; 2199 2200 if(EqualSid(u->sid, g->sid)) 2201 return 1; 2202 2203 if(EqualSid(g->sid, everyone)) 2204 return 1; 2205 2206 qlock(&u->lk); 2207 addgroups(u, 0); 2208 for(grps = u->group; grps != 0; grps = grps->next){ 2209 if(EqualSid(grps->user->sid, g->sid)){ 2210 qunlock(&u->lk); 2211 return 1; 2212 } 2213 } 2214 qunlock(&u->lk); 2215 return 0; 2216 } 2217 2218 /* 2219 * find out what groups a user belongs to. 2220 * if force, throw out the old info and do it again. 2221 * 2222 * note that a global group is also know as a group, 2223 * and a local group is also know as an alias. 2224 * global groups can only contain users. 2225 * local groups can contain global groups or users. 2226 * this code finds all global groups to which a user belongs, 2227 * and all the local groups to which the user or a global group 2228 * containing the user belongs. 2229 */ 2230 static void 2231 addgroups(User *u, int force) 2232 { 2233 LOCALGROUP_USERS_INFO_0 *loc; 2234 GROUP_USERS_INFO_0 *grp; 2235 DWORD i, n, rem; 2236 User *gu; 2237 Gmem *g, *next; 2238 Rune *srv, srvrock[MAX_PATH]; 2239 2240 if(force){ 2241 u->gotgroup = 0; 2242 for(g = u->group; g != nil; g = next){ 2243 next = g->next; 2244 free(g); 2245 } 2246 u->group = nil; 2247 } 2248 if(u->gotgroup) 2249 return; 2250 u->gotgroup = 1; 2251 2252 n = 0; 2253 srv = domsrv(u->dom, srvrock); 2254 i = net.UserGetGroups(srv, u->name, 0, 2255 (BYTE**)&grp, MAX_PREFERRED_LENGTH, &n, &rem); 2256 if(i == NERR_Success || i == ERROR_MORE_DATA){ 2257 for(i = 0; i < n; i++){ 2258 gu = domnametouser(srv, grp[i].grui0_name, u->dom); 2259 if(gu == 0) 2260 continue; 2261 g = malloc(sizeof(Gmem)); 2262 if(g == nil) 2263 error(Enomem); 2264 g->user = gu; 2265 g->next = u->group; 2266 u->group = g; 2267 } 2268 net.ApiBufferFree(grp); 2269 } 2270 2271 n = 0; 2272 i = net.UserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT, 2273 (BYTE**)&loc, MAX_PREFERRED_LENGTH, &n, &rem); 2274 if(i == NERR_Success || i == ERROR_MORE_DATA){ 2275 for(i = 0; i < n; i++){ 2276 gu = domnametouser(srv, loc[i].lgrui0_name, u->dom); 2277 if(gu == NULL) 2278 continue; 2279 g = malloc(sizeof(Gmem)); 2280 if(g == nil) 2281 error(Enomem); 2282 g->user = gu; 2283 g->next = u->group; 2284 u->group = g; 2285 } 2286 net.ApiBufferFree(loc); 2287 } 2288 } 2289 2290 static SID* 2291 dupsid(SID *sid) 2292 { 2293 SID *nsid; 2294 int n; 2295 2296 n = GetLengthSid(sid); 2297 nsid = malloc(n); 2298 if(nsid == nil || !CopySid(n, nsid, sid)) 2299 panic("can't copy sid"); 2300 return nsid; 2301 } 2302 2303 /* 2304 * return the name of the server machine for file 2305 */ 2306 static Rune* 2307 filesrv(char *file) 2308 { 2309 int n; 2310 Rune *srv; 2311 char *p, uni[MAX_PATH], mfile[MAX_PATH]; 2312 wchar_t vol[3]; 2313 2314 strcpy(mfile, file); 2315 /* assume file is a fully qualified name - X: or \\server */ 2316 if(file[1] == ':') { 2317 vol[0] = file[0]; 2318 vol[1] = file[1]; 2319 vol[2] = 0; 2320 if(GetDriveType(vol) != DRIVE_REMOTE) 2321 return 0; 2322 n = sizeof(uni); 2323 if(WNetGetUniversalName(vol, UNIVERSAL_NAME_INFO_LEVEL, uni, &n) != NO_ERROR) 2324 return nil; 2325 runestoutf(mfile, ((UNIVERSAL_NAME_INFO*)uni)->lpUniversalName, MAX_PATH); 2326 file = mfile; 2327 } 2328 file += 2; 2329 p = strchr(file, '\\'); 2330 if(p == 0) 2331 n = strlen(file); 2332 else 2333 n = p - file; 2334 if(n >= MAX_PATH) 2335 n = MAX_PATH-1; 2336 2337 memmove(uni, file, n); 2338 uni[n] = '\0'; 2339 2340 srv = malloc((n + 1) * sizeof(Rune)); 2341 if(srv == nil) 2342 panic("filesrv: no memory"); 2343 utftorunes(srv, uni, n+1); 2344 return srv; 2345 } 2346 2347 /* 2348 * does the file system support acls? 2349 */ 2350 static int 2351 fsacls(char *file) 2352 { 2353 char *p; 2354 DWORD flags; 2355 char path[MAX_PATH]; 2356 wchar_t wpath[MAX_PATH]; 2357 2358 /* assume file is a fully qualified name - X: or \\server */ 2359 if(file[1] == ':') { 2360 path[0] = file[0]; 2361 path[1] = file[1]; 2362 path[2] = '\\'; 2363 path[3] = 0; 2364 } else { 2365 strcpy(path, file); 2366 p = strchr(path+2, '\\'); 2367 if(p == 0) 2368 return 0; 2369 p = strchr(p+1, '\\'); 2370 if(p == 0) 2371 strcat(path, "\\"); 2372 else 2373 p[1] = 0; 2374 } 2375 utftorunes(wpath, path, MAX_PATH); 2376 if(!GetVolumeInformation(wpath, NULL, 0, NULL, NULL, &flags, NULL, 0)) 2377 return 0; 2378 2379 return flags & FS_PERSISTENT_ACLS; 2380 } 2381 2382 /* 2383 * given a domain, find out the server to ask about its users. 2384 * we just ask the local machine to do the translation, 2385 * so it might fail sometimes. in those cases, we don't 2386 * trust the domain anyway, and vice versa, so it's not 2387 * clear what benifit we would gain by getting the answer "right". 2388 */ 2389 static Rune* 2390 domsrv(Rune *dom, Rune srv[MAX_PATH]) 2391 { 2392 Rune *psrv; 2393 int n, r; 2394 2395 if(dom[0] == 0) 2396 return nil; 2397 2398 r = net.GetAnyDCName(NULL, dom, (LPBYTE*)&psrv); 2399 if(r == NERR_Success) { 2400 n = runeslen(psrv); 2401 if(n >= MAX_PATH) 2402 n = MAX_PATH-1; 2403 memmove(srv, psrv, n*sizeof(Rune)); 2404 srv[n] = 0; 2405 net.ApiBufferFree(psrv); 2406 return srv; 2407 } 2408 2409 return nil; 2410 } 2411 2412 Rune* 2413 runesdup(Rune *r) 2414 { 2415 int n; 2416 Rune *s; 2417 2418 n = runeslen(r) + 1; 2419 s = malloc(n * sizeof(Rune)); 2420 if(s == nil) 2421 error(Enomem); 2422 memmove(s, r, n * sizeof(Rune)); 2423 return s; 2424 } 2425 2426 int 2427 runeslen(Rune *r) 2428 { 2429 int n; 2430 2431 n = 0; 2432 while(*r++ != 0) 2433 n++; 2434 return n; 2435 } 2436 2437 char* 2438 runestoutf(char *p, Rune *r, int nc) 2439 { 2440 char *op, *ep; 2441 int n, c; 2442 2443 op = p; 2444 ep = p + nc; 2445 while(c = *r++) { 2446 n = 1; 2447 if(c >= Runeself) 2448 n = runelen(c); 2449 if(p + n >= ep) 2450 break; 2451 if(c < Runeself) 2452 *p++ = c; 2453 else 2454 p += runetochar(p, r-1); 2455 } 2456 *p = '\0'; 2457 return op; 2458 } 2459 2460 Rune* 2461 utftorunes(Rune *r, char *p, int nc) 2462 { 2463 Rune *or, *er; 2464 2465 or = r; 2466 er = r + nc; 2467 while(*p != '\0' && r + 1 < er) 2468 p += chartorune(r++, p); 2469 *r = '\0'; 2470 return or; 2471 } 2472 2473 int 2474 runescmp(Rune *s1, Rune *s2) 2475 { 2476 Rune r1, r2; 2477 2478 for(;;) { 2479 r1 = *s1++; 2480 r2 = *s2++; 2481 if(r1 != r2) { 2482 if(r1 > r2) 2483 return 1; 2484 return -1; 2485 } 2486 if(r1 == 0) 2487 return 0; 2488 } 2489 } 2490 2491 Dev fsdevtab = { 2492 'U', 2493 "fs", 2494 2495 fsinit, 2496 fsattach, 2497 fswalk, 2498 fsstat, 2499 fsopen, 2500 fscreate, 2501 fsclose, 2502 fsread, 2503 devbread, 2504 fswrite, 2505 devbwrite, 2506 fsremove, 2507 fswstat 2508 }; 2509