# # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # # set -o emacs # fksmbcl is always 32-bit setenv LD_LIBRARY_PATH ${ROOT}/usr/lib/smbfs:${ROOT}/usr/lib:${ROOT}/lib echo 'Do one of: attach ${PID}' echo 'or: debug ${ROOT}/usr/lib/smbfs/fksmbcl' echo 'then: run -v //localhost' # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # Copyright 2019, Joyent, Inc. # PROG= fksmbcl OBJS_LOCAL = \ fksmbcl_main.o \ fkdev.o \ fknewvc.o \ fkiod_cl.o \ shares.o OBJS= ${OBJS_LOCAL} SRCS= ${OBJS_LOCAL:.o=.c} include ../../../Makefile.cmd include ../../../Makefile.ctf # Force SOURCEDEBUG CSOURCEDEBUGFLAGS = -g CCSOURCEDEBUGFLAGS = -g STRIP_STABS = : # Note: need our sys includes _before_ ENVCPPFLAGS, proto etc. # Also, like Makefile.uts, reset CPPFLAGS # CPPFLAGS.first += -I$(SRC)/lib/libfakekernel/common CPPFLAGS.first += -I$(SRC)/lib/smbclnt/libfknsmb/common CPPFLAGS= $(CPPFLAGS.first) INCS += -I$(SRC)/uts/common/fs/smbclnt INCS += -I$(SRC)/uts/common INCS += -I$(SRC)/common/smbclnt INCS += -I$(SRC)/common # Allow cpp to find libfknsmb.h etc. via # include INCS += -I$(SRC)/lib/smbclnt INCS += -I$(SRC)/lib/libsmbfs INCS += -I$(SRC)/lib/libsmbfs/netsmb CPPFLAGS += $(INCS) CPPFLAGS += -D_REENTRANT CPPFLAGS += -D_FILE_OFFSET_BITS=64 CPPFLAGS += -D_LARGEFILE64_SOURCE=1 CPPFLAGS += -DFKSMBCL # Always want DEBUG here CPPFLAGS += -DDEBUG # needs work SMOFF += all_func_returns CSTD= $(CSTD_GNU99) CFLAGS += $(CCVERBOSE) CFLAGS64 += $(CCVERBOSE) LDFLAGS += $(ZNOLAZYLOAD) LDFLAGS += -R/usr/lib/smbfs # Hammerhead: GNU ld needs rpath-link to resolve mech_krb5.so.1 (transitive dep of libsmbfs) LDFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/gss LDLIBS += -L$(ROOT)/usr/lib/smbfs LDLIBS += -lsmbfs -lfksmbfs -lfknsmb LDLIBS += -lsocket ROOTSMBDDIR = $(ROOTLIB)/smbfs ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%) .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) $(POST_PROCESS) clean: -$(RM) $(OBJS) include ../../../Makefile.targ install: all $(ROOTSMBDFILE) $(ROOTSMBDDIR)/%: % $(INS.file) # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # This directory builds a program linking all of the SMB client code into a user-level process. The result is not a fully functional SMB client but is very useful for some kinds of development work. The architecture of this roughly parallels the in-kernel version, where the kernel modules are built as libraries including: libfksmbfs, libfknsmb Just as with the kernel code, there are mdb modules that know how to walk data structures in libfksmbfs, etc. For debugging, etc. it's easiest to run this as a normal user, i.e. yourself (not root) Now you can run fksmbcl from the proto area using this script: ./Run.sh -df You can also run it under dbx (see the .dbxrc file). To run it under mdb (with mdb modules build here): mdb -L $ROOT/usr/lib/mdb/proc:/usr/lib/mdb/proc ... where ... is one of: fksmbcl, core.nnn, -p $PID There are also some dtrace scripts in here, and in ../dtrace for watching either all activity or only selected areas. Run these like: dtrace -s Watch-all.d -p $PID -o output These two (from over in ../dtrace) also work with fksmbcl: dtrace -s fksmbcl.d -p `pgrep fksmbcl` -o output Here is the help output: > help Commands: help exit logon [user [dom [pass]]] logoff [close-driver] shares mount {share} [optstr] umount unmount statfs dir {rdir} [lfile] dirx {rdir} [lfile] get {rfile} [lfile] put {lfile} [rfile] mv {from} {to} rm {rfile} mkdir {rfile} rmdir {rfile} opt {option} Here is an example of how to connect, mount, and list a directory: $ ./Run.sh //myserver # Start with: > logon [user [dom [pw]]] > shares > mount {share} > logon test test test > shares open pipe: /srvsvc enum strings junk c$ Default Share test1 ipc$ Remote IPC test > mount test > dir 1224750917 . 1224750917 .. 900818955 test9.dat 3908265151 lock1.txt 2452346625 test_dir > umount > logoff > exit $ #!/bin/sh # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # # Helper program to run fksmbd (user-space smbd for debugging) # using binaries from the proto area. [ -n "$ROOT" ] || { echo "Need a bldenv to set ROOT=..." exit 1; } # OK, setup env. to run it. LD_LIBRARY_PATH=$ROOT/usr/lib/smbfs:$ROOT/usr/lib:$ROOT/lib export LD_LIBRARY_PATH # run with the passed options exec $ROOT/usr/lib/smbfs/fksmbcl "$@" #!/usr/sbin/dtrace -s /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * User-level dtrace for fksmbcl * Usage: dtrace -s Watch-fksmbcl.d -p $PID */ /* * If traced, print entry/return */ pid$target:fksmbcl::entry, pid$target:libsmbfs.so.1::entry, pid$target:libfksmbfs.so.1::entry, pid$target:libfknsmb.so.1::entry, pid$target:libfakekernel.so.1::entry { printf("\t0x%x", arg0); printf("\t0x%x", arg1); printf("\t0x%x", arg2); printf("\t0x%x", arg3); printf("\t0x%x", arg4); printf("\t0x%x", arg5); } pid$target:fksmbcl::return, pid$target:libsmbfs.so.1::return, pid$target:libfksmbfs.so.1::return, pid$target:libfknsmb.so.1::return, pid$target:libfakekernel.so.1::entry { printf("\t0x%x", arg1); } pid$target::smbfslookup:entry { printf("\tname = %s\n", copyinstr(arg1)); } pid$target:libfknsmb.so.1:smb_dtrace2:entry /copyinstr(arg0) == "debugmsg2"/ { this->f = copyinstr(arg1); this->m = copyinstr(arg2); printf("\n\t debugmsg2: %s: %s ", this->f, this->m); } pid$target:libfknsmb.so.1:smb_dtrace3:entry /copyinstr(arg0) == "debugmsg3"/ { this->f = copyinstr(arg1); this->m = copyinstr(arg2); printf("\n\t debugmsg3: %s: %s ", this->f, this->m); trace(arg3); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * This file implements device open/close/ioctl wrappers that * redirect access from the real "nsmb" device to the in-process * device simulation in libfknsmb. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smb/charsets.h" #include "smb/private.h" int smb_open_driver(void) { dev32_t dev; int fd; int rc; rc = nsmb_drv_open(&dev, 0, 0); if (rc != 0) { errno = rc; return (-1); } assert((dev & 0xFFFF0000) != 0); fd = (int)dev; return (fd); } int nsmb_ioctl(int fd, int cmd, void *arg) { dev32_t dev; int err; dev = (dev32_t)fd; assert((dev & 0xFFFF0000) != 0); err = nsmb_drv_ioctl(dev, cmd, (intptr_t)arg, 0); if (err != 0) { errno = err; return (-1); } return (0); } int nsmb_close(int fd) { dev32_t dev; dev = (dev32_t)fd; assert((dev & 0xFFFF0000) != 0); (void) nsmb_drv_close(dev, 0, 0); return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * Client-side interface to the IO Daemon (IOD) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smb/charsets.h" #include "smb/private.h" /* * Make sure we don't call the real IOD here. */ int smb_iod_open_door(int *fdp) { *fdp = -1; return (ENOTSUP); } /* * Get a door handle to the IOD... */ int smb_iod_start(smb_ctx_t *ctx) { return (0); } void * iod_work(void *arg) { smb_ctx_t *ctx = arg; (void) smb_iod_work(ctx); smb_ctx_free(ctx); return (NULL); } /* * Ask the IOD to connect using the info in ctx. * Called by newvc. * * This function largely follows smbiod.c : iod_newvc() */ int smb_iod_cl_newvc(smb_ctx_t *cl_ctx) { smb_ctx_t *ctx; thread_t tid; int err = 0; /* * Clone the context, like in smbiod.c */ err = smb_ctx_alloc(&ctx); if (err) return (err); bcopy(&cl_ctx->ct_iod_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn)); /* * Create the driver session first... */ if ((err = smb_ctx_gethandle(ctx)) != 0) goto out; if (nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) { err = errno; if (err == EEXIST) err = 0; /* see above */ goto out; } /* * Do the initial connection setup here, so we can * report the outcome to the door client. */ err = smb_iod_connect(ctx); if (err != 0) { fprintf(stderr, "smb_iod_connect, err=%d\n", err); goto out; } /* The rest happens in the iod_work thread. */ err = thr_create(NULL, 0, iod_work, ctx, THR_DETACHED, &tid); if (err == 0) { /* * Given to the new thread. * free at end of iod_work */ ctx = NULL; } out: if (ctx != NULL) smb_ctx_free(ctx); return (err); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * Create a new VC given a list of addresses. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smb/charsets.h" #include "smb/private.h" /* * Ask the IOD to create a VC with this IP address. */ static int fknewvc(struct smb_ctx *ctx, struct addrinfo *ai) { char host[256]; char svc[32]; smbioc_ossn_t *ssn = &ctx->ct_ssn; int err; if (smb_debug) { err = getnameinfo(ai->ai_addr, ai->ai_addrlen, host, sizeof (host), svc, sizeof (svc), AI_NUMERICHOST); if (err != 0) { strlcpy(host, "(?)", sizeof (host)); strlcpy(svc, "?", sizeof (host)); } printf("fknewvc: Try AF=%d %s:%s\n", ai->ai_family, host, svc); } /* * Copy the passed address into ssn_srvaddr, * but first sanity-check lengths. Also, * zero it first to avoid trailing junk. */ if (ai->ai_addrlen > sizeof (ssn->ssn_srvaddr)) return (EINVAL); bzero(&ssn->ssn_srvaddr, sizeof (ssn->ssn_srvaddr)); bcopy(ai->ai_addr, &ssn->ssn_srvaddr, ai->ai_addrlen); /* Ask the IOD to connect using the info in ctx. */ err = smb_iod_cl_newvc(ctx); if (smb_debug) { printf("fknewvc: iod_cl_newvc err=%d\n", err); } return (err); } /* * Setup a new VC via the IOD. * Similar to findvc.c */ int smb_ctx_newvc(struct smb_ctx *ctx) { struct addrinfo *ai; int err; /* Should already have the address list. */ if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) return (EINVAL); err = EPROTONOSUPPORT; /* in case no AF match */ for (ai = ctx->ct_addrinfo; ai; ai = ai->ai_next) { switch (ai->ai_family) { case AF_INET: case AF_INET6: case AF_NETBIOS: err = fknewvc(ctx, ai); if (err == 0) return (0); break; default: break; } } /* * In the error case, the caller may try again * with new auth. info, so keep the door open. * Error return will close in smb_ctx_done. */ return (err); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * Test & debug program for the SMB client * * This implements a simple command reader which accepts * commands to simulate system calls into the file system. */ #include #include #include #include #include #include /* SL_NOTE */ #include #include #include #include #include #include #include #include #include #include #include #include #if _FILE_OFFSET_BITS != 64 #error "This calls (fake) VFS code which requires 64-bit off_t" #endif extern int list_shares(struct smb_ctx *); #define MAXARG 10 struct cmd_tbl_ent { void (*ce_func)(int, char **); const char *ce_name; const char *ce_argdesc; }; static struct cmd_tbl_ent cmd_tbl[]; static struct smb_ctx *ctx = NULL; static char *server = NULL; static vfs_t *vfsp = NULL; static void show_dents(vnode_t *, offset_t *, char *, int); static void run_cli(void); #define TBUFSZ 8192 static char tbuf[TBUFSZ]; static void fksmbcl_usage(void) { printf("usage: fksmbcl //user@server (like smbutil)\n"); exit(1); } int main(int argc, char *argv[]) { int error, opt; /* * Initializations */ nsmb_drv_load(); nsmb_drv_init(); fksmbfs_init(); while ((opt = getopt(argc, argv, "dv")) != -1) { switch (opt) { case 'd': smb_debug++; break; case 'v': smb_verbose++; break; case '?': fksmbcl_usage(); break; } } if (optind >= argc) fksmbcl_usage(); server = argv[optind]; /* * Setup the libsmbfs context */ error = smb_ctx_alloc(&ctx); if (error) { fprintf(stderr, "%s: smb_ctx_alloc failed (%d)\n", argv[0], error); return (1); } error = smb_ctx_scan_argv(ctx, argc, argv, SMBL_SERVER, SMBL_SERVER, USE_WILDCARD); if (error) { fprintf(stderr, "logon: smb_ctx_scan_argv, error %d\n", error); return (1); } error = smb_ctx_readrc(ctx); if (error) { fprintf(stderr, "logon: smb_ctx_readrc, error %d\n", error); return (1); } /* Do smb_ctx_setshare later, and smb_ctx_resolve. */ /* * Next would be smb_ctx_get_ssn() but don't do that until * the "logon" command so one can set breakpoints etc. */ /* * Run the CLI */ run_cli(); /* * Cleanup */ fksmbfs_fini(); nsmb_drv_fini(); return (0); } static void run_cli() { static char cmdbuf[100]; int argc, i; char *argv[MAXARG]; char *cmd; char *savep = NULL; char *sep = " \t\n"; char *prompt = NULL; struct cmd_tbl_ent *ce; if (isatty(0)) { fputs("# Start with:\n" "> logon [user [dom [pw]]]\n" "> shares\n" "> mount {share}\n\n", stdout); prompt = "> "; } for (;;) { if (prompt) { fputs(prompt, stdout); fflush(stdout); } cmd = fgets(cmdbuf, sizeof (cmdbuf), stdin); if (cmd == NULL) break; if (cmd[0] == '#') continue; if (prompt == NULL) { /* Put commands in the output too. */ fprintf(stdout, "+ %s", cmdbuf); } argv[0] = strtok_r(cmd, sep, &savep); if (argv[0] == NULL) continue; for (argc = 1; argc < MAXARG; argc++) { argv[argc] = strtok_r(NULL, sep, &savep); if (argv[argc] == NULL) break; } for (i = argc; i < MAXARG; i++) argv[i++] = NULL; for (ce = cmd_tbl; ce->ce_name != NULL; ce++) if (strcmp(ce->ce_name, argv[0]) == 0) break; if (ce->ce_name != NULL) { ce->ce_func(argc, argv); } else { fprintf(stderr, "%s unknown command. Try help\n", argv[0]); } } } /* * Command handlers */ static void do_exit(int argc, char **argv) { exit(0); } static void do_help(int argc, char **argv) { struct cmd_tbl_ent *ce; printf("Commands:\n"); for (ce = cmd_tbl; ce->ce_func != NULL; ce++) printf("%s %s\n", ce->ce_name, ce->ce_argdesc); } static void do_logon(int argc, char **argv) { int error; if (argc > 1) { if (argv[1][0] == '-') { smb_ctx_setuser(ctx, "", B_TRUE); ctx->ct_flags |= SMBCF_NOPWD; } else { smb_ctx_setuser(ctx, argv[1], B_TRUE); } } if (argc > 2) smb_ctx_setdomain(ctx, argv[2], B_TRUE); if (argc > 3) smb_ctx_setpassword(ctx, argv[3], 0); /* * Resolve the server address, setup derived defaults. */ error = smb_ctx_resolve(ctx); if (error) { fprintf(stderr, "logon: smb_ctx_resolve, error %d\n", error); return; } /* * Have server, share, etc. now. * Get the logon session. */ again: error = smb_ctx_get_ssn(ctx); if (error == EAUTH) { int err2; err2 = smb_get_authentication(ctx); if (err2 == 0) goto again; } if (error) { fprintf(stderr, "//%s: login failed, error %d\n", ctx->ct_fullserver, error); } } /* * Drop session created by the "logon" command. */ static void do_logoff(int argc, char **argv) { (void) nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_SSN_RELE, NULL); if (argc > 1) { smb_ctx_done(ctx); (void) smb_ctx_init(ctx); } } /* * List shares */ static void do_shares(int argc, char **argv) { int error; smb_ctx_setshare(ctx, "IPC$", USE_IPC); error = smb_ctx_get_tree(ctx); if (error) { fprintf(stderr, "shares, tcon IPC$, error=%d\n", error); return; } error = list_shares(ctx); if (error) { fprintf(stderr, "shares, enum, error=%d\n", error); } (void) nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_TREE_RELE, NULL); } char mnt_opt_buf[MAX_MNTOPT_STR]; char mnt_resource[MAXPATHLEN]; /* * Minimal excerpt from vfs.c:domount() */ void do_mount(int argc, char **argv) { struct smbfs_args mdata; struct mounta ma; char *shrname; int error; if (vfsp != NULL) { fprintf(stderr, "Already mounted\n"); return; } if (argc < 2) { fprintf(stderr, "%s: missing share name\n", argv[0]); return; } shrname = argv[1]; if (argc > 2) strlcpy(mnt_opt_buf, argv[2], sizeof (mnt_opt_buf)); else memset(mnt_opt_buf, 0, sizeof (mnt_opt_buf)); smb_ctx_setshare(ctx, shrname, USE_DISKDEV); error = smb_ctx_get_tree(ctx); if (error) { fprintf(stderr, "//%s/%s: tree connect failed, %d\n", server, shrname, error); return; } (void) snprintf(mnt_resource, sizeof (mnt_resource), "//%s/%s", ctx->ct_fullserver, shrname); bzero(&mdata, sizeof (mdata)); mdata.version = SMBFS_VERSION; /* smbfs mount version */ mdata.file_mode = S_IRWXU; mdata.dir_mode = S_IRWXU; mdata.devfd = ctx->ct_dev_fd; /* Build mount args */ bzero(&ma, sizeof (ma)); ma.spec = mnt_resource; ma.dir = "/"; ma.flags = MS_DATA | MS_OPTIONSTR | MS_NOSPLICE | MS_NOSUID; ma.fstype = "smbfs"; ma.dataptr = (void *) &mdata; ma.datalen = sizeof (mdata); ma.optptr = mnt_opt_buf; ma.optlen = sizeof (mnt_opt_buf); error = fake_domount("smbfs", &ma, &vfsp); if (error != 0) { fprintf(stderr, "domount error=%d\n", error); } /* Mount takes a ref, so always rele here. */ (void) nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_TREE_RELE, NULL); } void do_unmount(int argc, char **argv) { int error; if (vfsp == NULL) { fprintf(stderr, "Not mounted\n"); return; } error = fake_dounmount(vfsp, 0); if (error != 0) { fprintf(stderr, "dounmount error=%d\n", error); return; } vfsp = NULL; } void do_statfs(int argc, char **argv) { statvfs64_t st; int error; if (vfsp == NULL) { fprintf(stderr, "Not mounted\n"); return; } error = fsop_statfs(vfsp, &st); if (error != 0) { fprintf(stderr, "df error=%d\n", error); return; } printf("bsize=%ld\n", st.f_bsize); printf("frsize=%ld\n", st.f_frsize); printf("blocks=%" PRIu64 "\n", st.f_blocks); printf(" bfree=%" PRIu64 "\n", st.f_bfree); printf("bavail=%" PRIu64 "\n", st.f_bavail); } void do_dir(int argc, char **argv) { char *rdir; vnode_t *vp = NULL; offset_t off; int cnt; int error; if (vfsp == NULL) { fprintf(stderr, "mnt required first\n"); return; } if (argc > 1) rdir = argv[1]; else rdir = ""; error = vn_open(rdir, 0, FREAD, 0, &vp, 0, 0); if (error != 0) { fprintf(stderr, "do_dir, vn_open error=%d\n", error); return; } off = 0; do { cnt = fake_getdents(vp, &off, tbuf, TBUFSZ); if (cnt < 0) { fprintf(stderr, "do_dir, getdents %d\n", -cnt); break; } show_dents(vp, &off, tbuf, cnt); } while (cnt > 0); if (vp != NULL) vn_close_rele(vp, 0); } void do_dirx(int argc, char **argv) { char *rdir; vnode_t *vp = NULL; offset_t off; int cnt; int error; if (vfsp == NULL) { fprintf(stderr, "mnt required first\n"); return; } if (argc > 1) rdir = argv[1]; else rdir = ""; error = vn_open(rdir, 0, FREAD|FXATTRDIROPEN, 0, &vp, 0, 0); if (error != 0) { fprintf(stderr, "do_dirx, vn_open error=%d\n", error); return; } off = 0; do { cnt = fake_getdents(vp, &off, tbuf, TBUFSZ); if (cnt < 0) { fprintf(stderr, "do_dirx, getdents %d\n", -cnt); break; } show_dents(vp, &off, tbuf, cnt); } while (cnt > 0); if (vp != NULL) vn_close_rele(vp, 0); } static void show_dents(vnode_t *dvp, offset_t *offp, char *buf, int cnt) { char time_buf[40]; struct stat64 st; vnode_t *vp; char *p; dirent_t *d; offset_t offset = (offset_t)-1L; int error; uint_t mode; char type; p = buf; while (p < (buf + cnt)) { d = (dirent_t *)(void *)p; p += d->d_reclen; offset = d->d_off; error = fake_lookup(dvp, d->d_name, &vp); if (error != 0) { fprintf(stderr, "%s: lookup error=%d\n", d->d_name, error); continue; } error = fake_stat(vp, &st, 0); vn_rele(vp); if (error != 0) { fprintf(stderr, "%s: stat error=%d\n", d->d_name, error); continue; } /* * Print type, mode, size, name * First mode (only dir, file expected here) */ if (S_ISDIR(st.st_mode)) { type = 'd'; } else if (S_ISREG(st.st_mode)) { type = ' '; } else { type = '?'; } mode = st.st_mode & 0777; (void) strftime(time_buf, sizeof (time_buf), "%b %e %T %Y", localtime(&st.st_mtime)); printf("%c 0%3o %9" PRIu64 " %s %s\n", type, mode, (uint64_t)st.st_size, time_buf, d->d_name); } *offp = offset; } /* * get rname [lname] */ void do_get(int argc, char **argv) { struct stat64 st; char *rname; char *lname; vnode_t *vp = NULL; offset_t off; ssize_t cnt, x; int oflg = O_RDWR | O_CREAT; int lfd = -1; int error; if (vfsp == NULL) { fprintf(stderr, "mnt required first\n"); return; } if (argc < 2) { fprintf(stderr, "rname required\n"); return; } rname = argv[1]; if (argc > 2) { lname = argv[2]; /* * When local name is specified, overwrite. * Convenient for scripts etc. */ oflg |= O_TRUNC; } else { lname = rname; /* Local file should not exist. */ oflg |= O_EXCL; } lfd = open(lname, oflg, 0644); if (lfd < 0) { perror(lname); return; } error = vn_open(rname, 0, FREAD, 0, &vp, 0, 0); if (error != 0) { fprintf(stderr, "do_get, vn_open error=%d\n", error); goto out; } error = fake_stat(vp, &st, 0); if (error != 0) { fprintf(stderr, "do_get, stat error=%d\n", error); goto out; } off = 0; do { cnt = fake_pread(vp, tbuf, TBUFSZ, off); if (cnt < 0) { fprintf(stderr, "do_get, read %d\n", -cnt); goto out; } x = write(lfd, tbuf, cnt); if (x < 0) { fprintf(stderr, "do_get, write %d\n", errno); goto out; } off += x; } while (off < st.st_size); out: if (vp != NULL) vn_close_rele(vp, 0); if (lfd != -1) close(lfd); } /* * put lname [rname] */ void do_put(int argc, char **argv) { struct stat64 rst; struct stat st; char *rname; char *lname; vnode_t *vp = NULL; offset_t off; ssize_t cnt, x; int oflg = FREAD|FWRITE|FCREAT; int lfd = -1; int error; if (vfsp == NULL) { fprintf(stderr, "mnt required first\n"); return; } if (argc < 2) { fprintf(stderr, "lname required\n"); return; } lname = argv[1]; if (argc > 2) { rname = argv[2]; /* * When remote name is specified, overwrite. * Convenient for scripts etc. */ oflg |= FTRUNC; } else { rname = lname; /* Remote file should not exist. */ oflg |= FEXCL; } lfd = open(lname, O_RDONLY, 0); if (lfd < 0) { perror(lname); return; } error = fstat(lfd, &st); if (error != 0) { fprintf(stderr, "do_put, stat error=%d\n", error); goto out; } error = vn_open(rname, 0, oflg, 0, &vp, 0, 0); if (error != 0) { fprintf(stderr, "do_put, vn_open error=%d\n", error); goto out; } off = 0; do { cnt = pread(lfd, tbuf, TBUFSZ, off); if (cnt < 0) { fprintf(stderr, "do_put, read %d\n", errno); goto out; } x = fake_pwrite(vp, tbuf, cnt, off); if (x < 0) { fprintf(stderr, "do_put, write %d\n", -x); goto out; } off += cnt; } while (off < st.st_size); /* This getattr should go OtW. */ error = fake_stat(vp, &rst, 0); if (error != 0) { fprintf(stderr, "do_put, stat error=%d\n", error); goto out; } if (rst.st_size != st.st_size) { fprintf(stderr, "do_put, wrong size?\n"); } out: if (vp != NULL) vn_close_rele(vp, 0); if (lfd != -1) close(lfd); } /* * rm rname */ void do_rm(int argc, char **argv) { char *rname; int error; if (vfsp == NULL) { fprintf(stderr, "mnt required first\n"); return; } if (argc < 2) { fprintf(stderr, "rname required\n"); return; } rname = argv[1]; error = fake_unlink(rname, 0); if (error != 0) { fprintf(stderr, "do_rm, unlink error=%d\n", error); } } /* * mv fromname toname */ void do_mv(int argc, char **argv) { int error; if (vfsp == NULL) { fprintf(stderr, "mnt required first\n"); return; } if (argc < 3) { fprintf(stderr, "from_name to_name required\n"); return; } error = fake_rename(argv[1], argv[2]); if (error != 0) { fprintf(stderr, "do_mv, rename error=%d\n", error); } } /* * mkdir rname */ void do_mkdir(int argc, char **argv) { char *rname; vnode_t *vp = NULL; int error; if (vfsp == NULL) { fprintf(stderr, "mnt required first\n"); return; } if (argc < 2) { fprintf(stderr, "rname required\n"); return; } rname = argv[1]; error = vn_open(rname, 0, FCREAT, 0, &vp, CRMKDIR, 0); if (error != 0) { fprintf(stderr, "do_mkdir, vn_open error=%d\n", error); } if (vp != NULL) vn_close_rele(vp, 0); } /* * rmdir rname */ void do_rmdir(int argc, char **argv) { char *rname; int error; if (vfsp == NULL) { fprintf(stderr, "mnt required first\n"); return; } if (argc < 2) { fprintf(stderr, "rname required\n"); return; } rname = argv[1]; error = fake_unlink(rname, AT_REMOVEDIR); if (error != 0) { fprintf(stderr, "do_rmdir, unlink error=%d\n", error); } } /* * Simple option setting * * Each arg is handled as one line in .nsmbrc [default] */ void do_opt(int argc, char **argv) { static char template[20] = "/tmp/fksmbclXXXXXX"; static char rcname[30]; char *tdname; char *save_home; FILE *fp; int err, i; if (argc < 2) { fprintf(stderr, "opt {key}[=value]\n"); return; } tdname = mkdtemp(template); if (tdname == NULL) { perror("mkdtemp"); return; } (void) snprintf(rcname, sizeof (rcname), "%s/.nsmbrc", tdname); fp = fopen(rcname, "w"); if (fp == NULL) { perror(rcname); goto out; } fprintf(fp, "[default]\n"); for (i = 1; i < argc; i++) fprintf(fp, "%s\n", argv[i]); fclose(fp); save_home = ctx->ct_home; ctx->ct_home = tdname; err = smb_ctx_readrc(ctx); ctx->ct_home = save_home; if (err != 0) fprintf(stderr, "readrc, err=%d\n", err); out: (void) unlink(rcname); (void) rmdir(tdname); } /* * Command table */ static struct cmd_tbl_ent cmd_tbl[] = { { do_help, "help", "" }, { do_exit, "exit", "" }, { do_logon, "logon", "[user [dom [pass]]]" }, { do_logoff, "logoff", "[close-driver]" }, { do_shares, "shares", "" }, { do_mount, "mount", "{share} [optstr]" }, { do_unmount, "umount", "" }, { do_unmount, "unmount", "" }, { do_statfs, "statfs", "" }, { do_dir, "dir", "{rdir} [lfile]" }, { do_dirx, "dirx", "{rdir} [lfile]" }, { do_get, "get", "{rfile} [lfile]" }, { do_put, "put", "{lfile} [rfile]" }, { do_mv, "mv", "{from} {to}" }, { do_rm, "rm", "{rfile}" }, { do_mkdir, "mkdir", "{rfile}" }, { do_rmdir, "rmdir", "{rfile}" }, { do_opt, "opt", "{option}" }, { NULL, NULL, NULL } }; /* * Provide a real function (one that prints something) to replace * the stub in libfakekernel. This prints cmn_err() messages. */ void fakekernel_putlog(char *msg, size_t len, int flags) { /* * [CE_CONT, CE_NOTE, CE_WARN, CE_PANIC] maps to * [SL_NOTE, SL_NOTE, SL_WARN, SL_FATAL] */ if (smb_debug == 0 && (flags & SL_NOTE)) return; (void) fwrite(msg, 1, len, stdout); (void) fflush(stdout); } /* * Print nsmb debug messages via driver smb_debugmsg() */ void smb_debugmsg(const char *func, char *msg) { if (smb_debug < 2) return; printf("[kmod] %s: %s\n", func, msg); } /* * Enable libumem debugging */ const char * _umem_debug_init(void) { return ("default,verbose"); /* $UMEM_DEBUG setting */ } const char * _umem_logging_init(void) { return ("fail,contents"); /* $UMEM_LOGGING setting */ } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * Test program for the smbfs named pipe API. */ #include #include #include #include #include #include #include #include #include #include /* * This is a quick hack for testing client-side named pipes. * Its purpose is to test SMB named-pipe interface separately * from the RPC implementation. It's a "hack" because it uses * hand-crafted RPC messages (extracted from network traffic). */ /* This is a DCE/RPC bind call for "srvsvc". */ static const uchar_t srvsvc_bind[] = { 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xc8, 0x4f, 0x32, 0x4b, 0x70, 0x16, 0xd3, 0x01, 0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88, 0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00 }; /* This is a srvsvc "enum servers" call, in two parts */ static const uchar_t srvsvc_enum1[] = { 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, #define ENUM_RPCLEN_OFF 8 /* V - RPC frag length */ 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ... and the operation number is: VVVV */ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0f, 0x00, #define ENUM_SLEN1_OFF 28 #define ENUM_SLEN2_OFF 36 /* server name, length 14 vv ... */ 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00 }; /* UNC server here, i.e.: "\\192.168.1.6" */ static const uchar_t srvsvc_enum2[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; static uchar_t sendbuf[1024]; static uchar_t recvbuf[4096]; /* * Print strings found in the buffer. */ static void pstrings(const uchar_t *buf, int len) { const uchar_t *p = buf; uint16_t u2; boolean_t instr = B_FALSE; while (len > 2) { u2 = *p++; u2 |= (*p++) << 8; len -= 2; if ((u2 & 0xFF80) == 0 && isprint(u2)) { /* printable */ instr = B_TRUE; putchar(u2); } else { /* not printalbe */ if (instr) putchar('\n'); instr = B_FALSE; } } if (instr) putchar('\n'); } /* * Put a unicode UNC server name, including the null. * Quick-n-dirty, just for this test... */ static int put_uncserver(const char *s, uchar_t *buf) { uchar_t *p = buf; char c; *p++ = '\\'; *p++ = '\0'; *p++ = '\\'; *p++ = '\0'; do { c = *s++; if (c == '/') c = '\\'; *p++ = c; *p++ = '\0'; } while (c != 0); return (p - buf); } /* * Send the bind and read the ack. * This tests smb_fh_xactnp. */ static int do_bind(int fid) { int err, len, more; more = 0; len = sizeof (recvbuf); err = smb_fh_xactnp(fid, sizeof (srvsvc_bind), (char *)srvsvc_bind, &len, (char *)recvbuf, &more); if (err) { printf("xact bind, err=%d\n", err); return (err); } if (more > 0) { if (more > sizeof (recvbuf)) { printf("bogus more=%d\n", more); more = sizeof (recvbuf); } len = smb_fh_read(fid, 0, more, (char *)recvbuf); if (len == -1) { err = EIO; printf("read enum resp, err=%d\n", err); return (err); } } return (0); } static int do_enum(char *server, int fid) { int err, len, rlen, wlen; uchar_t *p; /* * Build the enum request - three parts. * See above: srvsvc_enum1, srvsvc_enum2 * * First part: RPC header, etc. */ p = sendbuf; len = sizeof (srvsvc_enum1); /* 40 */ memcpy(p, srvsvc_enum1, len); p += len; /* Second part: UNC server name */ len = put_uncserver(server, p); p += len; sendbuf[ENUM_SLEN1_OFF] = len / 2; sendbuf[ENUM_SLEN2_OFF] = len / 2; /* Third part: level, etc. (align4) */ for (len = (p - sendbuf) & 3; len; len--) *p++ = '\0'; len = sizeof (srvsvc_enum2); /* 28 */ memcpy(p, srvsvc_enum2, len); p += len; /* * Compute total length, and fixup RPC header. */ len = p - sendbuf; sendbuf[ENUM_RPCLEN_OFF] = len; /* * Send the enum request, read the response. * This tests smb_fh_write, smb_fh_read. */ wlen = smb_fh_write(fid, 0, len, (char *)sendbuf); if (wlen == -1) { err = errno; printf("write enum req, err=%d\n", err); return (err); } if (wlen != len) { printf("write enum req, short write %d\n", wlen); return (EIO); } rlen = smb_fh_read(fid, 0, sizeof (recvbuf), (char *)recvbuf); if (rlen == -1) { err = errno; printf("read enum resp, err=%d\n", err); return (err); } /* * Just dump strings found in the response data. * Skip the first 0x90 (RPC wrappers). */ printf("enum strings\n"); pstrings(recvbuf + 0x90, rlen - 0x90); return (0); } int list_shares(struct smb_ctx *ctx) { static char path[] = "/srvsvc"; static uchar_t key[16]; char *server = ctx->ct_srvname; int err, fd; printf("open pipe: %s\n", path); fd = smb_fh_open(ctx, path, O_RDWR); if (fd < 0) { perror(path); return (errno); } /* Test this too. */ err = smb_fh_getssnkey(fd, key, sizeof (key)); if (err) { printf("getssnkey: %d\n", err); goto out; } err = do_bind(fd); if (err) { printf("do_bind: %d\n", err); goto out; } err = do_enum(server, fd); if (err) printf("do_enum: %d\n", err); out: smb_fh_close(fd); return (0); }