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