1 /* 2 Unix SMB/Netbios implementation. 3 SMB client library implementation 4 Copyright (C) Andrew Tridgell 1998 5 Copyright (C) Richard Sharpe 2000, 2002 6 Copyright (C) John Terpstra 2000 7 Copyright (C) Tom Jansen (Ninja ISD) 2002 8 Copyright (C) Derrell Lipman 2003-2008 9 Copyright (C) Jeremy Allison 2007, 2008 10 11 This program is free software; you can redistribute it and/or modify 12 it under the terms of the GNU General Public License as published by 13 the Free Software Foundation; either version 3 of the License, or 14 (at your option) any later version. 15 16 This program is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License for more details. 20 21 You should have received a copy of the GNU General Public License 22 along with this program. If not, see <http://www.gnu.org/licenses/>. 23 */ 24 25 #include "includes.h" 26 #include "libsmb/libsmb.h" 27 #include "libsmbclient.h" 28 #include "libsmb_internal.h" 29 #include "../libcli/smb/smbXcli_base.h" 30 31 /* 32 * Routine to open() a file ... 33 */ 34 35 SMBCFILE * 36 SMBC_open_ctx(SMBCCTX *context, 37 const char *fname, 38 int flags, 39 mode_t mode) 40 { 41 char *server = NULL; 42 char *share = NULL; 43 char *user = NULL; 44 char *password = NULL; 45 char *workgroup = NULL; 46 char *path = NULL; 47 char *targetpath = NULL; 48 struct cli_state *targetcli = NULL; 49 SMBCSRV *srv = NULL; 50 SMBCFILE *file = NULL; 51 uint16_t fd; 52 uint16_t port = 0; 53 NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID; 54 TALLOC_CTX *frame = talloc_stackframe(); 55 56 if (!context || !context->internal->initialized) { 57 errno = EINVAL; /* Best I can think of ... */ 58 TALLOC_FREE(frame); 59 return NULL; 60 } 61 62 if (!fname) { 63 errno = EINVAL; 64 TALLOC_FREE(frame); 65 return NULL; 66 } 67 68 if (SMBC_parse_path(frame, 69 context, 70 fname, 71 &workgroup, 72 &server, 73 &port, 74 &share, 75 &path, 76 &user, 77 &password, 78 NULL)) { 79 errno = EINVAL; 80 TALLOC_FREE(frame); 81 return NULL; 82 } 83 84 if (!user || user[0] == (char)0) { 85 user = talloc_strdup(frame, smbc_getUser(context)); 86 if (!user) { 87 errno = ENOMEM; 88 TALLOC_FREE(frame); 89 return NULL; 90 } 91 } 92 93 srv = SMBC_server(frame, context, True, 94 server, port, share, &workgroup, &user, &password); 95 if (!srv) { 96 if (errno == EPERM) errno = EACCES; 97 TALLOC_FREE(frame); 98 return NULL; /* SMBC_server sets errno */ 99 } 100 101 /* Hmmm, the test for a directory is suspect here ... FIXME */ 102 103 if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') { 104 status = NT_STATUS_OBJECT_PATH_INVALID; 105 } else { 106 file = SMB_MALLOC_P(SMBCFILE); 107 if (!file) { 108 errno = ENOMEM; 109 TALLOC_FREE(frame); 110 return NULL; 111 } 112 113 ZERO_STRUCTP(file); 114 115 /*d_printf(">>>open: resolving %s\n", path);*/ 116 status = cli_resolve_path( 117 frame, "", context->internal->auth_info, 118 srv->cli, path, &targetcli, &targetpath); 119 if (!NT_STATUS_IS_OK(status)) { 120 d_printf("Could not resolve %s\n", path); 121 errno = ENOENT; 122 SAFE_FREE(file); 123 TALLOC_FREE(frame); 124 return NULL; 125 } 126 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/ 127 128 status = cli_open(targetcli, targetpath, flags, 129 context->internal->share_mode, &fd); 130 if (!NT_STATUS_IS_OK(status)) { 131 132 /* Handle the error ... */ 133 134 SAFE_FREE(file); 135 errno = SMBC_errno(context, targetcli); 136 TALLOC_FREE(frame); 137 return NULL; 138 } 139 140 /* Fill in file struct */ 141 142 file->cli_fd = fd; 143 file->fname = SMB_STRDUP(fname); 144 file->srv = srv; 145 file->offset = 0; 146 file->file = True; 147 /* 148 * targetcli is either equal to srv->cli or 149 * is a subsidiary DFS connection. Either way 150 * file->cli_fd belongs to it so we must cache 151 * it for read/write/close, not re-resolve each time. 152 * Re-resolving is both slow and incorrect. 153 */ 154 file->targetcli = targetcli; 155 156 DLIST_ADD(context->internal->files, file); 157 158 /* 159 * If the file was opened in O_APPEND mode, all write 160 * operations should be appended to the file. To do that, 161 * though, using this protocol, would require a getattrE() 162 * call for each and every write, to determine where the end 163 * of the file is. (There does not appear to be an append flag 164 * in the protocol.) Rather than add all of that overhead of 165 * retrieving the current end-of-file offset prior to each 166 * write operation, we'll assume that most append operations 167 * will continuously write, so we'll just set the offset to 168 * the end of the file now and hope that's adequate. 169 * 170 * Note to self: If this proves inadequate, and O_APPEND 171 * should, in some cases, be forced for each write, add a 172 * field in the context options structure, for 173 * "strict_append_mode" which would select between the current 174 * behavior (if FALSE) or issuing a getattrE() prior to each 175 * write and forcing the write to the end of the file (if 176 * TRUE). Adding that capability will likely require adding 177 * an "append" flag into the _SMBCFILE structure to track 178 * whether a file was opened in O_APPEND mode. -- djl 179 */ 180 if (flags & O_APPEND) { 181 if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) { 182 (void) SMBC_close_ctx(context, file); 183 errno = ENXIO; 184 TALLOC_FREE(frame); 185 return NULL; 186 } 187 } 188 189 TALLOC_FREE(frame); 190 return file; 191 } 192 193 /* Check if opendir needed ... */ 194 195 if (!NT_STATUS_IS_OK(status)) { 196 int eno = 0; 197 198 eno = SMBC_errno(context, srv->cli); 199 file = smbc_getFunctionOpendir(context)(context, fname); 200 if (!file) errno = eno; 201 TALLOC_FREE(frame); 202 return file; 203 } 204 205 errno = EINVAL; /* FIXME, correct errno ? */ 206 TALLOC_FREE(frame); 207 return NULL; 208 } 209 210 /* 211 * Routine to create a file 212 */ 213 214 SMBCFILE * 215 SMBC_creat_ctx(SMBCCTX *context, 216 const char *path, 217 mode_t mode) 218 { 219 if (!context || !context->internal->initialized) { 220 errno = EINVAL; 221 return NULL; 222 } 223 224 return SMBC_open_ctx(context, path, 225 O_WRONLY | O_CREAT | O_TRUNC, mode); 226 } 227 228 /* 229 * Routine to read() a file ... 230 */ 231 232 ssize_t 233 SMBC_read_ctx(SMBCCTX *context, 234 SMBCFILE *file, 235 void *buf, 236 size_t count) 237 { 238 size_t ret; 239 TALLOC_CTX *frame = talloc_stackframe(); 240 NTSTATUS status; 241 242 /* 243 * offset: 244 * 245 * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) -- 246 * appears to pass file->offset (which is type off_t) differently than 247 * a local variable of type off_t. Using local variable "offset" in 248 * the call to cli_read() instead of file->offset fixes a problem 249 * retrieving data at an offset greater than 4GB. 250 */ 251 off_t offset; 252 253 if (!context || !context->internal->initialized) { 254 errno = EINVAL; 255 TALLOC_FREE(frame); 256 return -1; 257 } 258 259 DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count)); 260 261 if (!file || !SMBC_dlist_contains(context->internal->files, file)) { 262 errno = EBADF; 263 TALLOC_FREE(frame); 264 return -1; 265 } 266 267 offset = file->offset; 268 269 /* Check that the buffer exists ... */ 270 271 if (buf == NULL) { 272 errno = EINVAL; 273 TALLOC_FREE(frame); 274 return -1; 275 } 276 277 status = cli_read(file->targetcli, file->cli_fd, (char *)buf, offset, 278 count, &ret); 279 if (!NT_STATUS_IS_OK(status)) { 280 errno = SMBC_errno(context, file->targetcli); 281 TALLOC_FREE(frame); 282 return -1; 283 } 284 285 file->offset += ret; 286 287 DEBUG(4, (" --> %ld\n", (unsigned long)ret)); 288 289 TALLOC_FREE(frame); 290 return ret; /* Success, ret bytes of data ... */ 291 } 292 293 off_t 294 SMBC_splice_ctx(SMBCCTX *context, 295 SMBCFILE *srcfile, 296 SMBCFILE *dstfile, 297 off_t count, 298 int (*splice_cb)(off_t n, void *priv), 299 void *priv) 300 { 301 off_t written = 0; 302 TALLOC_CTX *frame = talloc_stackframe(); 303 NTSTATUS status; 304 305 if (!context || !context->internal->initialized) { 306 errno = EINVAL; 307 TALLOC_FREE(frame); 308 return -1; 309 } 310 311 if (!srcfile || 312 !SMBC_dlist_contains(context->internal->files, srcfile)) 313 { 314 errno = EBADF; 315 TALLOC_FREE(frame); 316 return -1; 317 } 318 319 if (!dstfile || 320 !SMBC_dlist_contains(context->internal->files, dstfile)) 321 { 322 errno = EBADF; 323 TALLOC_FREE(frame); 324 return -1; 325 } 326 327 status = cli_splice(srcfile->targetcli, dstfile->targetcli, 328 srcfile->cli_fd, dstfile->cli_fd, 329 count, srcfile->offset, dstfile->offset, &written, 330 splice_cb, priv); 331 if (!NT_STATUS_IS_OK(status)) { 332 errno = SMBC_errno(context, srcfile->targetcli); 333 TALLOC_FREE(frame); 334 return -1; 335 } 336 337 srcfile->offset += written; 338 dstfile->offset += written; 339 340 TALLOC_FREE(frame); 341 return written; 342 } 343 344 /* 345 * Routine to write() a file ... 346 */ 347 348 ssize_t 349 SMBC_write_ctx(SMBCCTX *context, 350 SMBCFILE *file, 351 const void *buf, 352 size_t count) 353 { 354 off_t offset; 355 TALLOC_CTX *frame = talloc_stackframe(); 356 NTSTATUS status; 357 358 /* First check all pointers before dereferencing them */ 359 360 if (!context || !context->internal->initialized) { 361 errno = EINVAL; 362 TALLOC_FREE(frame); 363 return -1; 364 } 365 366 if (!file || !SMBC_dlist_contains(context->internal->files, file)) { 367 errno = EBADF; 368 TALLOC_FREE(frame); 369 return -1; 370 } 371 372 /* Check that the buffer exists ... */ 373 374 if (buf == NULL) { 375 errno = EINVAL; 376 TALLOC_FREE(frame); 377 return -1; 378 } 379 380 offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */ 381 382 status = cli_writeall(file->targetcli, file->cli_fd, 383 0, (const uint8_t *)buf, offset, count, NULL); 384 if (!NT_STATUS_IS_OK(status)) { 385 errno = map_errno_from_nt_status(status); 386 TALLOC_FREE(frame); 387 return -1; 388 } 389 390 file->offset += count; 391 392 TALLOC_FREE(frame); 393 return count; /* Success, 0 bytes of data ... */ 394 } 395 396 /* 397 * Routine to close() a file ... 398 */ 399 400 int 401 SMBC_close_ctx(SMBCCTX *context, 402 SMBCFILE *file) 403 { 404 TALLOC_CTX *frame = talloc_stackframe(); 405 406 if (!context || !context->internal->initialized) { 407 errno = EINVAL; 408 TALLOC_FREE(frame); 409 return -1; 410 } 411 412 if (!file || !SMBC_dlist_contains(context->internal->files, file)) { 413 errno = EBADF; 414 TALLOC_FREE(frame); 415 return -1; 416 } 417 418 /* IS a dir ... */ 419 if (!file->file) { 420 TALLOC_FREE(frame); 421 return smbc_getFunctionClosedir(context)(context, file); 422 } 423 424 if (!NT_STATUS_IS_OK(cli_close(file->targetcli, file->cli_fd))) { 425 SMBCSRV *srv; 426 DEBUG(3, ("cli_close failed on %s. purging server.\n", 427 file->fname)); 428 /* Deallocate slot and remove the server 429 * from the server cache if unused */ 430 errno = SMBC_errno(context, file->targetcli); 431 srv = file->srv; 432 DLIST_REMOVE(context->internal->files, file); 433 SAFE_FREE(file->fname); 434 SAFE_FREE(file); 435 smbc_getFunctionRemoveUnusedServer(context)(context, srv); 436 TALLOC_FREE(frame); 437 return -1; 438 } 439 440 DLIST_REMOVE(context->internal->files, file); 441 SAFE_FREE(file->fname); 442 SAFE_FREE(file); 443 TALLOC_FREE(frame); 444 return 0; 445 } 446 447 /* 448 * Get info from an SMB server on a file. Use a qpathinfo call first 449 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo 450 */ 451 bool 452 SMBC_getatr(SMBCCTX * context, 453 SMBCSRV *srv, 454 const char *path, 455 struct stat *sb) 456 { 457 char *fixedpath = NULL; 458 char *targetpath = NULL; 459 struct cli_state *targetcli = NULL; 460 uint16_t mode = 0; 461 off_t size = 0; 462 struct timespec create_time_ts = {0}; 463 struct timespec access_time_ts = {0}; 464 struct timespec write_time_ts = {0}; 465 struct timespec change_time_ts = {0}; 466 time_t write_time = 0; 467 SMB_INO_T ino = 0; 468 TALLOC_CTX *frame = talloc_stackframe(); 469 NTSTATUS status; 470 471 if (!context || !context->internal->initialized) { 472 errno = EINVAL; 473 TALLOC_FREE(frame); 474 return False; 475 } 476 477 /* path fixup for . and .. */ 478 if (strequal(path, ".") || strequal(path, "..")) { 479 fixedpath = talloc_strdup(frame, "\\"); 480 if (!fixedpath) { 481 errno = ENOMEM; 482 TALLOC_FREE(frame); 483 return False; 484 } 485 } else { 486 fixedpath = talloc_strdup(frame, path); 487 if (!fixedpath) { 488 errno = ENOMEM; 489 TALLOC_FREE(frame); 490 return False; 491 } 492 trim_string(fixedpath, NULL, "\\.."); 493 trim_string(fixedpath, NULL, "\\."); 494 } 495 DEBUG(4,("SMBC_getatr: sending qpathinfo\n")); 496 497 status = cli_resolve_path(frame, "", context->internal->auth_info, 498 srv->cli, fixedpath, 499 &targetcli, &targetpath); 500 if (!NT_STATUS_IS_OK(status)) { 501 d_printf("Couldn't resolve %s\n", path); 502 errno = ENOENT; 503 TALLOC_FREE(frame); 504 return False; 505 } 506 507 if (!srv->no_pathinfo2) { 508 status = cli_qpathinfo2(targetcli, 509 targetpath, 510 &create_time_ts, 511 &access_time_ts, 512 &write_time_ts, 513 &change_time_ts, 514 &size, 515 &mode, 516 &ino); 517 if (NT_STATUS_IS_OK(status)) { 518 goto setup_stat; 519 } 520 } 521 522 srv->no_pathinfo2 = True; 523 524 if (!srv->no_pathinfo3) { 525 status = cli_qpathinfo3(targetcli, 526 targetpath, 527 &create_time_ts, 528 &access_time_ts, 529 &write_time_ts, 530 &change_time_ts, 531 &size, 532 &mode, 533 &ino); 534 if (NT_STATUS_IS_OK(status)) { 535 goto setup_stat; 536 } 537 } 538 539 srv->no_pathinfo3 = True; 540 541 /* if this is NT then don't bother with the getatr */ 542 if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) { 543 goto all_failed; 544 } 545 546 status = cli_getatr(targetcli, targetpath, &mode, &size, &write_time); 547 if (NT_STATUS_IS_OK(status)) { 548 struct timespec w_time_ts = 549 convert_time_t_to_timespec(write_time); 550 551 access_time_ts = change_time_ts = write_time_ts = w_time_ts; 552 553 goto setup_stat; 554 } 555 556 setup_stat: 557 setup_stat(sb, 558 path, 559 size, 560 mode, 561 ino, 562 srv->dev, 563 access_time_ts, 564 change_time_ts, 565 write_time_ts); 566 567 TALLOC_FREE(frame); 568 return true; 569 570 all_failed: 571 srv->no_pathinfo2 = False; 572 srv->no_pathinfo3 = False; 573 574 errno = EPERM; 575 TALLOC_FREE(frame); 576 return False; 577 } 578 579 /* 580 * Set file info on an SMB server. Use setpathinfo call first. If that 581 * fails, use setattrE.. 582 * 583 * Access and modification time parameters are always used and must be 584 * provided. Create time, if zero, will be determined from the actual create 585 * time of the file. If non-zero, the create time will be set as well. 586 * 587 * "mode" (attributes) parameter may be set to -1 if it is not to be set. 588 */ 589 bool 590 SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, 591 time_t create_time, 592 time_t access_time, 593 time_t write_time, 594 time_t change_time, 595 uint16_t mode) 596 { 597 uint16_t fd; 598 int ret; 599 TALLOC_CTX *frame = talloc_stackframe(); 600 601 /* 602 * First, try setpathinfo (if qpathinfo succeeded), for it is the 603 * modern function for "new code" to be using, and it works given a 604 * filename rather than requiring that the file be opened to have its 605 * attributes manipulated. 606 */ 607 if (srv->no_pathinfo || 608 !NT_STATUS_IS_OK(cli_setpathinfo_basic(srv->cli, path, 609 create_time, 610 access_time, 611 write_time, 612 change_time, 613 mode))) { 614 615 /* 616 * setpathinfo is not supported; go to plan B. 617 * 618 * cli_setatr() does not work on win98, and it also doesn't 619 * support setting the access time (only the modification 620 * time), so in all cases, we open the specified file and use 621 * cli_setattrE() which should work on all OS versions, and 622 * supports both times. 623 */ 624 625 /* Don't try {q,set}pathinfo() again, with this server */ 626 srv->no_pathinfo = True; 627 628 /* Open the file */ 629 if (!NT_STATUS_IS_OK(cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd))) { 630 errno = SMBC_errno(context, srv->cli); 631 TALLOC_FREE(frame); 632 return False; 633 } 634 635 /* Set the new attributes */ 636 ret = NT_STATUS_IS_OK(cli_setattrE(srv->cli, fd, 637 change_time, 638 access_time, 639 write_time)); 640 641 /* Close the file */ 642 cli_close(srv->cli, fd); 643 644 /* 645 * Unfortunately, setattrE() doesn't have a provision for 646 * setting the access mode (attributes). We'll have to try 647 * cli_setatr() for that, and with only this parameter, it 648 * seems to work on win98. 649 */ 650 if (ret && mode != (uint16_t) -1) { 651 ret = NT_STATUS_IS_OK(cli_setatr(srv->cli, path, mode, 0)); 652 } 653 654 if (! ret) { 655 errno = SMBC_errno(context, srv->cli); 656 TALLOC_FREE(frame); 657 return False; 658 } 659 } 660 661 TALLOC_FREE(frame); 662 return True; 663 } 664 665 /* 666 * A routine to lseek() a file 667 */ 668 669 off_t 670 SMBC_lseek_ctx(SMBCCTX *context, 671 SMBCFILE *file, 672 off_t offset, 673 int whence) 674 { 675 off_t size; 676 TALLOC_CTX *frame = talloc_stackframe(); 677 678 if (!context || !context->internal->initialized) { 679 errno = EINVAL; 680 TALLOC_FREE(frame); 681 return -1; 682 } 683 684 if (!file || !SMBC_dlist_contains(context->internal->files, file)) { 685 errno = EBADF; 686 TALLOC_FREE(frame); 687 return -1; 688 } 689 690 if (!file->file) { 691 errno = EINVAL; 692 TALLOC_FREE(frame); 693 return -1; /* Can't lseek a dir ... */ 694 } 695 696 switch (whence) { 697 case SEEK_SET: 698 file->offset = offset; 699 break; 700 case SEEK_CUR: 701 file->offset += offset; 702 break; 703 case SEEK_END: 704 if (!NT_STATUS_IS_OK(cli_qfileinfo_basic( 705 file->targetcli, file->cli_fd, NULL, 706 &size, NULL, NULL, NULL, NULL, 707 NULL))) { 708 off_t b_size = size; 709 if (!NT_STATUS_IS_OK(cli_getattrE(file->targetcli, file->cli_fd, 710 NULL, &b_size, NULL, NULL, NULL))) { 711 errno = EINVAL; 712 TALLOC_FREE(frame); 713 return -1; 714 } else 715 size = b_size; 716 } 717 file->offset = size + offset; 718 break; 719 default: 720 errno = EINVAL; 721 break; 722 } 723 724 TALLOC_FREE(frame); 725 return file->offset; 726 } 727 728 729 /* 730 * Routine to truncate a file given by its file descriptor, to a specified size 731 */ 732 733 int 734 SMBC_ftruncate_ctx(SMBCCTX *context, 735 SMBCFILE *file, 736 off_t length) 737 { 738 off_t size = length; 739 TALLOC_CTX *frame = talloc_stackframe(); 740 741 if (!context || !context->internal->initialized) { 742 errno = EINVAL; 743 TALLOC_FREE(frame); 744 return -1; 745 } 746 747 if (!file || !SMBC_dlist_contains(context->internal->files, file)) { 748 errno = EBADF; 749 TALLOC_FREE(frame); 750 return -1; 751 } 752 753 if (!file->file) { 754 errno = EINVAL; 755 TALLOC_FREE(frame); 756 return -1; 757 } 758 759 if (!NT_STATUS_IS_OK(cli_ftruncate(file->targetcli, file->cli_fd, (uint64_t)size))) { 760 errno = EINVAL; 761 TALLOC_FREE(frame); 762 return -1; 763 } 764 765 TALLOC_FREE(frame); 766 return 0; 767 } 768