1/* 2 Unix SMB/Netbios implementation. 3 Version 2.0 4 SMB wrapper functions 5 Copyright (C) Andrew Tridgell 1998 6 Copyright (C) Derrell Lipman 2003-2005 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. 20*/ 21 22#include <stdio.h> 23#include <stdlib.h> 24#include <unistd.h> 25#include <stdarg.h> 26#include <assert.h> 27#include "smbw.h" 28#include "bsd-strlfunc.h" 29 30typedef enum StartupType 31{ 32 StartupType_Fake, 33 StartupType_Real 34} StartupType; 35 36int smbw_fd_map[__FD_SETSIZE]; 37int smbw_ref_count[__FD_SETSIZE]; 38char smbw_cwd[PATH_MAX]; 39char smbw_prefix[] = SMBW_PREFIX; 40 41/* needs to be here because of dumb include files on some systems */ 42int creat_bits = O_WRONLY|O_CREAT|O_TRUNC; 43 44int smbw_initialized = 0; 45 46static int debug_level = 0; 47 48static SMBCCTX *smbw_ctx; 49 50extern int smbw_debug; 51 52 53/***************************************************** 54smbw_ref -- manipulate reference counts 55******************************************************/ 56int smbw_ref(int client_fd, Ref_Count_Type type, ...) 57{ 58 /* client id values begin at SMBC_BASE_FC. */ 59 client_fd -= SMBC_BASE_FD; 60 61 switch(type) 62 { 63 case SMBW_RCT_Increment: 64 return ++smbw_ref_count[client_fd]; 65 66 case SMBW_RCT_Decrement: 67 return --smbw_ref_count[client_fd]; 68 69 case SMBW_RCT_Get: 70 return smbw_ref_count[client_fd]; 71 72 case SMBW_RCT_Set: 73 { 74 va_list ap; 75 int ret; 76 77 va_start(ap, type); 78 ret = (smbw_ref_count[client_fd] = va_arg(ap, int)); 79 va_end(ap); 80 return ret; 81 } 82 } 83 84 /* never gets here */ 85 return -1; 86} 87 88 89/* 90 * Return a username and password given a server and share name 91 * 92 * Returns 0 upon success; 93 * non-zero otherwise, and errno is set to indicate the error. 94 */ 95static void get_envvar_auth_data(const char *srv, 96 const char *shr, 97 char *wg, int wglen, 98 char *un, int unlen, 99 char *pw, int pwlen) 100{ 101 char *u; 102 char *p; 103 char *w; 104 105 /* Fall back to environment variables */ 106 107 w = getenv("WORKGROUP"); 108 if (w == NULL) w = ""; 109 110 u = getenv("USER"); 111 if (u == NULL) u = ""; 112 113 p = getenv("PASSWORD"); 114 if (p == NULL) p = ""; 115 116 smbw_strlcpy(wg, w, wglen); 117 smbw_strlcpy(un, u, unlen); 118 smbw_strlcpy(pw, p, pwlen); 119} 120 121static smbc_get_auth_data_fn get_auth_data_fn = get_envvar_auth_data; 122 123/***************************************************** 124set the get auth data function 125******************************************************/ 126void smbw_set_auth_data_fn(smbc_get_auth_data_fn fn) 127{ 128 get_auth_data_fn = fn; 129} 130 131 132/***************************************************** 133ensure that all connections are terminated upon exit 134******************************************************/ 135static void do_shutdown(void) 136{ 137 if (smbw_ctx != NULL) { 138 smbc_free_context(smbw_ctx, 1); 139 } 140} 141 142 143/***************************************************** 144initialise structures 145*******************************************************/ 146static void do_init(StartupType startupType) 147{ 148 int i; 149 char *p; 150 151 smbw_initialized = 1; /* this must be first to avoid recursion! */ 152 153 smbw_ctx = NULL; /* don't free context until it's established */ 154 155 /* initially, no file descriptors are mapped */ 156 for (i = 0; i < __FD_SETSIZE; i++) { 157 smbw_fd_map[i] = -1; 158 smbw_ref_count[i] = 0; 159 } 160 161 /* See if we've been told to start in a particular directory */ 162 if ((p=getenv("SMBW_DIR")) != NULL) { 163 smbw_strlcpy(smbw_cwd, p, PATH_MAX); 164 165 /* we don't want the old directory to be busy */ 166 (* smbw_libc.chdir)("/"); 167 168 } else { 169 *smbw_cwd = '\0'; 170 } 171 172 if ((p=getenv("DEBUG"))) { 173 debug_level = atoi(p); 174 } 175 176 if ((smbw_ctx = smbc_new_context()) == NULL) { 177 fprintf(stderr, "Could not create a context.\n"); 178 exit(1); 179 } 180 181 smbc_setDebug(smbw_ctx, debug_level); 182 smbc_setFunctionAuthData(smbw_ctx, get_auth_data_fn); 183 smbc_setOptionBrowseMaxLmbCount(smbw_ctx, 0); 184 smbc_setOptionUrlEncodeReaddirEntries(smbw_ctx, 1); 185 smbc_setOptionOneSharePerServer(smbw_ctx, 1); 186 187 if (smbc_init_context(smbw_ctx) == NULL) { 188 fprintf(stderr, "Could not initialize context.\n"); 189 exit(1); 190 } 191 192 smbc_set_context(smbw_ctx); 193 194 /* if not real startup, exit handler has already been established */ 195 if (startupType == StartupType_Real) { 196 atexit(do_shutdown); 197 } 198} 199 200/***************************************************** 201initialise structures, real start up vs a fork() 202*******************************************************/ 203void smbw_init(void) 204{ 205 do_init(StartupType_Real); 206} 207 208 209/***************************************************** 210determine if a file descriptor is a smb one 211*******************************************************/ 212int smbw_fd(int smbw_fd) 213{ 214 SMBW_INIT(); 215 216 return (smbw_fd >= 0 && 217 smbw_fd < __FD_SETSIZE && 218 smbw_fd_map[smbw_fd] >= SMBC_BASE_FD); /* minimum smbc_ fd */ 219} 220 221 222/***************************************************** 223determine if a path is a smb one 224*******************************************************/ 225int smbw_path(const char *name) 226{ 227 int len; 228 int ret; 229 int saved_errno; 230 231 saved_errno = errno; 232 233 SMBW_INIT(); 234 235 len = strlen(smbw_prefix); 236 237 ret = ((strncmp(name, smbw_prefix, len) == 0 && 238 (name[len] == '\0' || name[len] == '/')) || 239 (*name != '/' && *smbw_cwd != '\0')); 240 241 errno = saved_errno; 242 return ret; 243} 244 245 246/***************************************************** 247remove redundent stuff from a filename 248*******************************************************/ 249void smbw_clean_fname(char *name) 250{ 251 char *p, *p2; 252 int l; 253 int modified = 1; 254 255 if (!name) return; 256 257 DEBUG(10, ("Clean [%s]...\n", name)); 258 259 while (modified) { 260 modified = 0; 261 262 if ((p=strstr(name,"/./"))) { 263 modified = 1; 264 while (*p) { 265 p[0] = p[2]; 266 p++; 267 } 268 DEBUG(10, ("\tclean 1 (/./) produced [%s]\n", name)); 269 } 270 271 if ((p=strstr(name,"//"))) { 272 modified = 1; 273 while (*p) { 274 p[0] = p[1]; 275 p++; 276 } 277 DEBUG(10, ("\tclean 2 (//) produced [%s]\n", name)); 278 } 279 280 if (strcmp(name,"/../")==0) { 281 modified = 1; 282 name[1] = 0; 283 DEBUG(10,("\tclean 3 (^/../$) produced [%s]\n", name)); 284 } 285 286 if ((p=strstr(name,"/../"))) { 287 modified = 1; 288 for (p2 = (p > name ? p-1 : p); p2 > name; p2--) { 289 if (p2[0] == '/') break; 290 } 291 if (p2 > name) p2++; 292 while (*p2) { 293 p2[0] = p[3]; 294 p2++; 295 p++; 296 } 297 DEBUG(10, ("\tclean 4 (/../) produced [%s]\n", name)); 298 } 299 300 if (strcmp(name,"/..")==0) { 301 modified = 1; 302 name[1] = 0; 303 DEBUG(10, ("\tclean 5 (^/..$) produced [%s]\n", name)); 304 } 305 306 l = strlen(name); 307 p = l>=3?(name+l-3):name; 308 if (strcmp(p,"/..")==0) { 309 modified = 1; 310 for (p2=p-1;p2>name;p2--) { 311 if (p2[0] == '/') break; 312 } 313 if (p2==name) { 314 p[0] = '/'; 315 p[1] = 0; 316 } else { 317 p2[0] = 0; 318 } 319 DEBUG(10, ("\tclean 6 (/..) produced [%s]\n", name)); 320 } 321 322 l = strlen(name); 323 p = l>=2?(name+l-2):name; 324 if (strcmp(p,"/.")==0) { 325 modified = 1; 326 if (p == name) { 327 p[1] = 0; 328 } else { 329 p[0] = 0; 330 } 331 DEBUG(10, ("\tclean 7 (/.) produced [%s]\n", name)); 332 } 333 334 if (strncmp(p=name,"./",2) == 0) { 335 modified = 1; 336 do { 337 p[0] = p[2]; 338 } while (*p++); 339 DEBUG(10, ("\tclean 8 (^./) produced [%s]\n", name)); 340 } 341 342 l = strlen(p=name); 343 if (l > 1 && p[l-1] == '/') { 344 modified = 1; 345 p[l-1] = 0; 346 DEBUG(10, ("\tclean 9 (/) produced [%s]\n", name)); 347 } 348 } 349} 350 351void smbw_fix_path(const char *src, char *dest) 352{ 353 const char *p; 354 int len = strlen(smbw_prefix); 355 356 if (*src == '/') { 357 for (p = src + len; *p == '/'; p++) 358 ; 359 snprintf(dest, PATH_MAX, "smb://%s", p); 360 } else { 361 snprintf(dest, PATH_MAX, "%s/%s", smbw_cwd, src); 362 } 363 364 smbw_clean_fname(dest + 5); 365 366 DEBUG(10, ("smbw_fix_path(%s) returning [%s]\n", src, dest)); 367} 368 369 370 371/***************************************************** 372a wrapper for open() 373*******************************************************/ 374int smbw_open(const char *fname, int flags, mode_t mode) 375{ 376 int client_fd; 377 int smbw_fd; 378 char path[PATH_MAX]; 379 380 SMBW_INIT(); 381 382 if (!fname) { 383 errno = EINVAL; 384 return -1; 385 } 386 387 smbw_fd = (smbw_libc.open)(SMBW_DUMMY, O_WRONLY, 0200); 388 if (smbw_fd == -1) { 389 errno = EMFILE; 390 return -1; 391 } 392 393 smbw_fix_path(fname, path); 394 if (flags == creat_bits) { 395 client_fd = smbc_creat(path, mode); 396 } else { 397 client_fd = smbc_open(path, flags, mode); 398 } 399 400 if (client_fd < 0) { 401 (* smbw_libc.close)(smbw_fd); 402 return -1; 403 } 404 405 smbw_fd_map[smbw_fd] = client_fd; 406 smbw_ref(client_fd, SMBW_RCT_Increment); 407 return smbw_fd; 408} 409 410 411/***************************************************** 412a wrapper for pread() 413 414there should really be an smbc_pread() to avoid the two 415lseek()s required in this kludge. 416*******************************************************/ 417ssize_t smbw_pread(int smbw_fd, void *buf, size_t count, SMBW_OFF_T ofs) 418{ 419 int client_fd; 420 ssize_t ret; 421 int saved_errno; 422 SMBW_OFF_T old_ofs; 423 424 if (count == 0) { 425 return 0; 426 } 427 428 client_fd = smbw_fd_map[smbw_fd]; 429 430 if ((old_ofs = smbc_lseek(client_fd, 0, SEEK_CUR)) < 0 || 431 smbc_lseek(client_fd, ofs, SEEK_SET) < 0) { 432 return -1; 433 } 434 435 if ((ret = smbc_read(client_fd, buf, count)) < 0) { 436 saved_errno = errno; 437 (void) smbc_lseek(client_fd, old_ofs, SEEK_SET); 438 errno = saved_errno; 439 return -1; 440 } 441 442 return ret; 443} 444 445/***************************************************** 446a wrapper for read() 447*******************************************************/ 448ssize_t smbw_read(int smbw_fd, void *buf, size_t count) 449{ 450 int client_fd; 451 452 client_fd = smbw_fd_map[smbw_fd]; 453 454 return smbc_read(client_fd, buf, count); 455} 456 457 458 459/***************************************************** 460a wrapper for write() 461*******************************************************/ 462ssize_t smbw_write(int smbw_fd, void *buf, size_t count) 463{ 464 int client_fd; 465 466 client_fd = smbw_fd_map[smbw_fd]; 467 468 return smbc_write(client_fd, buf, count); 469} 470 471/***************************************************** 472a wrapper for pwrite() 473*******************************************************/ 474ssize_t smbw_pwrite(int smbw_fd, void *buf, size_t count, SMBW_OFF_T ofs) 475{ 476 int saved_errno; 477 int client_fd; 478 ssize_t ret; 479 SMBW_OFF_T old_ofs; 480 481 if (count == 0) { 482 return 0; 483 } 484 485 client_fd = smbw_fd_map[smbw_fd]; 486 487 if ((old_ofs = smbc_lseek(client_fd, 0, SEEK_CUR)) < 0 || 488 smbc_lseek(client_fd, ofs, SEEK_SET) < 0) { 489 return -1; 490 } 491 492 if ((ret = smbc_write(client_fd, buf, count)) < 0) { 493 saved_errno = errno; 494 (void) smbc_lseek(client_fd, old_ofs, SEEK_SET); 495 errno = saved_errno; 496 return -1; 497 } 498 499 return ret; 500} 501 502/***************************************************** 503a wrapper for close() 504*******************************************************/ 505int smbw_close(int smbw_fd) 506{ 507 int client_fd; 508 509 client_fd = smbw_fd_map[smbw_fd]; 510 511 if (smbw_ref(client_fd, SMBW_RCT_Decrement) > 0) { 512 return 0; 513 } 514 515 (* smbw_libc.close)(smbw_fd); 516 smbw_fd_map[smbw_fd] = -1; 517 return smbc_close(client_fd); 518} 519 520 521/***************************************************** 522a wrapper for fcntl() 523*******************************************************/ 524int smbw_fcntl(int smbw_fd, int cmd, long arg) 525{ 526 return 0; 527} 528 529 530/***************************************************** 531a wrapper for access() 532*******************************************************/ 533int smbw_access(const char *name, int mode) 534{ 535 struct SMBW_stat st; 536 537 SMBW_INIT(); 538 539 if (smbw_stat(name, &st)) return -1; 540 541 if (((mode & R_OK) && !(st.s_mode & S_IRUSR)) || 542 ((mode & W_OK) && !(st.s_mode & S_IWUSR)) || 543 ((mode & X_OK) && !(st.s_mode & S_IXUSR))) { 544 errno = EACCES; 545 return -1; 546 } 547 548 return 0; 549} 550 551/***************************************************** 552a wrapper for readlink() - needed for correct errno setting 553*******************************************************/ 554int smbw_readlink(const char *fname, char *buf, size_t bufsize) 555{ 556 struct SMBW_stat st; 557 int ret; 558 559 SMBW_INIT(); 560 561 ret = smbw_stat(fname, &st); 562 if (ret != 0) { 563 return -1; 564 } 565 566 /* it exists - say it isn't a link */ 567 errno = EINVAL; 568 return -1; 569} 570 571 572/***************************************************** 573a wrapper for unlink() 574*******************************************************/ 575int smbw_unlink(const char *fname) 576{ 577 char path[PATH_MAX]; 578 579 SMBW_INIT(); 580 581 smbw_fix_path(fname, path); 582 return smbc_unlink(path); 583} 584 585 586/***************************************************** 587a wrapper for rename() 588*******************************************************/ 589int smbw_rename(const char *oldname, const char *newname) 590{ 591 char path_old[PATH_MAX]; 592 char path_new[PATH_MAX]; 593 594 SMBW_INIT(); 595 596 smbw_fix_path(oldname, path_old); 597 smbw_fix_path(newname, path_new); 598 return smbc_rename(path_old, path_new); 599} 600 601 602/***************************************************** 603a wrapper for utimes 604*******************************************************/ 605int smbw_utimes(const char *fname, void *buf) 606{ 607 char path[PATH_MAX]; 608 609 smbw_fix_path(fname, path); 610 return smbc_utimes(path, buf); 611} 612 613 614/***************************************************** 615a wrapper for utime 616*******************************************************/ 617int smbw_utime(const char *fname, void *buf) 618{ 619 char path[PATH_MAX]; 620 621 smbw_fix_path(fname, path); 622 return smbc_utime(path, buf); 623} 624 625/***************************************************** 626a wrapper for chown() 627*******************************************************/ 628int smbw_chown(const char *fname, uid_t owner, gid_t group) 629{ 630 /* always indiciate that this is not supported. */ 631 errno = ENOTSUP; 632 return -1; 633} 634 635/***************************************************** 636a wrapper for chmod() 637*******************************************************/ 638int smbw_chmod(const char *fname, mode_t newmode) 639{ 640 char path[PATH_MAX]; 641 642 smbw_fix_path(fname, path); 643 return smbc_chmod(path, newmode); 644} 645 646 647/***************************************************** 648a wrapper for lseek() 649*******************************************************/ 650SMBW_OFF_T smbw_lseek(int smbw_fd, 651 SMBW_OFF_T offset, 652 int whence) 653{ 654 int client_fd; 655 SMBW_OFF_T ret; 656 657 client_fd = smbw_fd_map[smbw_fd]; 658 659 ret = smbc_lseek(client_fd, offset, whence); 660 if (smbw_debug) 661 { 662 printf("smbw_lseek(%d/%d, 0x%llx) returned 0x%llx\n", 663 smbw_fd, client_fd, 664 (unsigned long long) offset, 665 (unsigned long long) ret); 666 } 667 return ret; 668} 669 670/***************************************************** 671a wrapper for dup() 672*******************************************************/ 673int smbw_dup(int smbw_fd) 674{ 675 int fd2; 676 677 fd2 = (smbw_libc.dup)(smbw_fd); 678 if (fd2 == -1) { 679 return -1; 680 } 681 682 smbw_fd_map[fd2] = smbw_fd_map[smbw_fd]; 683 smbw_ref(smbw_fd_map[smbw_fd], SMBW_RCT_Increment); 684 return fd2; 685} 686 687 688/***************************************************** 689a wrapper for dup2() 690*******************************************************/ 691int smbw_dup2(int smbw_fd, int fd2) 692{ 693 if ((* smbw_libc.dup2)(smbw_fd, fd2) != fd2) { 694 return -1; 695 } 696 697 smbw_fd_map[fd2] = smbw_fd_map[smbw_fd]; 698 smbw_ref(smbw_fd_map[smbw_fd], SMBW_RCT_Increment); 699 return fd2; 700} 701 702 703/***************************************************** 704when we fork we have to close all connections and files 705in the child 706*******************************************************/ 707int smbw_fork(void) 708{ 709 int i; 710 pid_t child_pid; 711 int p[2]; 712 char c = 0; 713 714 SMBW_INIT(); 715 716 if (pipe(p)) return (* smbw_libc.fork)(); 717 718 child_pid = (* smbw_libc.fork)(); 719 720 if (child_pid) { 721 /* block the parent for a moment until the sockets are 722 closed */ 723 (* smbw_libc.close)(p[1]); 724 (* smbw_libc.read)(p[0], &c, 1); 725 (* smbw_libc.close)(p[0]); 726 return child_pid; 727 } 728 729 (* smbw_libc.close)(p[0]); 730 731 /* close all server connections and locally-opened files */ 732 for (i = 0; i < __FD_SETSIZE; i++) { 733 if (smbw_fd_map[i] > 0 && 734 smbw_ref(smbw_fd_map[i], SMBW_RCT_Get) > 0) { 735 736 smbc_close(smbw_fd_map[i]); 737 smbw_ref(smbw_fd_map[i], SMBW_RCT_Set, 0); 738 (* smbw_libc.close)(i); 739 } 740 741 smbw_fd_map[i] = -1; 742 } 743 744 /* unblock the parent */ 745 write(p[1], &c, 1); 746 (* smbw_libc.close)(p[1]); 747 748 /* specify directory to start in, if it's simulated smb */ 749 if (*smbw_cwd != '\0') { 750 setenv("SMBW_DIR", smbw_cwd, 1); 751 } else { 752 unsetenv("SMBW_DIR"); 753 } 754 755 /* Re-initialize this library for the child */ 756 do_init(StartupType_Fake); 757 758 /* and continue in the child */ 759 return 0; 760} 761 762int smbw_setxattr(const char *fname, 763 const char *name, 764 const void *value, 765 size_t size, 766 int flags) 767{ 768 char path[PATH_MAX]; 769 770 if (strcmp(name, "system.posix_acl_access") == 0) 771 { 772 name = "system.*"; 773 } 774 775 smbw_fix_path(fname, path); 776 return smbc_setxattr(path, name, value, size, flags); 777} 778 779int smbw_lsetxattr(const char *fname, 780 const char *name, 781 const void *value, 782 size_t size, 783 int flags) 784{ 785 char path[PATH_MAX]; 786 787 if (strcmp(name, "system.posix_acl_access") == 0) 788 { 789 name = "system.*"; 790 } 791 792 smbw_fix_path(fname, path); 793 return smbc_lsetxattr(path, name, value, size, flags); 794} 795 796int smbw_fsetxattr(int smbw_fd, 797 const char *name, 798 const void *value, 799 size_t size, 800 int flags) 801{ 802 int client_fd; 803 804 if (strcmp(name, "system.posix_acl_access") == 0) 805 { 806 name = "system.*"; 807 } 808 809 client_fd = smbw_fd_map[smbw_fd]; 810 return smbc_fsetxattr(client_fd, name, value, size, flags); 811} 812 813int smbw_getxattr(const char *fname, 814 const char *name, 815 const void *value, 816 size_t size) 817{ 818 char path[PATH_MAX]; 819 820 if (strcmp(name, "system.posix_acl_access") == 0) 821 { 822 name = "system.*"; 823 } 824 825 smbw_fix_path(fname, path); 826 827 return smbc_getxattr(path, name, value, size); 828} 829 830int smbw_lgetxattr(const char *fname, 831 const char *name, 832 const void *value, 833 size_t size) 834{ 835 char path[PATH_MAX]; 836 837 if (strcmp(name, "system.posix_acl_access") == 0) 838 { 839 name = "system.*"; 840 } 841 842 smbw_fix_path(fname, path); 843 return smbc_lgetxattr(path, name, value, size); 844} 845 846int smbw_fgetxattr(int smbw_fd, 847 const char *name, 848 const void *value, 849 size_t size) 850{ 851 int client_fd; 852 853 if (strcmp(name, "system.posix_acl_access") == 0) 854 { 855 name = "system.*"; 856 } 857 858 client_fd = smbw_fd_map[smbw_fd]; 859 return smbc_fgetxattr(client_fd, name, value, size); 860} 861 862int smbw_removexattr(const char *fname, 863 const char *name) 864{ 865 char path[PATH_MAX]; 866 867 if (strcmp(name, "system.posix_acl_access") == 0) 868 { 869 name = "system.*"; 870 } 871 872 smbw_fix_path(fname, path); 873 return smbc_removexattr(path, name); 874} 875 876int smbw_lremovexattr(const char *fname, 877 const char *name) 878{ 879 char path[PATH_MAX]; 880 881 if (strcmp(name, "system.posix_acl_access") == 0) 882 { 883 name = "system.*"; 884 } 885 886 smbw_fix_path(fname, path); 887 return smbc_lremovexattr(path, name); 888} 889 890int smbw_fremovexattr(int smbw_fd, 891 const char *name) 892{ 893 int client_fd; 894 895 if (strcmp(name, "system.posix_acl_access") == 0) 896 { 897 name = "system.*"; 898 } 899 900 client_fd = smbw_fd_map[smbw_fd]; 901 return smbc_fremovexattr(client_fd, name); 902} 903 904int smbw_listxattr(const char *fname, 905 char *list, 906 size_t size) 907{ 908 char path[PATH_MAX]; 909 910 smbw_fix_path(fname, path); 911 return smbc_listxattr(path, list, size); 912} 913 914int smbw_llistxattr(const char *fname, 915 char *list, 916 size_t size) 917{ 918 char path[PATH_MAX]; 919 920 smbw_fix_path(fname, path); 921 return smbc_llistxattr(path, list, size); 922} 923 924int smbw_flistxattr(int smbw_fd, 925 char *list, 926 size_t size) 927{ 928 int client_fd; 929 930 client_fd = smbw_fd_map[smbw_fd]; 931 return smbc_flistxattr(client_fd, list, size); 932} 933