1 #include <u.h> 2 #include <libc.h> 3 #include <fcall.h> 4 #include <thread.h> 5 #include <libsec.h> 6 #include <9p.h> 7 #include "cifs.h" 8 9 #define max(a,b) (((a) > (b))? (a): (b)) 10 #define min(a,b) (((a) < (b))? (a): (b)) 11 12 typedef struct Aux Aux; 13 struct Aux { 14 Aux *next; 15 Aux *prev; 16 char *path; /* full path fo file */ 17 Share *sp; /* this share's info */ 18 long expire; /* expiration time of cache */ 19 long off; /* file pos of start of cache */ 20 long end; /* file pos of end of cache */ 21 char *cache; 22 int fh; /* file handle */ 23 int sh; /* search handle */ 24 long srch; /* find first's internal state */ 25 }; 26 27 extern int chatty9p; 28 29 int Dfstout = 100; /* timeout (in ms) for ping of dfs servers (assume they are local) */ 30 int Billtrog = 1; /* enable file owner/group resolution */ 31 int Attachpid; /* pid of proc that attaches (ugh !) */ 32 char *Debug = nil; /* messages */ 33 Qid Root; /* root of remote system */ 34 Share Ipc; /* Share info of IPC$ share */ 35 Session *Sess; /* current session */ 36 int Active = IDLE_TIME; /* secs until next keepalive is sent */ 37 static int Keeppid; /* process ID of keepalive thread */ 38 Share Shares[MAX_SHARES]; /* table of connected shares */ 39 int Nshares = 0; /* number of Shares connected */ 40 Aux *Auxroot = nil; /* linked list of Aux structs */ 41 char *Host = nil; /* host we are connected to */ 42 43 #define ptype(x) (((x) & 0xf)) 44 #define pindex(x) (((x) & 0xff0) >> 4) 45 46 void 47 setup(void) 48 { 49 int fd; 50 char buf[32]; 51 52 /* 53 * This is revolting but I cannot see any other way to get 54 * the pid of the server. We need this as Windows doesn't 55 * drop the TCP connection when it closes a connection. 56 * Thus we keepalive() to detect when/if we are thrown off. 57 */ 58 Attachpid = getpid(); 59 60 snprint(buf, sizeof buf, "#p/%d/args", getpid()); 61 if((fd = open(buf, OWRITE)) >= 0){ 62 fprint(fd, "%s network", Host); 63 close(fd); 64 } 65 } 66 67 int 68 filetableinfo(Fmt *f) 69 { 70 Aux *ap; 71 char *type; 72 73 if((ap = Auxroot) != nil) 74 do{ 75 type = "walked"; 76 if(ap->sh != -1) 77 type = "opendir"; 78 if(ap->fh != -1) 79 type = "openfile"; 80 fmtprint(f, "%-9s %s\n", type, ap->path); 81 ap = ap->next; 82 }while(ap != Auxroot); 83 return 0; 84 } 85 86 Qid 87 mkqid(char *s, int is_dir, long vers, int subtype, long path) 88 { 89 Qid q; 90 union { /* align digest suitably */ 91 uchar digest[SHA1dlen]; 92 uvlong uvl; 93 } u; 94 95 sha1((uchar *)s, strlen(s), u.digest, nil); 96 q.type = is_dir? QTDIR: 0; 97 q.vers = vers; 98 if(subtype){ 99 q.path = *((uvlong *)u.digest) & ~0xfffL; 100 q.path |= (path & 0xff) << 4 | (subtype & 0xf); 101 }else 102 q.path = *((uvlong *)u.digest) & ~0xfL; 103 return q; 104 } 105 106 /* 107 * used only for root dir and shares 108 */ 109 static void 110 V2D(Dir *d, Qid qid, char *name) 111 { 112 memset(d, 0, sizeof(Dir)); 113 d->type = 'C'; 114 d->dev = 1; 115 d->name = strlwr(estrdup9p(name)); 116 d->uid = estrdup9p("bill"); 117 d->muid = estrdup9p("boyd"); 118 d->gid = estrdup9p("trog"); 119 d->mode = 0755 | DMDIR; 120 d->atime = time(nil); 121 d->mtime = d->atime; 122 d->length = 0; 123 d->qid = qid; 124 } 125 126 static void 127 I2D(Dir *d, Share *sp, char *path, FInfo *fi) 128 { 129 char *name; 130 131 if((name = strrchr(fi->name, '\\')) != nil) 132 name++; 133 else 134 name = fi->name; 135 d->name = estrdup9p(name); 136 d->type = 'C'; 137 d->dev = sp->tid; 138 d->uid = estrdup9p("bill"); 139 d->gid = estrdup9p("trog"); 140 d->muid = estrdup9p("boyd"); 141 d->atime = fi->accessed; 142 d->mtime = fi->written; 143 144 if(fi->attribs & ATTR_READONLY) 145 d->mode = 0444; 146 else 147 d->mode = 0666; 148 149 d->length = fi->size; 150 d->qid = mkqid(path, fi->attribs & ATTR_DIRECTORY, fi->changed, 0, 0); 151 152 if(fi->attribs & ATTR_DIRECTORY){ 153 d->length = 0; 154 d->mode |= DMDIR|0111; 155 } 156 } 157 158 static void 159 responderrstr(Req *r) 160 { 161 char e[ERRMAX]; 162 163 *e = 0; 164 rerrstr(e, sizeof e); 165 respond(r, e); 166 } 167 168 static char * 169 newpath(char *path, char *name) 170 { 171 char *p, *q; 172 173 assert((p = strrchr(path, '/')) != nil); 174 175 if(strcmp(name, "..") == 0){ 176 if(p == path) 177 return estrdup9p("/"); 178 q = emalloc9p((p-path)+1); 179 strecpy(q, q+(p-path)+1, path); 180 return q; 181 } 182 if(strcmp(path, "/") == 0) 183 return smprint("/%s", name); 184 return smprint("%s/%s", path, name); 185 } 186 187 static int 188 dirgen(int slot, Dir *d, void *aux) 189 { 190 int numinf = numinfo(), rc, got; 191 int slots = min(Sess->mtu, MTU) / sizeof(FInfo); 192 long off; 193 char *npath; 194 Aux *a = aux; 195 FInfo *fi; 196 197 if(strcmp(a->path, "/") == 0){ 198 if(slot < numinf){ 199 dirgeninfo(slot, d); 200 return 0; 201 } else 202 slot -= numinf; 203 204 if(slot >= Nshares) 205 return -1; 206 V2D(d, mkqid(Shares[slot].name, 1, 1, Pshare, slot), 207 Shares[slot].name); 208 return 0; 209 } 210 211 off = slot * sizeof(FInfo); 212 if(off >= a->off && off < a->end && time(nil) < a->expire) 213 goto from_cache; 214 215 if(off == 0){ 216 fi = (FInfo *)a->cache; 217 npath = smprint("%s/*", mapfile(a->path)); 218 a->sh = T2findfirst(Sess, a->sp, slots, npath, &got, &a->srch, 219 (FInfo *)a->cache); 220 free(npath); 221 if(a->sh == -1) 222 return -1; 223 224 a->off = 0; 225 a->end = got * sizeof(FInfo); 226 227 if(got >= 2 && strcmp(fi[0].name, ".") == 0 && 228 strcmp(fi[1].name, "..") == 0){ 229 a->end = (got - 2) * sizeof(FInfo); 230 memmove(a->cache, a->cache + sizeof(FInfo)*2, 231 a->end - a->off); 232 } 233 } 234 235 while(off >= a->end && a->sh != -1){ 236 fi = (FInfo *)(a->cache + (a->end - a->off) - sizeof(FInfo)); 237 a->off = a->end; 238 npath = smprint("%s/%s", mapfile(a->path), fi->name); 239 rc = T2findnext(Sess, a->sp, slots, npath, 240 &got, &a->srch, (FInfo *)a->cache, a->sh); 241 free(npath); 242 if(rc == -1 || got == 0) 243 break; 244 a->end = a->off + got * sizeof(FInfo); 245 } 246 a->expire = time(nil) + CACHETIME; 247 248 if(got < slots){ 249 if(a->sh != -1) 250 CIFSfindclose2(Sess, a->sp, a->sh); 251 a->sh = -1; 252 } 253 254 if(off >= a->end) 255 return -1; 256 257 from_cache: 258 fi = (FInfo *)(a->cache + (off - a->off)); 259 npath = smprint("%s/%s", mapfile(a->path), fi->name); 260 I2D(d, a->sp, npath, fi); 261 if(Billtrog == 0) 262 upd_names(Sess, a->sp, npath, d); 263 free(npath); 264 return 0; 265 } 266 267 static void 268 fsattach(Req *r) 269 { 270 char *spec = r->ifcall.aname; 271 Aux *a; 272 static int first = 1; 273 274 if(first) 275 setup(); 276 277 if(spec && *spec){ 278 respond(r, "invalid attach specifier"); 279 return; 280 } 281 282 r->ofcall.qid = mkqid("/", 1, 1, Proot, 0); 283 r->fid->qid = r->ofcall.qid; 284 285 a = r->fid->aux = emalloc9p(sizeof(Aux)); 286 memset(a, 0, sizeof(Aux)); 287 a->path = estrdup9p("/"); 288 a->sp = nil; 289 a->fh = -1; 290 a->sh = -1; 291 292 if(Auxroot){ 293 a->prev = Auxroot; 294 a->next = Auxroot->next; 295 Auxroot->next->prev = a; 296 Auxroot->next = a; 297 } else { 298 Auxroot = a; 299 a->next = a; 300 a->prev = a; 301 } 302 respond(r, nil); 303 } 304 305 static char* 306 fsclone(Fid *ofid, Fid *fid) 307 { 308 Aux *oa = ofid->aux; 309 Aux *a = emalloc9p(sizeof(Aux)); 310 311 fid->aux = a; 312 313 memset(a, 0, sizeof(Aux)); 314 a->sh = -1; 315 a->fh = -1; 316 a->sp = oa->sp; 317 a->path = estrdup9p(oa->path); 318 319 if(Auxroot){ 320 a->prev = Auxroot; 321 a->next = Auxroot->next; 322 Auxroot->next->prev = a; 323 Auxroot->next = a; 324 } else { 325 Auxroot = a; 326 a->next = a; 327 a->prev = a; 328 } 329 return nil; 330 } 331 332 static char* 333 fswalk1(Fid *fid, char *name, Qid *qid) 334 { 335 int rc, n, i; 336 char *npath; 337 Aux *a = fid->aux; 338 FInfo fi; 339 static char e[ERRMAX]; 340 341 *e = 0; 342 npath = newpath(a->path, name); 343 if(strcmp(npath, "/") == 0) 344 *qid = mkqid("/", 1, 1, Proot, 0); 345 else if(strrchr(npath, '/') == npath){ 346 if((n = walkinfo(name)) != -1) 347 *qid = mkqid(npath, 0, 1, Pinfo, n); 348 else { 349 for(i = 0; i < Nshares; i++){ 350 n = strlen(Shares[i].name); 351 if(cistrncmp(npath+1, Shares[i].name, n) != 0 || 352 npath[n+1] != 0 && npath[n+1] != '/') 353 continue; 354 break; 355 } 356 if(i < Nshares){ 357 a->sp = Shares+i; 358 *qid = mkqid(npath, 1, 1, Pshare, i); 359 } else { 360 free(npath); 361 return "not found"; 362 } 363 } 364 } else { 365 again: 366 if(mapshare(npath, &a->sp) == -1){ 367 free(npath); 368 return "not found"; 369 } 370 371 memset(&fi, 0, sizeof fi); 372 373 if(Sess->caps & CAP_NT_SMBS) 374 rc = T2queryall(Sess, a->sp, mapfile(npath), &fi); 375 else 376 rc = T2querystandard(Sess, a->sp, mapfile(npath), &fi); 377 378 if((a->sp->options & SMB_SHARE_IS_IN_DFS) != 0 && 379 (fi.attribs & ATTR_REPARSE) != 0 && 380 redirect(Sess, a->sp, npath) != -1) 381 goto again; 382 if(rc == -1){ 383 rerrstr(e, sizeof(e)); 384 free(npath); 385 return e; 386 } 387 *qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0); 388 } 389 390 free(a->path); 391 a->path = npath; 392 fid->qid = *qid; 393 return nil; 394 } 395 396 static void 397 fsstat(Req *r) 398 { 399 int rc; 400 FInfo fi; 401 Aux *a = r->fid->aux; 402 403 if(ptype(r->fid->qid.path) == Proot) 404 V2D(&r->d, r->fid->qid, ""); 405 else if(ptype(r->fid->qid.path) == Pinfo) 406 dirgeninfo(pindex(r->fid->qid.path), &r->d); 407 else if(ptype(r->fid->qid.path) == Pshare) 408 V2D(&r->d, r->fid->qid, a->path +1); 409 else{ 410 memset(&fi, 0, sizeof fi); 411 if(Sess->caps & CAP_NT_SMBS) 412 rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi); 413 else 414 rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi); 415 if(rc == -1){ 416 responderrstr(r); 417 return; 418 } 419 I2D(&r->d, a->sp, a->path, &fi); 420 if(Billtrog == 0) 421 upd_names(Sess, a->sp, mapfile(a->path), &r->d); 422 } 423 respond(r, nil); 424 } 425 426 static int 427 smbcreateopen(Aux *a, char *path, int mode, int perm, int is_create, 428 int is_dir, FInfo *fip) 429 { 430 int rc, action, attrs, access, result; 431 432 if(is_create && is_dir){ 433 if(CIFScreatedirectory(Sess, a->sp, path) == -1) 434 return -1; 435 return 0; 436 } 437 438 if(mode & DMAPPEND) { 439 werrstr("filesystem does not support DMAPPEND"); 440 return -1; 441 } 442 443 if(is_create) 444 action = 0x12; 445 else if(mode & OTRUNC) 446 action = 0x02; 447 else 448 action = 0x01; 449 450 if(perm & 0222) 451 attrs = ATTR_NORMAL; 452 else 453 attrs = ATTR_NORMAL|ATTR_READONLY; 454 455 switch (mode & OMASK){ 456 case OREAD: 457 access = 0; 458 break; 459 case OWRITE: 460 access = 1; 461 break; 462 case ORDWR: 463 access = 2; 464 break; 465 case OEXEC: 466 access = 3; 467 break; 468 default: 469 werrstr("%d bad open mode", mode & OMASK); 470 return -1; 471 break; 472 } 473 474 if(mode & DMEXCL == 0) 475 access |= 0x10; 476 else 477 access |= 0x40; 478 479 if((a->fh = CIFS_SMB_opencreate(Sess, a->sp, path, access, attrs, 480 action, &result)) == -1) 481 return -1; 482 483 if(Sess->caps & CAP_NT_SMBS) 484 rc = T2queryall(Sess, a->sp, mapfile(a->path), fip); 485 else 486 rc = T2querystandard(Sess, a->sp, mapfile(a->path), fip); 487 if(rc == -1){ 488 fprint(2, "internal error: stat of newly open/created file failed\n"); 489 return -1; 490 } 491 492 if((mode & OEXCL) && (result & 0x8000) == 0){ 493 werrstr("%d bad open mode", mode & OMASK); 494 return -1; 495 } 496 return 0; 497 } 498 499 /* Uncle Bill, you have a lot to answer for... */ 500 static int 501 ntcreateopen(Aux *a, char *path, int mode, int perm, int is_create, 502 int is_dir, FInfo *fip) 503 { 504 int options, result, attrs, flags, access, action, share; 505 506 if(mode & DMAPPEND){ 507 werrstr("CIFSopen, DMAPPEND not supported"); 508 return -1; 509 } 510 511 if(is_create){ 512 if(mode & OEXCL) 513 action = FILE_OPEN; 514 else if(mode & OTRUNC) 515 action = FILE_CREATE; 516 else 517 action = FILE_OVERWRITE_IF; 518 } else { 519 if(mode & OTRUNC) 520 action = FILE_OVERWRITE_IF; 521 else 522 action = FILE_OPEN_IF; 523 } 524 525 flags = 0; /* FIXME: really not sure */ 526 527 if(mode & OEXCL) 528 share = FILE_NO_SHARE; 529 else 530 share = FILE_SHARE_ALL; 531 532 switch (mode & OMASK){ 533 case OREAD: 534 access = GENERIC_READ; 535 break; 536 case OWRITE: 537 access = GENERIC_WRITE; 538 break; 539 case ORDWR: 540 access = GENERIC_ALL; 541 break; 542 case OEXEC: 543 access = GENERIC_EXECUTE; 544 break; 545 default: 546 werrstr("%d bad open mode", mode & OMASK); 547 return -1; 548 break; 549 } 550 551 if(is_dir){ 552 action = FILE_CREATE; 553 options = FILE_DIRECTORY_FILE; 554 if(perm & 0222) 555 attrs = ATTR_DIRECTORY; 556 else 557 attrs = ATTR_DIRECTORY|ATTR_READONLY; 558 } else { 559 options = FILE_NON_DIRECTORY_FILE; 560 if(perm & 0222) 561 attrs = ATTR_NORMAL; 562 else 563 attrs = ATTR_NORMAL|ATTR_READONLY; 564 } 565 566 if(mode & ORCLOSE){ 567 options |= FILE_DELETE_ON_CLOSE; 568 attrs |= ATTR_DELETE_ON_CLOSE; 569 } 570 571 if((a->fh = CIFS_NT_opencreate(Sess, a->sp, path, flags, options, 572 attrs, access, share, action, &result, fip)) == -1) 573 return -1; 574 575 if((mode & OEXCL) && (result & 0x8000) == 0){ 576 werrstr("%d bad open mode", mode & OMASK); 577 return -1; 578 } 579 580 return 0; 581 } 582 583 static void 584 fscreate(Req *r) 585 { 586 int rc, is_dir; 587 char *npath; 588 Aux *a = r->fid->aux; 589 FInfo fi; 590 591 a->end = a->off = 0; 592 a->cache = emalloc9p(max(Sess->mtu, MTU)); 593 594 is_dir = (r->ifcall.perm & DMDIR) == DMDIR; 595 npath = smprint("%s/%s", a->path, r->ifcall.name); 596 597 if(Sess->caps & CAP_NT_SMBS) 598 rc = ntcreateopen(a, mapfile(npath), r->ifcall.mode, 599 r->ifcall.perm, 1, is_dir, &fi); 600 else 601 rc = smbcreateopen(a, mapfile(npath), r->ifcall.mode, 602 r->ifcall.perm, 1, is_dir, &fi); 603 if(rc == -1){ 604 free(npath); 605 responderrstr(r); 606 return; 607 } 608 609 r->fid->qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0); 610 611 r->ofcall.qid = r->fid->qid; 612 free(a->path); 613 a->path = npath; 614 615 respond(r, nil); 616 } 617 618 static void 619 fsopen(Req *r) 620 { 621 int rc; 622 FInfo fi; 623 Aux *a = r->fid->aux; 624 625 a->end = a->off = 0; 626 a->cache = emalloc9p(max(Sess->mtu, MTU)); 627 628 if(ptype(r->fid->qid.path) == Pinfo){ 629 if(makeinfo(pindex(r->fid->qid.path)) != -1) 630 respond(r, nil); 631 else 632 respond(r, "cannot generate info"); 633 return; 634 } 635 636 if(r->fid->qid.type & QTDIR){ 637 respond(r, nil); 638 return; 639 } 640 641 if(Sess->caps & CAP_NT_SMBS) 642 rc = ntcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777, 643 0, 0, &fi); 644 else 645 rc = smbcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777, 646 0, 0, &fi); 647 if(rc == -1){ 648 responderrstr(r); 649 return; 650 } 651 respond(r, nil); 652 } 653 654 static void 655 fswrite(Req *r) 656 { 657 vlong n, m, got, len = r->ifcall.count, off = r->ifcall.offset; 658 char *buf = r->ifcall.data; 659 Aux *a = r->fid->aux; 660 661 got = 0; 662 n = Sess->mtu -OVERHEAD; 663 do{ 664 if(len - got < n) 665 n = len - got; 666 m = CIFSwrite(Sess, a->sp, a->fh, off + got, buf + got, n); 667 if(m != -1) 668 got += m; 669 } while(got < len && m >= n); 670 671 r->ofcall.count = got; 672 if(m == -1) 673 responderrstr(r); 674 else 675 respond(r, nil); 676 } 677 678 static void 679 fsread(Req *r) 680 { 681 vlong n, m, got, len = r->ifcall.count, off = r->ifcall.offset; 682 char *buf = r->ofcall.data; 683 Aux *a = r->fid->aux; 684 685 if(ptype(r->fid->qid.path) == Pinfo){ 686 r->ofcall.count = readinfo(pindex(r->fid->qid.path), buf, len, 687 off); 688 respond(r, nil); 689 return; 690 } 691 692 if(r->fid->qid.type & QTDIR){ 693 dirread9p(r, dirgen, a); 694 respond(r, nil); 695 return; 696 } 697 698 got = 0; 699 n = Sess->mtu -OVERHEAD; 700 do{ 701 if(len - got < n) 702 n = len - got; 703 m = CIFSread(Sess, a->sp, a->fh, off + got, buf + got, n, len); 704 if(m != -1) 705 got += m; 706 } while(got < len && m >= n); 707 708 r->ofcall.count = got; 709 if(m == -1) 710 responderrstr(r); 711 else 712 respond(r, nil); 713 } 714 715 static void 716 fsdestroyfid(Fid *f) 717 { 718 Aux *a = f->aux; 719 720 if(ptype(f->qid.path) == Pinfo) 721 freeinfo(pindex(f->qid.path)); 722 f->omode = -1; 723 if(! a) 724 return; 725 if(a->fh != -1) 726 if(CIFSclose(Sess, a->sp, a->fh) == -1) 727 fprint(2, "%s: close failed fh=%d %r\n", argv0, a->fh); 728 if(a->sh != -1) 729 if(CIFSfindclose2(Sess, a->sp, a->sh) == -1) 730 fprint(2, "%s: findclose failed sh=%d %r\n", 731 argv0, a->sh); 732 if(a->path) 733 free(a->path); 734 if(a->cache) 735 free(a->cache); 736 737 if(a == Auxroot) 738 Auxroot = a->next; 739 a->prev->next = a->next; 740 a->next->prev = a->prev; 741 if(a->next == a->prev) 742 Auxroot = nil; 743 if(a) 744 free(a); 745 } 746 747 int 748 rdonly(Session *s, Share *sp, char *path, int rdonly) 749 { 750 int rc; 751 FInfo fi; 752 753 if(Sess->caps & CAP_NT_SMBS) 754 rc = T2queryall(s, sp, path, &fi); 755 else 756 rc = T2querystandard(s, sp, path, &fi); 757 if(rc == -1) 758 return -1; 759 760 if((rdonly && !(fi.attribs & ATTR_READONLY)) || 761 (!rdonly && (fi.attribs & ATTR_READONLY))){ 762 fi.attribs &= ~ATTR_READONLY; 763 fi.attribs |= rdonly? ATTR_READONLY: 0; 764 rc = CIFSsetinfo(s, sp, path, &fi); 765 } 766 return rc; 767 } 768 769 static void 770 fsremove(Req *r) 771 { 772 int try, rc; 773 char e[ERRMAX]; 774 Aux *ap, *a = r->fid->aux; 775 776 *e = 0; 777 if(ptype(r->fid->qid.path) == Proot || 778 ptype(r->fid->qid.path) == Pshare){ 779 respond(r, "illegal operation"); 780 return; 781 } 782 783 /* close all instences of this file/dir */ 784 if((ap = Auxroot) != nil) 785 do{ 786 if(strcmp(ap->path, a->path) == 0){ 787 if(ap->sh != -1) 788 CIFSfindclose2(Sess, ap->sp, ap->sh); 789 ap->sh = -1; 790 if(ap->fh != -1) 791 CIFSclose(Sess, ap->sp, ap->fh); 792 ap->fh = -1; 793 } 794 ap = ap->next; 795 }while(ap != Auxroot); 796 try = 0; 797 again: 798 if(r->fid->qid.type & QTDIR) 799 rc = CIFSdeletedirectory(Sess, a->sp, mapfile(a->path)); 800 else 801 rc = CIFSdeletefile(Sess, a->sp, mapfile(a->path)); 802 803 rerrstr(e, sizeof(e)); 804 if(rc == -1 && try++ == 0 && strcmp(e, "permission denied") == 0 && 805 rdonly(Sess, a->sp, mapfile(a->path), 0) == 0) 806 goto again; 807 if(rc == -1) 808 responderrstr(r); 809 else 810 respond(r, nil); 811 } 812 813 static void 814 fswstat(Req *r) 815 { 816 int fh, result, rc; 817 char *p, *from, *npath; 818 Aux *a = r->fid->aux; 819 FInfo fi, tmpfi; 820 821 if(ptype(r->fid->qid.path) == Proot || 822 ptype(r->fid->qid.path) == Pshare){ 823 respond(r, "illegal operation"); 824 return; 825 } 826 827 if((r->d.uid && r->d.uid[0]) || (r->d.gid && r->d.gid[0])){ 828 respond(r, "cannot change ownership"); 829 return; 830 } 831 832 /* 833 * get current info 834 */ 835 if(Sess->caps & CAP_NT_SMBS) 836 rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi); 837 else 838 rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi); 839 if(rc == -1){ 840 werrstr("(query) - %r"); 841 responderrstr(r); 842 return; 843 } 844 845 /* 846 * always clear the readonly attribute if set, 847 * before trying to set any other fields. 848 * wstat() fails if the file/dir is readonly 849 * and this function is so full of races - who cares about one more? 850 */ 851 rdonly(Sess, a->sp, mapfile(a->path), 0); 852 853 /* 854 * rename - one piece of joy, renaming open files 855 * is legal (sharing permitting). 856 */ 857 if(r->d.name && r->d.name[0]){ 858 if((p = strrchr(a->path, '/')) == nil){ 859 respond(r, "illegal path"); 860 return; 861 } 862 npath = emalloc9p((p-a->path)+strlen(r->d.name)+2); 863 strecpy(npath, npath+(p- a->path)+2, a->path); 864 strcat(npath, r->d.name); 865 866 from = estrdup9p(mapfile(a->path)); 867 if(CIFSrename(Sess, a->sp, from, mapfile(npath)) == -1){ 868 werrstr("(rename) - %r"); 869 responderrstr(r); 870 free(npath); 871 free(from); 872 return; 873 } 874 free(from); 875 free(a->path); 876 a->path = npath; 877 } 878 879 /* 880 * set the files length, do this before setting 881 * the file times as open() will alter them 882 */ 883 if(~r->d.length){ 884 fi.size = r->d.length; 885 886 if(Sess->caps & CAP_NT_SMBS){ 887 if((fh = CIFS_NT_opencreate(Sess, a->sp, mapfile(a->path), 888 0, FILE_NON_DIRECTORY_FILE, 889 ATTR_NORMAL, GENERIC_WRITE, FILE_SHARE_ALL, 890 FILE_OPEN_IF, &result, &tmpfi)) == -1){ 891 werrstr("(set length, open) - %r"); 892 responderrstr(r); 893 return; 894 } 895 rc = T2setfilelength(Sess, a->sp, fh, &fi); 896 CIFSclose(Sess, a->sp, fh); 897 if(rc == -1){ 898 werrstr("(set length), set) - %r"); 899 responderrstr(r); 900 return; 901 } 902 } else { 903 if((fh = CIFS_SMB_opencreate(Sess, a->sp, mapfile(a->path), 904 1, ATTR_NORMAL, 1, &result)) == -1){ 905 werrstr("(set length, open) failed - %r"); 906 responderrstr(r); 907 return; 908 } 909 rc = CIFSwrite(Sess, a->sp, fh, fi.size, 0, 0); 910 CIFSclose(Sess, a->sp, fh); 911 if(rc == -1){ 912 werrstr("(set length, write) - %r"); 913 responderrstr(r); 914 return; 915 } 916 } 917 } 918 919 /* 920 * This doesn't appear to set length or 921 * attributes, no idea why, so I do those seperately 922 */ 923 if(~r->d.mtime || ~r->d.atime){ 924 if(~r->d.mtime) 925 fi.written = r->d.mtime; 926 if(~r->d.atime) 927 fi.accessed = r->d.atime; 928 if(T2setpathinfo(Sess, a->sp, mapfile(a->path), &fi) == -1){ 929 werrstr("(set path info) - %r"); 930 responderrstr(r); 931 return; 932 } 933 } 934 935 /* 936 * always update the readonly flag as 937 * we may have cleared it above. 938 */ 939 if(~r->d.mode) 940 if(r->d.mode & 0222) 941 fi.attribs &= ~ATTR_READONLY; 942 else 943 fi.attribs |= ATTR_READONLY; 944 if(rdonly(Sess, a->sp, mapfile(a->path), fi.attribs & ATTR_READONLY) == -1){ 945 werrstr("(set info) - %r"); 946 responderrstr(r); 947 return; 948 } 949 950 /* 951 * Win95 has a broken write-behind cache for metadata 952 * on open files (writes go to the cache, reads bypass 953 * the cache), so we must flush the file. 954 */ 955 if(r->fid->omode != -1 && CIFSflush(Sess, a->sp, a->fh) == -1){ 956 werrstr("(flush) %r"); 957 responderrstr(r); 958 return; 959 } 960 respond(r, nil); 961 } 962 963 static void 964 fsend(Srv *srv) 965 { 966 int i; 967 USED(srv); 968 969 for(i = 0; i < Nshares; i++) 970 CIFStreedisconnect(Sess, Shares+i); 971 CIFSlogoff(Sess); 972 postnote(PNPROC, Keeppid, "die"); 973 } 974 975 Srv fs = { 976 .destroyfid = fsdestroyfid, 977 .attach= fsattach, 978 .open= fsopen, 979 .create= fscreate, 980 .read= fsread, 981 .write= fswrite, 982 .remove= fsremove, 983 .stat= fsstat, 984 .wstat= fswstat, 985 .clone= fsclone, 986 .walk1= fswalk1, 987 .end= fsend, 988 }; 989 990 void 991 usage(void) 992 { 993 fprint(2, "usage: %s [-d name] [-Dvb] [-a auth-method] [-s srvname] " 994 "[-n called-name] [-k factotum-params] [-m mntpnt] " 995 "host [share...]\n", argv0); 996 exits("usage"); 997 } 998 999 /* 1000 * SMBecho looks like the function to use for keepalives, 1001 * sadly the echo packet does not seem to reload the 1002 * idle timer in Microsoft's servers. Instead we use 1003 * "get file system size" on each share until we get one that succeeds. 1004 */ 1005 static void 1006 keepalive(void) 1007 { 1008 int fd, i, rc = 0; 1009 uvlong tot, fre; 1010 char buf[32]; 1011 1012 snprint(buf, sizeof buf, "#p/%d/args", getpid()); 1013 if((fd = open(buf, OWRITE)) >= 0){ 1014 fprint(fd, "%s keepalive", Host); 1015 close(fd); 1016 } 1017 1018 do{ 1019 sleep(6000); 1020 if(Active-- != 0) 1021 continue; 1022 for(i = 0; i < Nshares; i++) 1023 if((rc = T2fssizeinfo(Sess, Shares+i, &tot, &fre)) != -1) 1024 break; 1025 }while(rc != -1); 1026 postnote(PNPROC, Attachpid, "die"); 1027 } 1028 1029 1030 static void 1031 ding(void *u, char *msg) 1032 { 1033 USED(u); 1034 if(strstr(msg, "alarm") != nil) 1035 noted(NCONT); 1036 noted(NDFLT); 1037 } 1038 1039 void 1040 dmpkey(char *s, void *v, int n) 1041 { 1042 int i; 1043 uchar *p = (uchar *)v; 1044 1045 print("%s", s); 1046 for(i = 0; i < n; i++) 1047 print("%02ux ", *p++); 1048 print("\n"); 1049 } 1050 1051 void 1052 main(int argc, char **argv) 1053 { 1054 int i, n; 1055 long svrtime; 1056 char windom[64], cname[64]; 1057 char *method, *sysname, *keyp, *mtpt, *svs; 1058 static char *sh[1024]; 1059 1060 *cname = 0; 1061 keyp = ""; 1062 method = nil; 1063 strcpy(windom, "unknown"); 1064 mtpt = svs = nil; 1065 1066 notify(ding); 1067 1068 ARGBEGIN{ 1069 case 'a': 1070 method = EARGF(autherr()); 1071 break; 1072 case 'b': 1073 Billtrog ^= 1; 1074 break; 1075 case 'D': 1076 chatty9p++; 1077 break; 1078 case 'd': 1079 Debug = EARGF(usage()); 1080 break; 1081 case 'k': 1082 keyp = EARGF(usage()); 1083 break; 1084 case 'm': 1085 mtpt = EARGF(usage()); 1086 break; 1087 case 'n': 1088 strncpy(cname, EARGF(usage()), sizeof(cname)); 1089 cname[sizeof(cname) -1] = 0; 1090 break; 1091 case 's': 1092 svs = EARGF(usage()); 1093 break; 1094 case 't': 1095 Dfstout = atoi(EARGF(usage())); 1096 break; 1097 default: 1098 usage(); 1099 break; 1100 }ARGEND 1101 1102 if(argc < 1) 1103 usage(); 1104 1105 Host = argv[0]; 1106 1107 if(mtpt == nil && svs == nil) 1108 mtpt = smprint("/n/%s", Host); 1109 1110 if((sysname = getenv("sysname")) == nil) 1111 sysname = "unknown"; 1112 1113 if(*cname && (Sess = cifsdial(Host, cname, sysname)) != nil) 1114 goto connected; 1115 1116 if(calledname(Host, cname) == 0 && 1117 (Sess = cifsdial(Host, cname, sysname)) != nil) 1118 goto connected; 1119 1120 strcpy(cname, Host); 1121 if((Sess = cifsdial(Host, Host, sysname)) != nil || 1122 (Sess = cifsdial(Host, "*SMBSERVER", sysname)) != nil) 1123 goto connected; 1124 sysfatal("%s - cannot dial, %r\n", Host); 1125 connected: 1126 if(CIFSnegotiate(Sess, &svrtime, windom, sizeof windom, cname, sizeof cname) == -1) 1127 sysfatal("%s - cannot negioate common protocol, %r\n", Host); 1128 1129 #ifndef DEBUG_MAC 1130 Sess->secmode &= ~SECMODE_SIGN_ENABLED; 1131 #endif 1132 1133 Sess->auth = getauth(method, windom, keyp, Sess->secmode, Sess->chal, 1134 Sess->challen); 1135 1136 if(CIFSsession(Sess) < 0) 1137 sysfatal("session authentication failed, %r\n"); 1138 1139 Sess->slip = svrtime - time(nil); 1140 Sess->cname = strlwr(estrdup9p(cname)); 1141 1142 if(CIFStreeconnect(Sess, cname, "IPC$", &Ipc) == -1) 1143 fprint(2, "IPC$, %r - can't connect\n"); 1144 1145 Nshares = 0; 1146 if(argc == 1){ 1147 Share *sip; 1148 1149 if((n = RAPshareenum(Sess, &Ipc, &sip)) < 1) 1150 sysfatal("can't enumerate shares: %r - specify share " 1151 "names on command line\n"); 1152 1153 for(i = 0; i < n; i++){ 1154 #ifdef NO_HIDDEN_SHARES 1155 int l = strlen(sip[i].name); 1156 1157 if(l > 1 && sip[i].name[l-1] == '$'){ 1158 free(sip[i].name); 1159 continue; 1160 } 1161 #endif 1162 memcpy(Shares+Nshares, sip+i, sizeof(Share)); 1163 if(CIFStreeconnect(Sess, Sess->cname, 1164 Shares[Nshares].name, Shares+Nshares) == -1){ 1165 free(Shares[Nshares].name); 1166 continue; 1167 } 1168 Nshares++; 1169 } 1170 free(sip); 1171 } else 1172 for(i = 1; i < argc; i++){ 1173 if(CIFStreeconnect(Sess, Sess->cname, argv[i], 1174 Shares+Nshares) == -1){ 1175 fprint(2, "%s: %s %q - can't connect to share" 1176 ", %r\n", argv0, Host, argv[i]); 1177 continue; 1178 } 1179 Shares[Nshares].name = strlwr(estrdup9p(argv[i])); 1180 Nshares++; 1181 } 1182 1183 if(Nshares == 0) 1184 fprint(2, "no available shares\n"); 1185 1186 if((Keeppid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFNAMEG)) == 0){ 1187 keepalive(); 1188 exits(nil); 1189 } 1190 postmountsrv(&fs, svs, mtpt, MREPL|MCREATE); 1191 exits(nil); 1192 } 1193