1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Test & debug program for the SMB client
  18  *
  19  * This implements a simple command reader which accepts
  20  * commands to simulate system calls into the file system.
  21  */
  22 
  23 #include <sys/types.h>
  24 #include <sys/file.h>
  25 #include <sys/stat.h>
  26 #include <sys/mount.h>
  27 #include <sys/dirent.h>
  28 #include <sys/strlog.h>           /* SL_NOTE */
  29 
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <strings.h>
  34 #include <errno.h>
  35 #include <fcntl.h>
  36 #include <inttypes.h>
  37 #include <unistd.h>
  38 
  39 #include <sys/fs/smbfs_mount.h>
  40 #include <netsmb/smb_lib.h>
  41 #include <libfknsmb/common/libfknsmb.h>
  42 #include <libfksmbfs/common/libfksmbfs.h>
  43 
  44 #if _FILE_OFFSET_BITS != 64
  45 #error "This calls (fake) VFS code which requires 64-bit off_t"
  46 #endif
  47 
  48 extern int list_shares(struct smb_ctx *);
  49 
  50 #define MAXARG  10
  51 
  52 struct cmd_tbl_ent {
  53         void (*ce_func)(int, char **);
  54         const char *ce_name;
  55         const char *ce_argdesc;
  56 };
  57 static struct cmd_tbl_ent cmd_tbl[];
  58 
  59 static struct smb_ctx *ctx = NULL;
  60 static char *server = NULL;
  61 
  62 static vfs_t *vfsp = NULL;
  63 
  64 static void show_dents(vnode_t *, offset_t *, char *, int);
  65 static void run_cli(void);
  66 
  67 #define TBUFSZ 8192
  68 static char tbuf[TBUFSZ];
  69 
  70 static void
  71 fksmbcl_usage(void)
  72 {
  73         printf("usage: fksmbcl //user@server (like smbutil)\n");
  74         exit(1);
  75 }
  76 
  77 int
  78 main(int argc, char *argv[])
  79 {
  80         int error, opt;
  81 
  82         /*
  83          * Initializations
  84          */
  85         nsmb_drv_load();
  86         nsmb_drv_init();
  87         fksmbfs_init();
  88 
  89         while ((opt = getopt(argc, argv, "dv")) != -1) {
  90                 switch (opt) {
  91                 case 'd':
  92                         smb_debug++;
  93                         break;
  94                 case 'v':
  95                         smb_verbose++;
  96                         break;
  97                 case '?':
  98                         fksmbcl_usage();
  99                         break;
 100                 }
 101         }
 102         if (optind >= argc)
 103                 fksmbcl_usage();
 104         server = argv[optind];
 105 
 106         /*
 107          * Setup the libsmbfs context
 108          */
 109         error = smb_ctx_alloc(&ctx);
 110         if (error) {
 111                 fprintf(stderr, "%s: smb_ctx_alloc failed (%d)\n",
 112                     argv[0], error);
 113                 return (1);
 114         }
 115 
 116         error = smb_ctx_scan_argv(ctx, argc, argv,
 117             SMBL_SERVER, SMBL_SERVER, USE_WILDCARD);
 118         if (error) {
 119                 fprintf(stderr, "logon: smb_ctx_scan_argv, error %d\n", error);
 120                 return (1);
 121         }
 122         error = smb_ctx_readrc(ctx);
 123         if (error) {
 124                 fprintf(stderr, "logon: smb_ctx_readrc, error %d\n", error);
 125                 return (1);
 126         }
 127 
 128         /* Do smb_ctx_setshare later, and smb_ctx_resolve. */
 129 
 130         /*
 131          * Next would be smb_ctx_get_ssn() but don't do that until
 132          * the "logon" command so one can set breakpoints etc.
 133          */
 134 
 135         /*
 136          * Run the CLI
 137          */
 138         run_cli();
 139 
 140         /*
 141          * Cleanup
 142          */
 143         fksmbfs_fini();
 144         nsmb_drv_fini();
 145 
 146         return (0);
 147 }
 148 
 149 static void
 150 run_cli()
 151 {
 152         static char cmdbuf[100];
 153         int argc, i;
 154         char *argv[MAXARG];
 155         char *cmd;
 156         char *savep = NULL;
 157         char *sep = " \t\n";
 158         char *prompt = NULL;
 159         struct cmd_tbl_ent *ce;
 160 
 161         if (isatty(0)) {
 162                 fputs("# Start with:\n"
 163                     "> logon [user [dom [pw]]]\n"
 164                     "> shares\n"
 165                     "> mount {share}\n\n",
 166                     stdout);
 167                 prompt = "> ";
 168         }
 169 
 170         for (;;) {
 171                 if (prompt) {
 172                         fputs(prompt, stdout);
 173                         fflush(stdout);
 174                 }
 175 
 176                 cmd = fgets(cmdbuf, sizeof (cmdbuf), stdin);
 177                 if (cmd == NULL)
 178                         break;
 179                 if (cmd[0] == '#')
 180                         continue;
 181 
 182                 if (prompt == NULL) {
 183                         /* Put commands in the output too. */
 184                         fprintf(stdout, "+ %s", cmdbuf);
 185                 }
 186 
 187                 argv[0] = strtok_r(cmd, sep, &savep);
 188                 if (argv[0] == NULL)
 189                         continue;
 190                 for (argc = 1; argc < MAXARG; argc++) {
 191                         argv[argc] = strtok_r(NULL, sep, &savep);
 192                         if (argv[argc] == NULL)
 193                                 break;
 194                 }
 195                 for (i = argc; i < MAXARG; i++)
 196                         argv[i++] = NULL;
 197 
 198                 for (ce = cmd_tbl; ce->ce_name != NULL; ce++)
 199                         if (strcmp(ce->ce_name, argv[0]) == 0)
 200                                 break;
 201                 if (ce->ce_name != NULL) {
 202                         ce->ce_func(argc, argv);
 203                 } else {
 204                         fprintf(stderr, "%s unknown command. Try help\n",
 205                             argv[0]);
 206                 }
 207         }
 208 }
 209 
 210 /*
 211  * Command handlers
 212  */
 213 
 214 static void
 215 do_exit(int argc, char **argv)
 216 {
 217         exit(0);
 218 }
 219 
 220 static void
 221 do_help(int argc, char **argv)
 222 {
 223         struct cmd_tbl_ent *ce;
 224 
 225         printf("Commands:\n");
 226         for (ce = cmd_tbl; ce->ce_func != NULL; ce++)
 227                 printf("%s %s\n", ce->ce_name, ce->ce_argdesc);
 228 }
 229 
 230 static void
 231 do_logon(int argc, char **argv)
 232 {
 233         int error;
 234 
 235         if (argc > 1) {
 236                 if (argv[1][0] == '-') {
 237                         smb_ctx_setuser(ctx, "", B_TRUE);
 238                         ctx->ct_flags |= SMBCF_NOPWD;
 239                 } else {
 240                         smb_ctx_setuser(ctx, argv[1], B_TRUE);
 241                 }
 242         }
 243         if (argc > 2)
 244                 smb_ctx_setdomain(ctx, argv[2], B_TRUE);
 245         if (argc > 3)
 246                 smb_ctx_setpassword(ctx, argv[3], NULL);
 247 
 248         /*
 249          * Resolve the server address, setup derived defaults.
 250          */
 251         error = smb_ctx_resolve(ctx);
 252         if (error) {
 253                 fprintf(stderr, "logon: smb_ctx_resolve, error %d\n", error);
 254                 return;
 255         }
 256 
 257         /*
 258          * Have server, share, etc. now.
 259          * Get the logon session.
 260          */
 261 again:
 262         error = smb_ctx_get_ssn(ctx);
 263         if (error == EAUTH) {
 264                 int err2;
 265                 err2 = smb_get_authentication(ctx);
 266                 if (err2 == 0)
 267                         goto again;
 268         }
 269         if (error) {
 270                 fprintf(stderr, "//%s: login failed, error %d\n",
 271                     ctx->ct_fullserver, error);
 272         }
 273 }
 274 
 275 /*
 276  * Drop session created by the "logon" command.
 277  */
 278 static void
 279 do_logoff(int argc, char **argv)
 280 {
 281 
 282         (void) nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_SSN_RELE, NULL);
 283         if (argc > 1) {
 284                 smb_ctx_done(ctx);
 285                 (void) smb_ctx_init(ctx);
 286         }
 287 }
 288 
 289 /*
 290  * List shares
 291  */
 292 static void
 293 do_shares(int argc, char **argv)
 294 {
 295         int error;
 296 
 297         smb_ctx_setshare(ctx, "IPC$", USE_IPC);
 298         error = smb_ctx_get_tree(ctx);
 299         if (error) {
 300                 fprintf(stderr, "shares, tcon IPC$, error=%d\n", error);
 301                 return;
 302         }
 303 
 304         error = list_shares(ctx);
 305         if (error) {
 306                 fprintf(stderr, "shares, enum, error=%d\n", error);
 307         }
 308 
 309         (void) nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_TREE_RELE, NULL);
 310 }
 311 
 312 char mnt_opt_buf[MAX_MNTOPT_STR];
 313 char mnt_resource[MAXPATHLEN];
 314 
 315 /*
 316  * Minimal excerpt from vfs.c:domount()
 317  */
 318 void
 319 do_mount(int argc, char **argv)
 320 {
 321         struct smbfs_args mdata;
 322         struct mounta ma;
 323         char *shrname;
 324         int error;
 325 
 326         if (vfsp != NULL) {
 327                 fprintf(stderr, "Already mounted\n");
 328                 return;
 329         }
 330 
 331         if (argc < 2) {
 332                 fprintf(stderr, "%s: missing share name\n", argv[0]);
 333                 return;
 334         }
 335         shrname = argv[1];
 336         if (argc > 2)
 337                 strlcpy(mnt_opt_buf, argv[2], sizeof (mnt_opt_buf));
 338         else
 339                 memset(mnt_opt_buf, 0, sizeof (mnt_opt_buf));
 340 
 341         smb_ctx_setshare(ctx, shrname, USE_DISKDEV);
 342         error = smb_ctx_get_tree(ctx);
 343         if (error) {
 344                 fprintf(stderr, "//%s/%s: tree connect failed, %d\n",
 345                     server, shrname, error);
 346                 return;
 347         }
 348 
 349         (void) snprintf(mnt_resource, sizeof (mnt_resource),
 350             "//%s/%s", ctx->ct_fullserver, shrname);
 351 
 352         bzero(&mdata, sizeof (mdata));
 353         mdata.version = SMBFS_VERSION;          /* smbfs mount version */
 354         mdata.file_mode = S_IRWXU;
 355         mdata.dir_mode = S_IRWXU;
 356         mdata.devfd = ctx->ct_dev_fd;
 357 
 358         /* Build mount args */
 359         bzero(&ma, sizeof (ma));
 360         ma.spec = mnt_resource;
 361         ma.dir = "/";
 362         ma.flags =  MS_DATA | MS_OPTIONSTR | MS_NOSPLICE | MS_NOSUID;
 363         ma.fstype = "smbfs";
 364         ma.dataptr = (void *) &mdata;
 365         ma.datalen = sizeof (mdata);
 366         ma.optptr = mnt_opt_buf;
 367         ma.optlen = sizeof (mnt_opt_buf);
 368 
 369         error = fake_domount("smbfs", &ma, &vfsp);
 370         if (error != 0) {
 371                 fprintf(stderr, "domount error=%d\n", error);
 372         }
 373 
 374         /* Mount takes a ref, so always rele here. */
 375         (void) nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_TREE_RELE, NULL);
 376 }
 377 
 378 void
 379 do_unmount(int argc, char **argv)
 380 {
 381         int error;
 382 
 383         if (vfsp == NULL) {
 384                 fprintf(stderr, "Not mounted\n");
 385                 return;
 386         }
 387 
 388         error = fake_dounmount(vfsp, 0);
 389         if (error != 0) {
 390                 fprintf(stderr, "dounmount error=%d\n", error);
 391                 return;
 392         }
 393         vfsp = NULL;
 394 }
 395 
 396 void
 397 do_statfs(int argc, char **argv)
 398 {
 399         statvfs64_t st;
 400         int error;
 401 
 402         if (vfsp == NULL) {
 403                 fprintf(stderr, "Not mounted\n");
 404                 return;
 405         }
 406 
 407         error = fsop_statfs(vfsp, &st);
 408         if (error != 0) {
 409                 fprintf(stderr, "df error=%d\n", error);
 410                 return;
 411         }
 412         printf("bsize=%ld\n", st.f_bsize);
 413         printf("frsize=%ld\n", st.f_frsize);
 414         printf("blocks=%" PRIu64 "\n", st.f_blocks);
 415         printf(" bfree=%" PRIu64 "\n", st.f_bfree);
 416         printf("bavail=%" PRIu64 "\n", st.f_bavail);
 417 }
 418 
 419 void
 420 do_dir(int argc, char **argv)
 421 {
 422         char *rdir;
 423         vnode_t *vp = NULL;
 424         offset_t off;
 425         int cnt;
 426         int error;
 427 
 428         if (vfsp == NULL) {
 429                 fprintf(stderr, "mnt required first\n");
 430                 return;
 431         }
 432         if (argc > 1)
 433                 rdir = argv[1];
 434         else
 435                 rdir = "";
 436 
 437         error = vn_open(rdir, 0, FREAD, 0, &vp, 0, 0);
 438         if (error != 0) {
 439                 fprintf(stderr, "do_dir, vn_open error=%d\n", error);
 440                 return;
 441         }
 442 
 443         off = 0;
 444         do {
 445                 cnt = fake_getdents(vp, &off, tbuf, TBUFSZ);
 446                 if (cnt < 0) {
 447                         fprintf(stderr, "do_dir, getdents %d\n", -cnt);
 448                         break;
 449                 }
 450                 show_dents(vp, &off, tbuf, cnt);
 451         } while (cnt > 0);
 452 
 453         if (vp != NULL)
 454                 vn_close_rele(vp, 0);
 455 }
 456 
 457 void
 458 do_dirx(int argc, char **argv)
 459 {
 460         char *rdir;
 461         vnode_t *vp = NULL;
 462         offset_t off;
 463         int cnt;
 464         int error;
 465 
 466         if (vfsp == NULL) {
 467                 fprintf(stderr, "mnt required first\n");
 468                 return;
 469         }
 470         if (argc > 1)
 471                 rdir = argv[1];
 472         else
 473                 rdir = "";
 474 
 475         error = vn_open(rdir, 0, FREAD|FXATTRDIROPEN, 0, &vp, 0, 0);
 476         if (error != 0) {
 477                 fprintf(stderr, "do_dirx, vn_open error=%d\n", error);
 478                 return;
 479         }
 480 
 481         off = 0;
 482         do {
 483                 cnt = fake_getdents(vp, &off, tbuf, TBUFSZ);
 484                 if (cnt < 0) {
 485                         fprintf(stderr, "do_dirx, getdents %d\n", -cnt);
 486                         break;
 487                 }
 488                 show_dents(vp, &off, tbuf, cnt);
 489         } while (cnt > 0);
 490 
 491         if (vp != NULL)
 492                 vn_close_rele(vp, 0);
 493 }
 494 
 495 static void
 496 show_dents(vnode_t *dvp, offset_t *offp, char *buf, int cnt)
 497 {
 498         char time_buf[40];
 499         struct stat64 st;
 500         vnode_t *vp;
 501         char *p;
 502         dirent_t *d;
 503         offset_t offset = (offset_t)-1L;
 504         int error;
 505         uint_t mode;
 506         char type;
 507 
 508         p = buf;
 509         while (p < (buf + cnt)) {
 510                 /* LINTED E_BAD_PTR_CAST_ALIGN */
 511                 d = (dirent_t *)p;
 512                 p += d->d_reclen;
 513                 offset = d->d_off;
 514 
 515                 error = fake_lookup(dvp, d->d_name, &vp);
 516                 if (error != 0) {
 517                         fprintf(stderr, "%s: lookup error=%d\n",
 518                             d->d_name, error);
 519                         continue;
 520                 }
 521                 error = fake_stat(vp, &st, 0);
 522                 vn_rele(vp);
 523                 if (error != 0) {
 524                         fprintf(stderr, "%s: stat error=%d\n",
 525                             d->d_name, error);
 526                         continue;
 527                 }
 528 
 529                 /*
 530                  * Print type, mode, size, name
 531                  * First mode (only dir, file expected here)
 532                  */
 533                 if (S_ISDIR(st.st_mode)) {
 534                         type = 'd';
 535                 } else if (S_ISREG(st.st_mode)) {
 536                         type = ' ';
 537                 } else {
 538                         type = '?';
 539                 }
 540                 mode = st.st_mode & 0777;
 541                 (void) strftime(time_buf, sizeof (time_buf),
 542                     "%b %e %T %Y", localtime(&st.st_mtime));
 543 
 544                 printf("%c 0%3o %9" PRIu64 "  %s  %s\n",
 545                     type, mode,
 546                     (uint64_t)st.st_size,
 547                     time_buf,
 548                     d->d_name);
 549         }
 550         *offp = offset;
 551 }
 552 
 553 /*
 554  * get rname [lname]
 555  */
 556 void
 557 do_get(int argc, char **argv)
 558 {
 559         struct stat64 st;
 560         char *rname;
 561         char *lname;
 562         vnode_t *vp = NULL;
 563         offset_t off;
 564         ssize_t cnt, x;
 565         int oflg = O_RDWR | O_CREAT;
 566         int lfd = -1;
 567         int error;
 568 
 569         if (vfsp == NULL) {
 570                 fprintf(stderr, "mnt required first\n");
 571                 return;
 572         }
 573         if (argc < 2) {
 574                 fprintf(stderr, "rname required\n");
 575                 return;
 576         }
 577         rname = argv[1];
 578         if (argc > 2) {
 579                 lname = argv[2];
 580                 /*
 581                  * When local name is specified, overwrite.
 582                  * Convenient for scripts etc.
 583                  */
 584                 oflg |= O_TRUNC;
 585         } else {
 586                 lname = rname;
 587                 /* Local file should not exist. */
 588                 oflg |= O_EXCL;
 589         }
 590 
 591         lfd = open(lname, oflg, 0644);
 592         if (lfd < 0) {
 593                 perror(lname);
 594                 return;
 595         }
 596 
 597         error = vn_open(rname, 0, FREAD, 0, &vp, 0, 0);
 598         if (error != 0) {
 599                 fprintf(stderr, "do_get, vn_open error=%d\n", error);
 600                 goto out;
 601         }
 602         error = fake_stat(vp, &st, 0);
 603         if (error != 0) {
 604                 fprintf(stderr, "do_get, stat error=%d\n", error);
 605                 goto out;
 606         }
 607 
 608         off = 0;
 609         do {
 610                 cnt = fake_pread(vp, tbuf, TBUFSZ, off);
 611                 if (cnt < 0) {
 612                         fprintf(stderr, "do_get, read %d\n", -cnt);
 613                         goto out;
 614                 }
 615                 x = write(lfd, tbuf, cnt);
 616                 if (x < 0) {
 617                         fprintf(stderr, "do_get, write %d\n", errno);
 618                         goto out;
 619                 }
 620                 off += x;
 621         } while (off < st.st_size);
 622 
 623 out:
 624         if (vp != NULL)
 625                 vn_close_rele(vp, 0);
 626         if (lfd != -1)
 627                 close(lfd);
 628 }
 629 
 630 /*
 631  * put lname [rname]
 632  */
 633 void
 634 do_put(int argc, char **argv)
 635 {
 636         struct stat64 rst;
 637         struct stat st;
 638         char *rname;
 639         char *lname;
 640         vnode_t *vp = NULL;
 641         offset_t off;
 642         ssize_t cnt, x;
 643         int oflg = FREAD|FWRITE|FCREAT;
 644         int lfd = -1;
 645         int error;
 646 
 647         if (vfsp == NULL) {
 648                 fprintf(stderr, "mnt required first\n");
 649                 return;
 650         }
 651         if (argc < 2) {
 652                 fprintf(stderr, "lname required\n");
 653                 return;
 654         }
 655         lname = argv[1];
 656         if (argc > 2) {
 657                 rname = argv[2];
 658                 /*
 659                  * When remote name is specified, overwrite.
 660                  * Convenient for scripts etc.
 661                  */
 662                 oflg |= FTRUNC;
 663         } else {
 664                 rname = lname;
 665                 /* Remote file should not exist. */
 666                 oflg |= FEXCL;
 667         }
 668 
 669         lfd = open(lname, O_RDONLY, 0);
 670         if (lfd < 0) {
 671                 perror(lname);
 672                 return;
 673         }
 674         error = fstat(lfd, &st);
 675         if (error != 0) {
 676                 fprintf(stderr, "do_put, stat error=%d\n", error);
 677                 goto out;
 678         }
 679 
 680         error = vn_open(rname, 0, oflg, 0, &vp, 0, 0);
 681         if (error != 0) {
 682                 fprintf(stderr, "do_put, vn_open error=%d\n", error);
 683                 goto out;
 684         }
 685 
 686         off = 0;
 687         do {
 688                 cnt = pread(lfd, tbuf, TBUFSZ, off);
 689                 if (cnt < 0) {
 690                         fprintf(stderr, "do_put, read %d\n", errno);
 691                         goto out;
 692                 }
 693                 x = fake_pwrite(vp, tbuf, cnt, off);
 694                 if (x < 0) {
 695                         fprintf(stderr, "do_put, write %d\n", -x);
 696                         goto out;
 697                 }
 698                 off += cnt;
 699         } while (off < st.st_size);
 700 
 701         /* This getattr should go OtW. */
 702         error = fake_stat(vp, &rst, 0);
 703         if (error != 0) {
 704                 fprintf(stderr, "do_put, stat error=%d\n", error);
 705                 goto out;
 706         }
 707         if (rst.st_size != st.st_size) {
 708                 fprintf(stderr, "do_put, wrong size?\n");
 709         }
 710 
 711 out:
 712         if (vp != NULL)
 713                 vn_close_rele(vp, 0);
 714         if (lfd != -1)
 715                 close(lfd);
 716 }
 717 
 718 /*
 719  * rm rname
 720  */
 721 void
 722 do_rm(int argc, char **argv)
 723 {
 724         char *rname;
 725         int error;
 726 
 727         if (vfsp == NULL) {
 728                 fprintf(stderr, "mnt required first\n");
 729                 return;
 730         }
 731         if (argc < 2) {
 732                 fprintf(stderr, "rname required\n");
 733                 return;
 734         }
 735         rname = argv[1];
 736 
 737         error = fake_unlink(rname, 0);
 738         if (error != 0) {
 739                 fprintf(stderr, "do_rm, unlink error=%d\n", error);
 740         }
 741 }
 742 
 743 /*
 744  * mv fromname toname
 745  */
 746 void
 747 do_mv(int argc, char **argv)
 748 {
 749         int error;
 750 
 751         if (vfsp == NULL) {
 752                 fprintf(stderr, "mnt required first\n");
 753                 return;
 754         }
 755         if (argc < 3) {
 756                 fprintf(stderr, "from_name to_name required\n");
 757                 return;
 758         }
 759 
 760         error = fake_rename(argv[1], argv[2]);
 761         if (error != 0) {
 762                 fprintf(stderr, "do_mv, rename error=%d\n", error);
 763         }
 764 }
 765 
 766 /*
 767  * mkdir rname
 768  */
 769 void
 770 do_mkdir(int argc, char **argv)
 771 {
 772         char *rname;
 773         vnode_t *vp = NULL;
 774         int error;
 775 
 776         if (vfsp == NULL) {
 777                 fprintf(stderr, "mnt required first\n");
 778                 return;
 779         }
 780         if (argc < 2) {
 781                 fprintf(stderr, "rname required\n");
 782                 return;
 783         }
 784         rname = argv[1];
 785 
 786         error = vn_open(rname, 0, FCREAT, 0, &vp, CRMKDIR, 0);
 787         if (error != 0) {
 788                 fprintf(stderr, "do_mkdir, vn_open error=%d\n", error);
 789         }
 790 
 791         if (vp != NULL)
 792                 vn_close_rele(vp, 0);
 793 }
 794 
 795 /*
 796  * rmdir rname
 797  */
 798 void
 799 do_rmdir(int argc, char **argv)
 800 {
 801         char *rname;
 802         int error;
 803 
 804         if (vfsp == NULL) {
 805                 fprintf(stderr, "mnt required first\n");
 806                 return;
 807         }
 808         if (argc < 2) {
 809                 fprintf(stderr, "rname required\n");
 810                 return;
 811         }
 812         rname = argv[1];
 813 
 814         error = fake_unlink(rname, AT_REMOVEDIR);
 815         if (error != 0) {
 816                 fprintf(stderr, "do_rmdir, unlink error=%d\n", error);
 817         }
 818 }
 819 
 820 /*
 821  * Simple option setting
 822  *
 823  * Each arg is handled as one line in .nsmbrc [default]
 824  */
 825 void
 826 do_opt(int argc, char **argv)
 827 {
 828         static char template[20] = "/tmp/fksmbclXXXXXX";
 829         static char rcname[30];
 830         char *tdname;
 831         char *save_home;
 832         FILE *fp;
 833         int err, i;
 834 
 835         if (argc < 2) {
 836                 fprintf(stderr, "opt {key}[=value]\n");
 837                 return;
 838         }
 839 
 840         tdname = mkdtemp(template);
 841         if (tdname == NULL) {
 842                 perror("mkdtemp");
 843                 return;
 844         }
 845         (void) snprintf(rcname, sizeof (rcname),
 846             "%s/.nsmbrc", tdname);
 847 
 848         fp = fopen(rcname, "w");
 849         if (fp == NULL) {
 850                 perror(rcname);
 851                 goto out;
 852         }
 853         fprintf(fp, "[default]\n");
 854         for (i = 1; i < argc; i++)
 855                 fprintf(fp, "%s\n", argv[i]);
 856         fclose(fp);
 857 
 858         save_home = ctx->ct_home;
 859         ctx->ct_home = tdname;
 860         err = smb_ctx_readrc(ctx);
 861         ctx->ct_home = save_home;
 862 
 863         if (err != 0)
 864                 fprintf(stderr, "readrc, err=%d\n", err);
 865 
 866 out:
 867         (void) unlink(rcname);
 868         (void) rmdir(tdname);
 869 }
 870 
 871 /*
 872  * Command table
 873  */
 874 static struct cmd_tbl_ent
 875 cmd_tbl[] = {
 876         { do_help,      "help", "" },
 877         { do_exit,      "exit", "" },
 878         { do_logon,     "logon", "[user [dom [pass]]]" },
 879         { do_logoff,    "logoff", "[close-driver]" },
 880         { do_shares,    "shares", "" },
 881         { do_mount,     "mount",  "{share} [optstr]" },
 882         { do_unmount,   "umount", "" },
 883         { do_unmount,   "unmount", "" },
 884         { do_statfs,    "statfs", "" },
 885         { do_dir,       "dir",  "{rdir} [lfile]" },
 886         { do_dirx,      "dirx", "{rdir} [lfile]" },
 887         { do_get,       "get",  "{rfile} [lfile]" },
 888         { do_put,       "put",  "{lfile} [rfile]" },
 889         { do_mv,        "mv",   "{from} {to}" },
 890         { do_rm,        "rm",   "{rfile}" },
 891         { do_mkdir,     "mkdir", "{rfile}" },
 892         { do_rmdir,     "rmdir", "{rfile}" },
 893         { do_opt,       "opt",   "{option}" },
 894         { NULL, NULL, NULL }
 895 };
 896 
 897 /*
 898  * Provide a real function (one that prints something) to replace
 899  * the stub in libfakekernel.  This prints cmn_err() messages.
 900  */
 901 void
 902 fakekernel_putlog(char *msg, size_t len, int flags)
 903 {
 904 
 905         /*
 906          * [CE_CONT, CE_NOTE, CE_WARN, CE_PANIC] maps to
 907          * [SL_NOTE, SL_NOTE, SL_WARN, SL_FATAL]
 908          */
 909         if (smb_debug == 0 && (flags & SL_NOTE))
 910                 return;
 911         (void) fwrite(msg, 1, len, stdout);
 912         (void) fflush(stdout);
 913 }
 914 
 915 /*
 916  * Print nsmb debug messages via driver smb_debugmsg()
 917  */
 918 void
 919 smb_debugmsg(const char *func, char *msg)
 920 {
 921         if (smb_debug < 2)
 922                 return;
 923         printf("[kmod] %s: %s\n", func, msg);
 924 }
 925 
 926 /*
 927  * Enable libumem debugging
 928  */
 929 const char *
 930 _umem_debug_init(void)
 931 {
 932         return ("default,verbose"); /* $UMEM_DEBUG setting */
 933 }
 934 
 935 const char *
 936 _umem_logging_init(void)
 937 {
 938         return ("fail,contents"); /* $UMEM_LOGGING setting */
 939 }