summaryrefslogtreecommitdiff
path: root/source4/client
diff options
context:
space:
mode:
Diffstat (limited to 'source4/client')
-rw-r--r--source4/client/cifsdd.c610
-rw-r--r--source4/client/cifsdd.h105
-rw-r--r--source4/client/cifsddio.c507
-rw-r--r--source4/client/client.c3273
-rw-r--r--source4/client/config.mk36
-rw-r--r--source4/client/mount.cifs.c557
-rw-r--r--source4/client/smbmnt.c306
-rw-r--r--source4/client/smbmount.c930
-rw-r--r--source4/client/smbspool.c353
-rw-r--r--source4/client/smbumount.c186
-rwxr-xr-xsource4/client/tests/test_cifsdd.sh73
-rwxr-xr-xsource4/client/tests/test_smbclient.sh160
-rw-r--r--source4/client/tree.c809
13 files changed, 7905 insertions, 0 deletions
diff --git a/source4/client/cifsdd.c b/source4/client/cifsdd.c
new file mode 100644
index 0000000000..141b165ad4
--- /dev/null
+++ b/source4/client/cifsdd.c
@@ -0,0 +1,610 @@
+/*
+ CIFSDD - dd for SMB.
+ Main program, argument handling and block copying.
+
+ Copyright (C) James Peach 2005-2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "auth/gensec/gensec.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/raw/libcliraw.h"
+#include "lib/events/events.h"
+
+#include "cifsdd.h"
+#include "param/param.h"
+
+const char * const PROGNAME = "cifsdd";
+
+#define SYNTAX_EXIT_CODE 1 /* Invokation syntax or logic error. */
+#define EOM_EXIT_CODE 9 /* Out of memory error. */
+#define FILESYS_EXIT_CODE 10 /* File manipulation error. */
+#define IOERROR_EXIT_CODE 11 /* Error during IO phase. */
+
+struct dd_stats_record dd_stats;
+
+static int dd_sigint;
+static int dd_sigusr1;
+
+static void dd_handle_signal(int sig)
+{
+ switch (sig)
+ {
+ case SIGINT:
+ ++dd_sigint;
+ break;
+ case SIGUSR1:
+ ++dd_sigusr1;
+ break;
+ default:
+ break;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* Argument handling. */
+/* ------------------------------------------------------------------------- */
+
+static const char * argtype_str(enum argtype arg_type)
+{
+ static const struct {
+ enum argtype arg_type;
+ const char * arg_name;
+ } names [] =
+ {
+ { ARG_NUMERIC, "COUNT" },
+ { ARG_SIZE, "SIZE" },
+ { ARG_PATHNAME, "FILE" },
+ { ARG_BOOL, "BOOLEAN" },
+ };
+
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ if (arg_type == names[i].arg_type) {
+ return(names[i].arg_name);
+ }
+ }
+
+ return("<unknown>");
+}
+
+static struct argdef args[] =
+{
+ { "bs", ARG_SIZE, "force ibs and obs to SIZE bytes" },
+ { "ibs", ARG_SIZE, "read SIZE bytes at a time" },
+ { "obs", ARG_SIZE, "write SIZE bytes at a time" },
+
+ { "count", ARG_NUMERIC, "copy COUNT input blocks" },
+ { "seek",ARG_NUMERIC, "skip COUNT blocks at start of output" },
+ { "skip",ARG_NUMERIC, "skip COUNT blocks at start of input" },
+
+ { "if", ARG_PATHNAME, "read input from FILE" },
+ { "of", ARG_PATHNAME, "write output to FILE" },
+
+ { "direct", ARG_BOOL, "use direct I/O if non-zero" },
+ { "sync", ARG_BOOL, "use synchronous writes if non-zero" },
+ { "oplock", ARG_BOOL, "take oplocks on the input and output files" },
+
+/* FIXME: We should support using iflags and oflags for setting oplock and I/O
+ * options. This would make us compatible with GNU dd.
+ */
+};
+
+static struct argdef * find_named_arg(const char * arg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(args); ++i) {
+ if (strwicmp(arg, args[i].arg_name) == 0) {
+ return(&args[i]);
+ }
+ }
+
+ return(NULL);
+}
+
+int set_arg_argv(const char * argv)
+{
+ struct argdef * arg;
+
+ char * name;
+ char * val;
+
+ if ((name = strdup(argv)) == NULL) {
+ return(0);
+ }
+
+ if ((val = strchr(name, '=')) == NULL) {
+ fprintf(stderr, "%s: malformed argument \"%s\"\n",
+ PROGNAME, argv);
+ goto fail;
+ }
+
+ *val = '\0';
+ val++;
+
+ if ((arg = find_named_arg(name)) == NULL) {
+ fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
+ PROGNAME, name);
+ goto fail;
+ }
+
+ /* Found a matching name; convert the variable argument. */
+ switch (arg->arg_type) {
+ case ARG_NUMERIC:
+ if (!conv_str_u64(val, &arg->arg_val.nval)) {
+ goto fail;
+ }
+ break;
+ case ARG_SIZE:
+ if (!conv_str_size(val, &arg->arg_val.nval)) {
+ goto fail;
+ }
+ break;
+ case ARG_BOOL:
+ if (!conv_str_bool(val, &arg->arg_val.bval)) {
+ goto fail;
+ }
+ break;
+ case ARG_PATHNAME:
+ if (!(arg->arg_val.pval = strdup(val))) {
+ goto fail;
+ }
+ break;
+ default:
+ fprintf(stderr, "%s: argument \"%s\" is of "
+ "unknown type\n", PROGNAME, name);
+ goto fail;
+ }
+
+ free(name);
+ return(1);
+
+fail:
+ free(name);
+ return(0);
+}
+
+void set_arg_val(const char * name, ...)
+{
+ va_list ap;
+ struct argdef * arg;
+
+ va_start(ap, name);
+ if ((arg = find_named_arg(name)) == NULL) {
+ goto fail;
+ }
+
+ /* Found a matching name; convert the variable argument. */
+ switch (arg->arg_type) {
+ case ARG_NUMERIC:
+ case ARG_SIZE:
+ arg->arg_val.nval = va_arg(ap, uint64_t);
+ break;
+ case ARG_BOOL:
+ arg->arg_val.bval = va_arg(ap, int);
+ break;
+ case ARG_PATHNAME:
+ arg->arg_val.pval = va_arg(ap, char *);
+ if (arg->arg_val.pval) {
+ arg->arg_val.pval = strdup(arg->arg_val.pval);
+ }
+ break;
+ default:
+ fprintf(stderr, "%s: argument \"%s\" is of "
+ "unknown type\n", PROGNAME, name);
+ goto fail;
+ }
+
+ va_end(ap);
+ return;
+
+fail:
+ fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
+ PROGNAME, name);
+ va_end(ap);
+ return;
+}
+
+bool check_arg_bool(const char * name)
+{
+ struct argdef * arg;
+
+ if ((arg = find_named_arg(name)) &&
+ (arg->arg_type == ARG_BOOL)) {
+ return(arg->arg_val.bval);
+ }
+
+ DEBUG(0, ("invalid argument name: %s", name));
+ SMB_ASSERT(0);
+ return(false);
+}
+
+uint64_t check_arg_numeric(const char * name)
+{
+ struct argdef * arg;
+
+ if ((arg = find_named_arg(name)) &&
+ (arg->arg_type == ARG_NUMERIC || arg->arg_type == ARG_SIZE)) {
+ return(arg->arg_val.nval);
+ }
+
+ DEBUG(0, ("invalid argument name: %s", name));
+ SMB_ASSERT(0);
+ return(-1);
+}
+
+const char * check_arg_pathname(const char * name)
+{
+ struct argdef * arg;
+
+ if ((arg = find_named_arg(name)) &&
+ (arg->arg_type == ARG_PATHNAME)) {
+ return(arg->arg_val.pval);
+ }
+
+ DEBUG(0, ("invalid argument name: %s", name));
+ SMB_ASSERT(0);
+ return(NULL);
+}
+
+static void dump_args(void)
+{
+ int i;
+
+ DEBUG(10, ("dumping argument values:\n"));
+ for (i = 0; i < ARRAY_SIZE(args); ++i) {
+ switch (args[i].arg_type) {
+ case ARG_NUMERIC:
+ case ARG_SIZE:
+ DEBUG(10, ("\t%s=%llu\n", args[i].arg_name,
+ (unsigned long long)args[i].arg_val.nval));
+ break;
+ case ARG_BOOL:
+ DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
+ args[i].arg_val.bval ? "yes" : "no"));
+ break;
+ case ARG_PATHNAME:
+ DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
+ args[i].arg_val.pval ?
+ args[i].arg_val.pval :
+ "(NULL)"));
+ break;
+ default:
+ SMB_ASSERT(0);
+ }
+ }
+}
+
+static void cifsdd_help_message(poptContext pctx,
+ enum poptCallbackReason preason,
+ struct poptOption * poption,
+ const char * parg,
+ void * pdata)
+{
+ static const char notes[] =
+"FILE can be a local filename or a UNC path of the form //server/share/path.\n";
+
+ char prefix[24];
+ int i;
+
+ if (poption->shortName != '?') {
+ poptPrintUsage(pctx, stdout, 0);
+ fprintf(stdout, " [dd options]\n");
+ exit(0);
+ }
+
+ poptPrintHelp(pctx, stdout, 0);
+ fprintf(stdout, "\nCIFS dd options:\n");
+
+ for (i = 0; i < ARRAY_SIZE(args); ++i) {
+ if (args[i].arg_name == NULL) {
+ break;
+ }
+
+ snprintf(prefix, sizeof(prefix), "%s=%-*s",
+ args[i].arg_name,
+ (int)(sizeof(prefix) - strlen(args[i].arg_name) - 2),
+ argtype_str(args[i].arg_type));
+ prefix[sizeof(prefix) - 1] = '\0';
+ fprintf(stdout, " %s%s\n", prefix, args[i].arg_help);
+ }
+
+ fprintf(stdout, "\n%s\n", notes);
+ exit(0);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Main block copying routine. */
+/* ------------------------------------------------------------------------- */
+
+static void print_transfer_stats(void)
+{
+ if (DEBUGLEVEL > 0) {
+ printf("%llu+%llu records in (%llu bytes)\n"
+ "%llu+%llu records out (%llu bytes)\n",
+ (unsigned long long)dd_stats.in.fblocks,
+ (unsigned long long)dd_stats.in.pblocks,
+ (unsigned long long)dd_stats.in.bytes,
+ (unsigned long long)dd_stats.out.fblocks,
+ (unsigned long long)dd_stats.out.pblocks,
+ (unsigned long long)dd_stats.out.bytes);
+ } else {
+ printf("%llu+%llu records in\n%llu+%llu records out\n",
+ (unsigned long long)dd_stats.in.fblocks,
+ (unsigned long long)dd_stats.in.pblocks,
+ (unsigned long long)dd_stats.out.fblocks,
+ (unsigned long long)dd_stats.out.pblocks);
+ }
+}
+
+static struct dd_iohandle * open_file(struct resolve_context *resolve_ctx,
+ struct event_context *ev,
+ const char * which, const char **ports,
+ struct smbcli_options *smb_options)
+{
+ int options = 0;
+ const char * path = NULL;
+ struct dd_iohandle * handle = NULL;
+
+ if (check_arg_bool("direct")) {
+ options |= DD_DIRECT_IO;
+ }
+
+ if (check_arg_bool("sync")) {
+ options |= DD_SYNC_IO;
+ }
+
+ if (check_arg_bool("oplock")) {
+ options |= DD_OPLOCK;
+ }
+
+ if (strcmp(which, "if") == 0) {
+ path = check_arg_pathname("if");
+ handle = dd_open_path(resolve_ctx, ev, path, ports,
+ check_arg_numeric("ibs"), options,
+ smb_options);
+ } else if (strcmp(which, "of") == 0) {
+ options |= DD_WRITE;
+ path = check_arg_pathname("of");
+ handle = dd_open_path(resolve_ctx, ev, path, ports,
+ check_arg_numeric("obs"), options,
+ smb_options);
+ } else {
+ SMB_ASSERT(0);
+ return(NULL);
+ }
+
+ if (!handle) {
+ fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
+ }
+
+ return(handle);
+}
+
+static int copy_files(struct event_context *ev, struct loadparm_context *lp_ctx)
+{
+ uint8_t * iobuf; /* IO buffer. */
+ uint64_t iomax; /* Size of the IO buffer. */
+ uint64_t data_size; /* Amount of data in the IO buffer. */
+
+ uint64_t ibs;
+ uint64_t obs;
+ uint64_t count;
+
+ struct dd_iohandle * ifile;
+ struct dd_iohandle * ofile;
+
+ struct smbcli_options options;
+
+ ibs = check_arg_numeric("ibs");
+ obs = check_arg_numeric("obs");
+ count = check_arg_numeric("count");
+
+ lp_smbcli_options(lp_ctx, &options);
+
+ /* Allocate IO buffer. We need more than the max IO size because we
+ * could accumulate a remainder if ibs and obs don't match.
+ */
+ iomax = 2 * MAX(ibs, obs);
+ if ((iobuf = malloc_array_p(uint8_t, iomax)) == NULL) {
+ fprintf(stderr,
+ "%s: failed to allocate IO buffer of %llu bytes\n",
+ PROGNAME, (unsigned long long)iomax);
+ return(EOM_EXIT_CODE);
+ }
+
+ options.max_xmit = MAX(ibs, obs);
+
+ DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
+ (unsigned long long)iomax, options.max_xmit));
+
+ if (!(ifile = open_file(lp_resolve_context(lp_ctx), ev, "if",
+ lp_smb_ports(lp_ctx), &options))) {
+ return(FILESYS_EXIT_CODE);
+ }
+
+ if (!(ofile = open_file(lp_resolve_context(lp_ctx), ev, "of",
+ lp_smb_ports(lp_ctx), &options))) {
+ return(FILESYS_EXIT_CODE);
+ }
+
+ /* Seek the files to their respective starting points. */
+ ifile->io_seek(ifile, check_arg_numeric("skip") * ibs);
+ ofile->io_seek(ofile, check_arg_numeric("seek") * obs);
+
+ DEBUG(4, ("max xmit was negotiated to be %d\n", options.max_xmit));
+
+ for (data_size = 0;;) {
+
+ /* Handle signals. We are somewhat compatible with GNU dd.
+ * SIGINT makes us stop, but still print transfer statistics.
+ * SIGUSR1 makes us print transfer statistics but we continue
+ * copying.
+ */
+ if (dd_sigint) {
+ break;
+ }
+
+ if (dd_sigusr1) {
+ print_transfer_stats();
+ dd_sigusr1 = 0;
+ }
+
+ if (ifile->io_flags & DD_END_OF_FILE) {
+ DEBUG(4, ("flushing %llu bytes at EOF\n",
+ (unsigned long long)data_size));
+ while (data_size > 0) {
+ if (!dd_flush_block(ofile, iobuf,
+ &data_size, obs)) {
+ return(IOERROR_EXIT_CODE);
+ }
+ }
+ goto done;
+ }
+
+ /* Try and read enough blocks of ibs bytes to be able write
+ * out one of obs bytes.
+ */
+ if (!dd_fill_block(ifile, iobuf, &data_size, obs, ibs)) {
+ return(IOERROR_EXIT_CODE);
+ }
+
+ if (data_size == 0) {
+ /* Done. */
+ SMB_ASSERT(ifile->io_flags & DD_END_OF_FILE);
+ }
+
+ /* Stop reading when we hit the block count. */
+ if (dd_stats.in.bytes >= (ibs * count)) {
+ ifile->io_flags |= DD_END_OF_FILE;
+ }
+
+ /* If we wanted to be a legitimate dd, we would do character
+ * conversions and other shenanigans here.
+ */
+
+ /* Flush what we read in units of obs bytes. We want to have
+ * at least obs bytes in the IO buffer but might not if the
+ * file is too small.
+ */
+ if (data_size &&
+ !dd_flush_block(ofile, iobuf, &data_size, obs)) {
+ return(IOERROR_EXIT_CODE);
+ }
+ }
+
+done:
+ print_transfer_stats();
+ return(0);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Main. */
+/* ------------------------------------------------------------------------- */
+
+struct poptOption cifsddHelpOptions[] = {
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&cifsdd_help_message, '\0', NULL, NULL },
+ { "help", '?', 0, NULL, '?', "Show this help message", NULL },
+ { "usage", '\0', 0, NULL, 'u', "Display brief usage message", NULL },
+ { NULL }
+} ;
+
+int main(int argc, const char ** argv)
+{
+ int i;
+ const char ** dd_args;
+ struct event_context *ev;
+
+ poptContext pctx;
+ struct poptOption poptions[] = {
+ /* POPT_AUTOHELP */
+ { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cifsddHelpOptions,
+ 0, "Help options:", NULL },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ /* Block sizes. */
+ set_arg_val("bs", (uint64_t)4096);
+ set_arg_val("ibs", (uint64_t)4096);
+ set_arg_val("obs", (uint64_t)4096);
+ /* Block counts. */
+ set_arg_val("count", (uint64_t)-1);
+ set_arg_val("seek", (uint64_t)0);
+ set_arg_val("seek", (uint64_t)0);
+ /* Files. */
+ set_arg_val("if", NULL);
+ set_arg_val("of", NULL);
+ /* Options. */
+ set_arg_val("direct", false);
+ set_arg_val("sync", false);
+ set_arg_val("oplock", false);
+
+ pctx = poptGetContext(PROGNAME, argc, argv, poptions, 0);
+ while ((i = poptGetNextOpt(pctx)) != -1) {
+ ;
+ }
+
+ for (dd_args = poptGetArgs(pctx); dd_args && *dd_args; ++dd_args) {
+
+ if (!set_arg_argv(*dd_args)) {
+ fprintf(stderr, "%s: invalid option: %s\n",
+ PROGNAME, *dd_args);
+ exit(SYNTAX_EXIT_CODE);
+ }
+
+ /* "bs" has the side-effect of setting "ibs" and "obs". */
+ if (strncmp(*dd_args, "bs=", 3) == 0) {
+ uint64_t bs = check_arg_numeric("bs");
+ set_arg_val("ibs", bs);
+ set_arg_val("obs", bs);
+ }
+ }
+
+ ev = s4_event_context_init(talloc_autofree_context());
+
+ gensec_init(cmdline_lp_ctx);
+ dump_args();
+
+ if (check_arg_numeric("ibs") == 0 || check_arg_numeric("ibs") == 0) {
+ fprintf(stderr, "%s: block sizes must be greater that zero\n",
+ PROGNAME);
+ exit(SYNTAX_EXIT_CODE);
+ }
+
+ if (check_arg_pathname("if") == NULL) {
+ fprintf(stderr, "%s: missing input filename\n", PROGNAME);
+ exit(SYNTAX_EXIT_CODE);
+ }
+
+ if (check_arg_pathname("of") == NULL) {
+ fprintf(stderr, "%s: missing output filename\n", PROGNAME);
+ exit(SYNTAX_EXIT_CODE);
+ }
+
+ CatchSignal(SIGINT, dd_handle_signal);
+ CatchSignal(SIGUSR1, dd_handle_signal);
+ return(copy_files(ev, cmdline_lp_ctx));
+}
+
+/* vim: set sw=8 sts=8 ts=8 tw=79 : */
diff --git a/source4/client/cifsdd.h b/source4/client/cifsdd.h
new file mode 100644
index 0000000000..21a4ad4882
--- /dev/null
+++ b/source4/client/cifsdd.h
@@ -0,0 +1,105 @@
+/*
+ CIFSDD - dd for SMB.
+ Declarations and administrivia.
+
+ Copyright (C) James Peach 2005-2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+extern const char * const PROGNAME;
+
+enum argtype
+{
+ ARG_NUMERIC,
+ ARG_SIZE,
+ ARG_PATHNAME,
+ ARG_BOOL,
+};
+
+struct argdef
+{
+ const char * arg_name;
+ enum argtype arg_type;
+ const char * arg_help;
+
+ union
+ {
+ bool bval;
+ uint64_t nval;
+ const char * pval;
+ } arg_val;
+};
+
+int set_arg_argv(const char * argv);
+void set_arg_val(const char * name, ...);
+
+bool check_arg_bool(const char * name);
+uint64_t check_arg_numeric(const char * name);
+const char * check_arg_pathname(const char * name);
+
+typedef bool (*dd_seek_func)(void * handle, uint64_t offset);
+typedef bool (*dd_read_func)(void * handle, uint8_t * buf,
+ uint64_t wanted, uint64_t * actual);
+typedef bool (*dd_write_func)(void * handle, uint8_t * buf,
+ uint64_t wanted, uint64_t * actual);
+
+struct dd_stats_record
+{
+ struct
+ {
+ uint64_t fblocks; /* Full blocks. */
+ uint64_t pblocks; /* Partial blocks. */
+ uint64_t bytes; /* Total bytes read. */
+ } in;
+ struct
+ {
+ uint64_t fblocks; /* Full blocks. */
+ uint64_t pblocks; /* Partial blocks. */
+ uint64_t bytes; /* Total bytes written. */
+ } out;
+};
+
+extern struct dd_stats_record dd_stats;
+
+struct dd_iohandle
+{
+ dd_seek_func io_seek;
+ dd_read_func io_read;
+ dd_write_func io_write;
+ int io_flags;
+};
+
+#define DD_END_OF_FILE 0x10000000
+
+#define DD_DIRECT_IO 0x00000001
+#define DD_SYNC_IO 0x00000002
+#define DD_WRITE 0x00000004
+#define DD_OPLOCK 0x00000008
+
+struct smbcli_options;
+struct event_context;
+
+struct dd_iohandle * dd_open_path(struct resolve_context *resolve_ctx,
+ struct event_context *ev,
+ const char * path,
+ const char **ports,
+ uint64_t io_size, int options,
+ struct smbcli_options *smb_options);
+bool dd_fill_block(struct dd_iohandle * h, uint8_t * buf,
+ uint64_t * buf_size, uint64_t need_size, uint64_t block_size);
+bool dd_flush_block(struct dd_iohandle * h, uint8_t * buf,
+ uint64_t * buf_size, uint64_t block_size);
+
+/* vim: set sw=8 sts=8 ts=8 tw=79 : */
diff --git a/source4/client/cifsddio.c b/source4/client/cifsddio.c
new file mode 100644
index 0000000000..4297c30012
--- /dev/null
+++ b/source4/client/cifsddio.c
@@ -0,0 +1,507 @@
+/*
+ CIFSDD - dd for SMB.
+ IO routines, generic and specific.
+
+ Copyright (C) James Peach 2005-2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "lib/cmdline/popt_common.h"
+
+#include "cifsdd.h"
+
+/* ------------------------------------------------------------------------- */
+/* UNIX file descriptor IO. */
+/* ------------------------------------------------------------------------- */
+
+struct fd_handle
+{
+ struct dd_iohandle h;
+ int fd;
+};
+
+#define IO_HANDLE_TO_FD(h) (((struct fd_handle *)(h))->fd)
+
+static bool fd_seek_func(void * handle, uint64_t offset)
+{
+ ssize_t ret;
+
+ ret = lseek(IO_HANDLE_TO_FD(handle), offset, SEEK_SET);
+ if (ret < 0) {
+ fprintf(stderr, "%s: seek failed: %s\n",
+ PROGNAME, strerror(errno));
+ return(false);
+ }
+
+ return(true);
+}
+
+static bool fd_read_func(void * handle,
+ uint8_t * buf,
+ uint64_t wanted,
+ uint64_t * actual)
+{
+ ssize_t ret;
+
+ ret = read(IO_HANDLE_TO_FD(handle), buf, wanted);
+ if (ret < 0) {
+ fprintf(stderr, "%s: %llu byte read failed: %s\n",
+ PROGNAME, (unsigned long long)wanted,
+ strerror(errno));
+ return(false);
+ }
+
+ *actual = (uint64_t)ret;
+ return(true);
+}
+
+static bool fd_write_func(void * handle,
+ uint8_t * buf,
+ uint64_t wanted,
+ uint64_t * actual)
+{
+ ssize_t ret;
+
+ ret = write(IO_HANDLE_TO_FD(handle), buf, wanted);
+ if (ret < 0) {
+ fprintf(stderr, "%s: %llu byte write failed: %s\n",
+ PROGNAME, (unsigned long long)wanted,
+ strerror(errno));
+ return(false);
+ }
+
+ *actual = (uint64_t)ret;
+ return(true);
+}
+
+static struct dd_iohandle * open_fd_handle(const char * path,
+ uint64_t io_size,
+ int options)
+{
+ struct fd_handle * fdh;
+ int oflags = 0;
+
+ DEBUG(4, ("opening fd stream for %s\n", path));
+ if ((fdh = talloc_zero(NULL, struct fd_handle)) == NULL) {
+ return(NULL);
+ }
+
+ fdh->h.io_read = fd_read_func;
+ fdh->h.io_write = fd_write_func;
+ fdh->h.io_seek = fd_seek_func;
+
+ if (options & DD_DIRECT_IO) {
+#ifdef HAVE_OPEN_O_DIRECT
+ oflags |= O_DIRECT;
+#else
+ DEBUG(1, ("no support for direct IO on this platform\n"));
+#endif
+ }
+
+ if (options & DD_SYNC_IO)
+ oflags |= O_SYNC;
+
+ oflags |= (options & DD_WRITE) ? (O_WRONLY | O_CREAT) : (O_RDONLY);
+
+ fdh->fd = open(path, oflags, 0644);
+ if (fdh->fd < 0) {
+ fprintf(stderr, "%s: %s: %s\n",
+ PROGNAME, path, strerror(errno));
+ talloc_free(fdh);
+ return(NULL);
+ }
+
+ if (options & DD_OPLOCK) {
+ DEBUG(2, ("FIXME: take local oplock on %s\n", path));
+ }
+
+ SMB_ASSERT((void *)fdh == (void *)&fdh->h);
+ return(&fdh->h);
+}
+
+/* ------------------------------------------------------------------------- */
+/* CIFS client IO. */
+/* ------------------------------------------------------------------------- */
+
+struct cifs_handle
+{
+ struct dd_iohandle h;
+ struct smbcli_state * cli;
+ int fnum;
+ uint64_t offset;
+};
+
+#define IO_HANDLE_TO_SMB(h) ((struct cifs_handle *)(h))
+
+static bool smb_seek_func(void * handle, uint64_t offset)
+{
+ IO_HANDLE_TO_SMB(handle)->offset = offset;
+ return(true);
+}
+
+static bool smb_read_func(void * handle, uint8_t * buf, uint64_t wanted,
+ uint64_t * actual)
+{
+ NTSTATUS ret;
+ union smb_read r;
+ struct cifs_handle * smbh;
+
+ ZERO_STRUCT(r);
+ smbh = IO_HANDLE_TO_SMB(handle);
+
+ r.generic.level = RAW_READ_READX;
+ r.readx.in.file.fnum = smbh->fnum;
+ r.readx.in.offset = smbh->offset;
+ r.readx.in.mincnt = wanted;
+ r.readx.in.maxcnt = wanted;
+ r.readx.out.data = buf;
+
+ /* FIXME: Should I really set readx.in.remaining? That just seems
+ * redundant.
+ */
+ ret = smb_raw_read(smbh->cli->tree, &r);
+ if (!NT_STATUS_IS_OK(ret)) {
+ fprintf(stderr, "%s: %llu byte read failed: %s\n",
+ PROGNAME, (unsigned long long)wanted,
+ nt_errstr(ret));
+ return(false);
+ }
+
+ /* Trap integer wrap. */
+ SMB_ASSERT((smbh->offset + r.readx.out.nread) >= smbh->offset);
+
+ *actual = r.readx.out.nread;
+ smbh->offset += r.readx.out.nread;
+ return(true);
+}
+
+static bool smb_write_func(void * handle, uint8_t * buf, uint64_t wanted,
+ uint64_t * actual)
+{
+ NTSTATUS ret;
+ union smb_write w;
+ struct cifs_handle * smbh;
+
+ ZERO_STRUCT(w);
+ smbh = IO_HANDLE_TO_SMB(handle);
+
+ w.generic.level = RAW_WRITE_WRITEX;
+ w.writex.in.file.fnum = smbh->fnum;
+ w.writex.in.offset = smbh->offset;
+ w.writex.in.count = wanted;
+ w.writex.in.data = buf;
+
+ ret = smb_raw_write(smbh->cli->tree, &w);
+ if (!NT_STATUS_IS_OK(ret)) {
+ fprintf(stderr, "%s: %llu byte write failed: %s\n",
+ PROGNAME, (unsigned long long)wanted,
+ nt_errstr(ret));
+ return(false);
+ }
+
+ *actual = w.writex.out.nwritten;
+ smbh->offset += w.writex.out.nwritten;
+ return(true);
+}
+
+static struct smbcli_state * init_smb_session(struct resolve_context *resolve_ctx,
+ struct event_context *ev,
+ const char * host,
+ const char **ports,
+ const char * share,
+ struct smbcli_options *options)
+{
+ NTSTATUS ret;
+ struct smbcli_state * cli = NULL;
+
+ /* When we support SMB URLs, we can get different user credentials for
+ * each connection, but for now, we just use the same one for both.
+ */
+ ret = smbcli_full_connection(NULL, &cli, host, ports, share,
+ NULL /* devtype */,
+ cmdline_credentials, resolve_ctx,
+ ev, options);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ fprintf(stderr, "%s: connecting to //%s/%s: %s\n",
+ PROGNAME, host, share, nt_errstr(ret));
+ return(NULL);
+ }
+
+ return(cli);
+}
+
+static int open_smb_file(struct smbcli_state * cli,
+ const char * path,
+ int options)
+{
+ NTSTATUS ret;
+ union smb_open o;
+
+ ZERO_STRUCT(o);
+
+ o.ntcreatex.level = RAW_OPEN_NTCREATEX;
+ o.ntcreatex.in.fname = path;
+
+ /* TODO: It's not clear whether to use these flags or to use the
+ * similarly named NTCREATEX flags in the create_options field.
+ */
+ if (options & DD_DIRECT_IO)
+ o.ntcreatex.in.flags |= FILE_FLAG_NO_BUFFERING;
+
+ if (options & DD_SYNC_IO)
+ o.ntcreatex.in.flags |= FILE_FLAG_WRITE_THROUGH;
+
+ o.ntcreatex.in.access_mask |=
+ (options & DD_WRITE) ? SEC_FILE_WRITE_DATA
+ : SEC_FILE_READ_DATA;
+
+ /* Try to create the file only if we will be writing to it. */
+ o.ntcreatex.in.open_disposition =
+ (options & DD_WRITE) ? NTCREATEX_DISP_OPEN_IF
+ : NTCREATEX_DISP_OPEN;
+
+ o.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+
+ if (options & DD_OPLOCK) {
+ o.ntcreatex.in.flags |= NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ }
+
+ ret = smb_raw_open(cli->tree, NULL, &o);
+ if (!NT_STATUS_IS_OK(ret)) {
+ fprintf(stderr, "%s: opening %s: %s\n",
+ PROGNAME, path, nt_errstr(ret));
+ return(-1);
+ }
+
+ return(o.ntcreatex.out.file.fnum);
+}
+
+static struct dd_iohandle * open_cifs_handle(struct resolve_context *resolve_ctx,
+ struct event_context *ev,
+ const char * host,
+ const char **ports,
+ const char * share,
+ const char * path,
+ uint64_t io_size,
+ int options,
+ struct smbcli_options *smb_options)
+{
+ struct cifs_handle * smbh;
+
+ if (path == NULL || *path == '\0') {
+ fprintf(stderr, "%s: missing path name within share //%s/%s\n",
+ PROGNAME, host, share);
+ }
+
+ DEBUG(4, ("opening SMB stream to //%s/%s for %s\n",
+ host, share, path));
+
+ if ((smbh = talloc_zero(NULL, struct cifs_handle)) == NULL) {
+ return(NULL);
+ }
+
+ smbh->h.io_read = smb_read_func;
+ smbh->h.io_write = smb_write_func;
+ smbh->h.io_seek = smb_seek_func;
+
+ if ((smbh->cli = init_smb_session(resolve_ctx, ev, host, ports, share,
+ smb_options)) == NULL) {
+ return(NULL);
+ }
+
+ DEBUG(4, ("connected to //%s/%s with xmit size of %u bytes\n",
+ host, share, smbh->cli->transport->negotiate.max_xmit));
+
+ smbh->fnum = open_smb_file(smbh->cli, path, options);
+ return(&smbh->h);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Abstract IO interface. */
+/* ------------------------------------------------------------------------- */
+
+struct dd_iohandle * dd_open_path(struct resolve_context *resolve_ctx,
+ struct event_context *ev,
+ const char * path,
+ const char **ports,
+ uint64_t io_size,
+ int options,
+ struct smbcli_options *smb_options)
+{
+ if (file_exist(path)) {
+ return(open_fd_handle(path, io_size, options));
+ } else {
+ char * host;
+ char * share;
+
+ if (smbcli_parse_unc(path, NULL, &host, &share)) {
+ const char * remain;
+ remain = strstr(path, share) + strlen(share);
+
+ /* Skip over leading directory separators. */
+ while (*remain == '/' || *remain == '\\') { remain++; }
+
+ return(open_cifs_handle(resolve_ctx, ev, host, ports,
+ share, remain,
+ io_size, options, smb_options));
+ }
+
+ return(open_fd_handle(path, io_size, options));
+ }
+}
+
+/* Fill the buffer till it has at least need_size bytes. Use read operations of
+ * block_size bytes. Return the number of bytes read and fill buf_size with
+ * the new buffer size.
+ *
+ * NOTE: The IO buffer is guaranteed to be big enough to fit
+ * need_size + block_size bytes into it.
+ */
+bool dd_fill_block(struct dd_iohandle * h,
+ uint8_t * buf,
+ uint64_t * buf_size,
+ uint64_t need_size,
+ uint64_t block_size)
+{
+ uint64_t read_size;
+
+ SMB_ASSERT(block_size > 0);
+ SMB_ASSERT(need_size > 0);
+
+ while (*buf_size < need_size) {
+
+ if (!h->io_read(h, buf + (*buf_size), block_size, &read_size)) {
+ return(false);
+ }
+
+ if (read_size == 0) {
+ h->io_flags |= DD_END_OF_FILE;
+ break;
+ }
+
+ DEBUG(6, ("added %llu bytes to IO buffer (need %llu bytes)\n",
+ (unsigned long long)read_size,
+ (unsigned long long)need_size));
+
+ *buf_size += read_size;
+ dd_stats.in.bytes += read_size;
+
+ if (read_size == block_size) {
+ dd_stats.in.fblocks++;
+ } else {
+ DEBUG(3, ("partial read of %llu bytes (expected %llu)\n",
+ (unsigned long long)read_size,
+ (unsigned long long)block_size));
+ dd_stats.in.pblocks++;
+ }
+ }
+
+ return(true);
+}
+
+/* Flush a buffer that contains buf_size bytes. Use writes of block_size to do it,
+ * and shift any remaining bytes back to the head of the buffer when there are
+ * no more block_size sized IOs left.
+ */
+bool dd_flush_block(struct dd_iohandle * h,
+ uint8_t * buf,
+ uint64_t * buf_size,
+ uint64_t block_size)
+{
+ uint64_t write_size;
+ uint64_t total_size = 0;
+
+ SMB_ASSERT(block_size > 0);
+
+ /* We have explicitly been asked to write a partial block. */
+ if ((*buf_size) < block_size) {
+
+ if (!h->io_write(h, buf, *buf_size, &write_size)) {
+ return(false);
+ }
+
+ if (write_size == 0) {
+ fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
+ PROGNAME);
+ return(false);
+ }
+
+ total_size += write_size;
+ dd_stats.out.bytes += write_size;
+ dd_stats.out.pblocks++;
+ }
+
+ /* Write as many full blocks as there are in the buffer. */
+ while (((*buf_size) - total_size) >= block_size) {
+
+ if (!h->io_write(h, buf + total_size, block_size, &write_size)) {
+ return(false);
+ }
+
+ if (write_size == 0) {
+ fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
+ PROGNAME);
+ return(false);
+ }
+
+ if (write_size == block_size) {
+ dd_stats.out.fblocks++;
+ } else {
+ dd_stats.out.pblocks++;
+ }
+
+ total_size += write_size;
+ dd_stats.out.bytes += write_size;
+
+ DEBUG(6, ("flushed %llu bytes from IO buffer of %llu bytes (%llu remain)\n",
+ (unsigned long long)block_size,
+ (unsigned long long)block_size,
+ (unsigned long long)(block_size - total_size)));
+ }
+
+ SMB_ASSERT(total_size > 0);
+
+ /* We have flushed as much of the IO buffer as we can while
+ * still doing block_size'd operations. Shift any remaining data
+ * to the front of the IO buffer.
+ */
+ if ((*buf_size) > total_size) {
+ uint64_t remain = (*buf_size) - total_size;
+
+ DEBUG(3, ("shifting %llu remainder bytes to IO buffer head\n",
+ (unsigned long long)remain));
+
+ memmove(buf, buf + total_size, remain);
+ (*buf_size) = remain;
+ } else if ((*buf_size) == total_size) {
+ (*buf_size) = 0;
+ } else {
+ /* Else buffer contains buf_size bytes that we will append
+ * to next time round.
+ */
+ DEBUG(3, ("%llu unflushed bytes left in IO buffer\n",
+ (unsigned long long)(*buf_size)));
+ }
+
+ return(true);
+}
+
+/* vim: set sw=8 sts=8 ts=8 tw=79 : */
diff --git a/source4/client/client.c b/source4/client/client.c
new file mode 100644
index 0000000000..e05e195372
--- /dev/null
+++ b/source4/client/client.c
@@ -0,0 +1,3273 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Simo Sorce 2001-2002
+ Copyright (C) Jelmer Vernooij 2003-2004
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * TODO: remove this ... and don't use talloc_append_string()
+ *
+ * NOTE: I'm not changing the code yet, because I assume there're
+ * some bugs in the existing code and I'm not sure how to fix
+ * them correctly.
+ */
+#define TALLOC_DEPRECATED 1
+
+#include "includes.h"
+#include "version.h"
+#include "libcli/libcli.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/util/clilsa.h"
+#include "system/dir.h"
+#include "system/filesys.h"
+#include "lib/util/dlinklist.h"
+#include "system/readline.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "system/time.h" /* needed by some systems for asctime() */
+#include "libcli/resolve/resolve.h"
+#include "libcli/security/security.h"
+#include "lib/smbreadline/smbreadline.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc.h"
+
+struct smbclient_context {
+ char *remote_cur_dir;
+ struct smbcli_state *cli;
+ char *fileselection;
+ time_t newer_than;
+ bool prompt;
+ bool recurse;
+ int archive_level;
+ bool lowercase;
+ int printmode;
+ bool translation;
+ int io_bufsize;
+};
+
+/* timing globals */
+static uint64_t get_total_size = 0;
+static uint_t get_total_time_ms = 0;
+static uint64_t put_total_size = 0;
+static uint_t put_total_time_ms = 0;
+
+/* Unfortunately, there is no way to pass the a context to the completion function as an argument */
+static struct smbclient_context *rl_ctx;
+
+/* totals globals */
+static double dir_total;
+
+/*******************************************************************
+ Reduce a file name, removing .. elements.
+********************************************************************/
+static void dos_clean_name(char *s)
+{
+ char *p=NULL,*r;
+
+ DEBUG(3,("dos_clean_name [%s]\n",s));
+
+ /* remove any double slashes */
+ all_string_sub(s, "\\\\", "\\", 0);
+
+ while ((p = strstr(s,"\\..\\")) != NULL) {
+ *p = '\0';
+ if ((r = strrchr(s,'\\')) != NULL)
+ memmove(r,p+3,strlen(p+3)+1);
+ }
+
+ trim_string(s,NULL,"\\..");
+
+ all_string_sub(s, "\\.\\", "\\", 0);
+}
+
+/****************************************************************************
+write to a local file with CR/LF->LF translation if appropriate. return the
+number taken from the buffer. This may not equal the number written.
+****************************************************************************/
+static int writefile(int f, const void *_b, int n, bool translation)
+{
+ const uint8_t *b = (const uint8_t *)_b;
+ int i;
+
+ if (!translation) {
+ return write(f,b,n);
+ }
+
+ i = 0;
+ while (i < n) {
+ if (*b == '\r' && (i<(n-1)) && *(b+1) == '\n') {
+ b++;i++;
+ }
+ if (write(f, b, 1) != 1) {
+ break;
+ }
+ b++;
+ i++;
+ }
+
+ return(i);
+}
+
+/****************************************************************************
+ read from a file with LF->CR/LF translation if appropriate. return the
+ number read. read approx n bytes.
+****************************************************************************/
+static int readfile(void *_b, int n, XFILE *f, bool translation)
+{
+ uint8_t *b = (uint8_t *)_b;
+ int i;
+ int c;
+
+ if (!translation)
+ return x_fread(b,1,n,f);
+
+ i = 0;
+ while (i < (n - 1)) {
+ if ((c = x_getc(f)) == EOF) {
+ break;
+ }
+
+ if (c == '\n') { /* change all LFs to CR/LF */
+ b[i++] = '\r';
+ }
+
+ b[i++] = c;
+ }
+
+ return(i);
+}
+
+
+/****************************************************************************
+send a message
+****************************************************************************/
+static void send_message(struct smbcli_state *cli, const char *desthost)
+{
+ char msg[1600];
+ int total_len = 0;
+ int grp_id;
+
+ if (!smbcli_message_start(cli->tree, desthost, cli_credentials_get_username(cmdline_credentials), &grp_id)) {
+ d_printf("message start: %s\n", smbcli_errstr(cli->tree));
+ return;
+ }
+
+
+ d_printf("Connected. Type your message, ending it with a Control-D\n");
+
+ while (!feof(stdin) && total_len < 1600) {
+ int maxlen = MIN(1600 - total_len,127);
+ int l=0;
+ int c;
+
+ for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++) {
+ if (c == '\n')
+ msg[l++] = '\r';
+ msg[l] = c;
+ }
+
+ if (!smbcli_message_text(cli->tree, msg, l, grp_id)) {
+ d_printf("SMBsendtxt failed (%s)\n",smbcli_errstr(cli->tree));
+ return;
+ }
+
+ total_len += l;
+ }
+
+ if (total_len >= 1600)
+ d_printf("the message was truncated to 1600 bytes\n");
+ else
+ d_printf("sent %d bytes\n",total_len);
+
+ if (!smbcli_message_end(cli->tree, grp_id)) {
+ d_printf("SMBsendend failed (%s)\n",smbcli_errstr(cli->tree));
+ return;
+ }
+}
+
+
+
+/****************************************************************************
+check the space on a device
+****************************************************************************/
+static int do_dskattr(struct smbclient_context *ctx)
+{
+ uint32_t bsize;
+ uint64_t total, avail;
+
+ if (NT_STATUS_IS_ERR(smbcli_dskattr(ctx->cli->tree, &bsize, &total, &avail))) {
+ d_printf("Error in dskattr: %s\n",smbcli_errstr(ctx->cli->tree));
+ return 1;
+ }
+
+ d_printf("\n\t\t%llu blocks of size %u. %llu blocks available\n",
+ (unsigned long long)total,
+ (unsigned)bsize,
+ (unsigned long long)avail);
+
+ return 0;
+}
+
+/****************************************************************************
+show cd/pwd
+****************************************************************************/
+static int cmd_pwd(struct smbclient_context *ctx, const char **args)
+{
+ d_printf("Current directory is %s\n", ctx->remote_cur_dir);
+ return 0;
+}
+
+/*
+ convert a string to dos format
+*/
+static void dos_format(char *s)
+{
+ string_replace(s, '/', '\\');
+}
+
+/****************************************************************************
+change directory - inner section
+****************************************************************************/
+static int do_cd(struct smbclient_context *ctx, const char *newdir)
+{
+ char *dname;
+
+ /* Save the current directory in case the
+ new directory is invalid */
+ if (newdir[0] == '\\')
+ dname = talloc_strdup(NULL, newdir);
+ else
+ dname = talloc_asprintf(NULL, "%s\\%s", ctx->remote_cur_dir, newdir);
+
+ dos_format(dname);
+
+ if (*(dname+strlen(dname)-1) != '\\') {
+ dname = talloc_append_string(NULL, dname, "\\");
+ }
+ dos_clean_name(dname);
+
+ if (NT_STATUS_IS_ERR(smbcli_chkpath(ctx->cli->tree, dname))) {
+ d_printf("cd %s: %s\n", dname, smbcli_errstr(ctx->cli->tree));
+ talloc_free(dname);
+ } else {
+ ctx->remote_cur_dir = dname;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+change directory
+****************************************************************************/
+static int cmd_cd(struct smbclient_context *ctx, const char **args)
+{
+ int rc = 0;
+
+ if (args[1])
+ rc = do_cd(ctx, args[1]);
+ else
+ d_printf("Current directory is %s\n",ctx->remote_cur_dir);
+
+ return rc;
+}
+
+
+static bool mask_match(struct smbcli_state *c, const char *string,
+ const char *pattern, bool is_case_sensitive)
+{
+ char *p2, *s2;
+ bool ret;
+
+ if (ISDOTDOT(string))
+ string = ".";
+ if (ISDOT(pattern))
+ return false;
+
+ if (is_case_sensitive)
+ return ms_fnmatch(pattern, string,
+ c->transport->negotiate.protocol) == 0;
+
+ p2 = strlower_talloc(NULL, pattern);
+ s2 = strlower_talloc(NULL, string);
+ ret = ms_fnmatch(p2, s2, c->transport->negotiate.protocol) == 0;
+ talloc_free(p2);
+ talloc_free(s2);
+
+ return ret;
+}
+
+
+
+/*******************************************************************
+ decide if a file should be operated on
+ ********************************************************************/
+static bool do_this_one(struct smbclient_context *ctx, struct clilist_file_info *finfo)
+{
+ if (finfo->attrib & FILE_ATTRIBUTE_DIRECTORY) return(true);
+
+ if (ctx->fileselection &&
+ !mask_match(ctx->cli, finfo->name,ctx->fileselection,false)) {
+ DEBUG(3,("mask_match %s failed\n", finfo->name));
+ return false;
+ }
+
+ if (ctx->newer_than && finfo->mtime < ctx->newer_than) {
+ DEBUG(3,("newer_than %s failed\n", finfo->name));
+ return(false);
+ }
+
+ if ((ctx->archive_level==1 || ctx->archive_level==2) && !(finfo->attrib & FILE_ATTRIBUTE_ARCHIVE)) {
+ DEBUG(3,("archive %s failed\n", finfo->name));
+ return(false);
+ }
+
+ return(true);
+}
+
+/****************************************************************************
+ display info about a file
+ ****************************************************************************/
+static void display_finfo(struct smbclient_context *ctx, struct clilist_file_info *finfo)
+{
+ if (do_this_one(ctx, finfo)) {
+ time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */
+ char *astr = attrib_string(NULL, finfo->attrib);
+ d_printf(" %-30s%7.7s %8.0f %s",
+ finfo->name,
+ astr,
+ (double)finfo->size,
+ asctime(localtime(&t)));
+ dir_total += finfo->size;
+ talloc_free(astr);
+ }
+}
+
+
+/****************************************************************************
+ accumulate size of a file
+ ****************************************************************************/
+static void do_du(struct smbclient_context *ctx, struct clilist_file_info *finfo)
+{
+ if (do_this_one(ctx, finfo)) {
+ dir_total += finfo->size;
+ }
+}
+
+static bool do_list_recurse;
+static bool do_list_dirs;
+static char *do_list_queue = 0;
+static long do_list_queue_size = 0;
+static long do_list_queue_start = 0;
+static long do_list_queue_end = 0;
+static void (*do_list_fn)(struct smbclient_context *, struct clilist_file_info *);
+
+/****************************************************************************
+functions for do_list_queue
+ ****************************************************************************/
+
+/*
+ * The do_list_queue is a NUL-separated list of strings stored in a
+ * char*. Since this is a FIFO, we keep track of the beginning and
+ * ending locations of the data in the queue. When we overflow, we
+ * double the size of the char*. When the start of the data passes
+ * the midpoint, we move everything back. This is logically more
+ * complex than a linked list, but easier from a memory management
+ * angle. In any memory error condition, do_list_queue is reset.
+ * Functions check to ensure that do_list_queue is non-NULL before
+ * accessing it.
+ */
+static void reset_do_list_queue(void)
+{
+ SAFE_FREE(do_list_queue);
+ do_list_queue_size = 0;
+ do_list_queue_start = 0;
+ do_list_queue_end = 0;
+}
+
+static void init_do_list_queue(void)
+{
+ reset_do_list_queue();
+ do_list_queue_size = 1024;
+ do_list_queue = malloc_array_p(char, do_list_queue_size);
+ if (do_list_queue == 0) {
+ d_printf("malloc fail for size %d\n",
+ (int)do_list_queue_size);
+ reset_do_list_queue();
+ } else {
+ memset(do_list_queue, 0, do_list_queue_size);
+ }
+}
+
+static void adjust_do_list_queue(void)
+{
+ if (do_list_queue == NULL) return;
+
+ /*
+ * If the starting point of the queue is more than half way through,
+ * move everything toward the beginning.
+ */
+ if (do_list_queue_start == do_list_queue_end)
+ {
+ DEBUG(4,("do_list_queue is empty\n"));
+ do_list_queue_start = do_list_queue_end = 0;
+ *do_list_queue = '\0';
+ }
+ else if (do_list_queue_start > (do_list_queue_size / 2))
+ {
+ DEBUG(4,("sliding do_list_queue backward\n"));
+ memmove(do_list_queue,
+ do_list_queue + do_list_queue_start,
+ do_list_queue_end - do_list_queue_start);
+ do_list_queue_end -= do_list_queue_start;
+ do_list_queue_start = 0;
+ }
+
+}
+
+static void add_to_do_list_queue(const char* entry)
+{
+ char *dlq;
+ long new_end = do_list_queue_end + ((long)strlen(entry)) + 1;
+ while (new_end > do_list_queue_size)
+ {
+ do_list_queue_size *= 2;
+ DEBUG(4,("enlarging do_list_queue to %d\n",
+ (int)do_list_queue_size));
+ dlq = realloc_p(do_list_queue, char, do_list_queue_size);
+ if (! dlq) {
+ d_printf("failure enlarging do_list_queue to %d bytes\n",
+ (int)do_list_queue_size);
+ reset_do_list_queue();
+ }
+ else
+ {
+ do_list_queue = dlq;
+ memset(do_list_queue + do_list_queue_size / 2,
+ 0, do_list_queue_size / 2);
+ }
+ }
+ if (do_list_queue)
+ {
+ safe_strcpy(do_list_queue + do_list_queue_end, entry,
+ do_list_queue_size - do_list_queue_end - 1);
+ do_list_queue_end = new_end;
+ DEBUG(4,("added %s to do_list_queue (start=%d, end=%d)\n",
+ entry, (int)do_list_queue_start, (int)do_list_queue_end));
+ }
+}
+
+static char *do_list_queue_head(void)
+{
+ return do_list_queue + do_list_queue_start;
+}
+
+static void remove_do_list_queue_head(void)
+{
+ if (do_list_queue_end > do_list_queue_start)
+ {
+ do_list_queue_start += strlen(do_list_queue_head()) + 1;
+ adjust_do_list_queue();
+ DEBUG(4,("removed head of do_list_queue (start=%d, end=%d)\n",
+ (int)do_list_queue_start, (int)do_list_queue_end));
+ }
+}
+
+static int do_list_queue_empty(void)
+{
+ return (! (do_list_queue && *do_list_queue));
+}
+
+/****************************************************************************
+a helper for do_list
+ ****************************************************************************/
+static void do_list_helper(struct clilist_file_info *f, const char *mask, void *state)
+{
+ struct smbclient_context *ctx = (struct smbclient_context *)state;
+
+ if (f->attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ if (do_list_dirs && do_this_one(ctx, f)) {
+ do_list_fn(ctx, f);
+ }
+ if (do_list_recurse &&
+ !ISDOT(f->name) &&
+ !ISDOTDOT(f->name)) {
+ char *mask2;
+ char *p;
+
+ mask2 = talloc_strdup(NULL, mask);
+ p = strrchr_m(mask2,'\\');
+ if (!p) return;
+ p[1] = 0;
+ mask2 = talloc_asprintf_append_buffer(mask2, "%s\\*", f->name);
+ add_to_do_list_queue(mask2);
+ }
+ return;
+ }
+
+ if (do_this_one(ctx, f)) {
+ do_list_fn(ctx, f);
+ }
+}
+
+
+/****************************************************************************
+a wrapper around smbcli_list that adds recursion
+ ****************************************************************************/
+static void do_list(struct smbclient_context *ctx, const char *mask,uint16_t attribute,
+ void (*fn)(struct smbclient_context *, struct clilist_file_info *),bool rec, bool dirs)
+{
+ static int in_do_list = 0;
+
+ if (in_do_list && rec)
+ {
+ fprintf(stderr, "INTERNAL ERROR: do_list called recursively when the recursive flag is true\n");
+ exit(1);
+ }
+
+ in_do_list = 1;
+
+ do_list_recurse = rec;
+ do_list_dirs = dirs;
+ do_list_fn = fn;
+
+ if (rec)
+ {
+ init_do_list_queue();
+ add_to_do_list_queue(mask);
+
+ while (! do_list_queue_empty())
+ {
+ /*
+ * Need to copy head so that it doesn't become
+ * invalid inside the call to smbcli_list. This
+ * would happen if the list were expanded
+ * during the call.
+ * Fix from E. Jay Berkenbilt (ejb@ql.org)
+ */
+ char *head;
+ head = do_list_queue_head();
+ smbcli_list(ctx->cli->tree, head, attribute, do_list_helper, ctx);
+ remove_do_list_queue_head();
+ if ((! do_list_queue_empty()) && (fn == display_finfo))
+ {
+ char* next_file = do_list_queue_head();
+ char* save_ch = 0;
+ if ((strlen(next_file) >= 2) &&
+ (next_file[strlen(next_file) - 1] == '*') &&
+ (next_file[strlen(next_file) - 2] == '\\'))
+ {
+ save_ch = next_file +
+ strlen(next_file) - 2;
+ *save_ch = '\0';
+ }
+ d_printf("\n%s\n",next_file);
+ if (save_ch)
+ {
+ *save_ch = '\\';
+ }
+ }
+ }
+ }
+ else
+ {
+ if (smbcli_list(ctx->cli->tree, mask, attribute, do_list_helper, ctx) == -1)
+ {
+ d_printf("%s listing %s\n", smbcli_errstr(ctx->cli->tree), mask);
+ }
+ }
+
+ in_do_list = 0;
+ reset_do_list_queue();
+}
+
+/****************************************************************************
+ get a directory listing
+ ****************************************************************************/
+static int cmd_dir(struct smbclient_context *ctx, const char **args)
+{
+ uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ char *mask;
+ int rc;
+
+ dir_total = 0;
+
+ mask = talloc_strdup(ctx, ctx->remote_cur_dir);
+ if(mask[strlen(mask)-1]!='\\')
+ mask = talloc_append_string(ctx, mask,"\\");
+
+ if (args[1]) {
+ mask = talloc_strdup(ctx, args[1]);
+ if (mask[0] != '\\')
+ mask = talloc_append_string(ctx, mask, "\\");
+ dos_format(mask);
+ }
+ else {
+ if (ctx->cli->tree->session->transport->negotiate.protocol <=
+ PROTOCOL_LANMAN1) {
+ mask = talloc_append_string(ctx, mask, "*.*");
+ } else {
+ mask = talloc_append_string(ctx, mask, "*");
+ }
+ }
+
+ do_list(ctx, mask, attribute, display_finfo, ctx->recurse, true);
+
+ rc = do_dskattr(ctx);
+
+ DEBUG(3, ("Total bytes listed: %.0f\n", dir_total));
+
+ return rc;
+}
+
+
+/****************************************************************************
+ get a directory listing
+ ****************************************************************************/
+static int cmd_du(struct smbclient_context *ctx, const char **args)
+{
+ uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ int rc;
+ char *mask;
+
+ dir_total = 0;
+
+ if (args[1]) {
+ if (args[1][0] == '\\')
+ mask = talloc_strdup(ctx, args[1]);
+ else
+ mask = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
+ dos_format(mask);
+ } else {
+ mask = talloc_asprintf(ctx, "%s\\*", ctx->remote_cur_dir);
+ }
+
+ do_list(ctx, mask, attribute, do_du, ctx->recurse, true);
+
+ talloc_free(mask);
+
+ rc = do_dskattr(ctx);
+
+ d_printf("Total number of bytes: %.0f\n", dir_total);
+
+ return rc;
+}
+
+
+/****************************************************************************
+ get a file from rname to lname
+ ****************************************************************************/
+static int do_get(struct smbclient_context *ctx, char *rname, const char *lname, bool reget)
+{
+ int handle = 0, fnum;
+ bool newhandle = false;
+ uint8_t *data;
+ struct timeval tp_start;
+ int read_size = ctx->io_bufsize;
+ uint16_t attr;
+ size_t size;
+ off_t start = 0;
+ off_t nread = 0;
+ int rc = 0;
+
+ GetTimeOfDay(&tp_start);
+
+ if (ctx->lowercase) {
+ strlower(discard_const_p(char, lname));
+ }
+
+ fnum = smbcli_open(ctx->cli->tree, rname, O_RDONLY, DENY_NONE);
+
+ if (fnum == -1) {
+ d_printf("%s opening remote file %s\n",smbcli_errstr(ctx->cli->tree),rname);
+ return 1;
+ }
+
+ if(!strcmp(lname,"-")) {
+ handle = fileno(stdout);
+ } else {
+ if (reget) {
+ handle = open(lname, O_WRONLY|O_CREAT, 0644);
+ if (handle >= 0) {
+ start = lseek(handle, 0, SEEK_END);
+ if (start == -1) {
+ d_printf("Error seeking local file\n");
+ return 1;
+ }
+ }
+ } else {
+ handle = open(lname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ }
+ newhandle = true;
+ }
+ if (handle < 0) {
+ d_printf("Error opening local file %s\n",lname);
+ return 1;
+ }
+
+
+ if (NT_STATUS_IS_ERR(smbcli_qfileinfo(ctx->cli->tree, fnum,
+ &attr, &size, NULL, NULL, NULL, NULL, NULL)) &&
+ NT_STATUS_IS_ERR(smbcli_getattrE(ctx->cli->tree, fnum,
+ &attr, &size, NULL, NULL, NULL))) {
+ d_printf("getattrib: %s\n",smbcli_errstr(ctx->cli->tree));
+ return 1;
+ }
+
+ DEBUG(2,("getting file %s of size %.0f as %s ",
+ rname, (double)size, lname));
+
+ if(!(data = (uint8_t *)malloc(read_size))) {
+ d_printf("malloc fail for size %d\n", read_size);
+ smbcli_close(ctx->cli->tree, fnum);
+ return 1;
+ }
+
+ while (1) {
+ int n = smbcli_read(ctx->cli->tree, fnum, data, nread + start, read_size);
+
+ if (n <= 0) break;
+
+ if (writefile(handle,data, n, ctx->translation) != n) {
+ d_printf("Error writing local file\n");
+ rc = 1;
+ break;
+ }
+
+ nread += n;
+ }
+
+ if (nread + start < size) {
+ DEBUG (0, ("Short read when getting file %s. Only got %ld bytes.\n",
+ rname, (long)nread));
+
+ rc = 1;
+ }
+
+ SAFE_FREE(data);
+
+ if (NT_STATUS_IS_ERR(smbcli_close(ctx->cli->tree, fnum))) {
+ d_printf("Error %s closing remote file\n",smbcli_errstr(ctx->cli->tree));
+ rc = 1;
+ }
+
+ if (newhandle) {
+ close(handle);
+ }
+
+ if (ctx->archive_level >= 2 && (attr & FILE_ATTRIBUTE_ARCHIVE)) {
+ smbcli_setatr(ctx->cli->tree, rname, attr & ~(uint16_t)FILE_ATTRIBUTE_ARCHIVE, 0);
+ }
+
+ {
+ struct timeval tp_end;
+ int this_time;
+
+ GetTimeOfDay(&tp_end);
+ this_time =
+ (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+ (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ get_total_time_ms += this_time;
+ get_total_size += nread;
+
+ DEBUG(2,("(%3.1f kb/s) (average %3.1f kb/s)\n",
+ nread / (1.024*this_time + 1.0e-4),
+ get_total_size / (1.024*get_total_time_ms)));
+ }
+
+ return rc;
+}
+
+
+/****************************************************************************
+ get a file
+ ****************************************************************************/
+static int cmd_get(struct smbclient_context *ctx, const char **args)
+{
+ const char *lname;
+ char *rname;
+
+ if (!args[1]) {
+ d_printf("get <filename>\n");
+ return 1;
+ }
+
+ rname = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
+
+ if (args[2])
+ lname = args[2];
+ else
+ lname = args[1];
+
+ dos_clean_name(rname);
+
+ return do_get(ctx, rname, lname, false);
+}
+
+/****************************************************************************
+ Put up a yes/no prompt.
+****************************************************************************/
+static bool yesno(char *p)
+{
+ char ans[4];
+ printf("%s",p);
+
+ if (!fgets(ans,sizeof(ans)-1,stdin))
+ return(false);
+
+ if (*ans == 'y' || *ans == 'Y')
+ return(true);
+
+ return(false);
+}
+
+/****************************************************************************
+ do a mget operation on one file
+ ****************************************************************************/
+static void do_mget(struct smbclient_context *ctx, struct clilist_file_info *finfo)
+{
+ char *rname;
+ char *quest;
+ char *mget_mask;
+ char *saved_curdir;
+
+ if (ISDOT(finfo->name) || ISDOTDOT(finfo->name))
+ return;
+
+ if (finfo->attrib & FILE_ATTRIBUTE_DIRECTORY)
+ asprintf(&quest, "Get directory %s? ",finfo->name);
+ else
+ asprintf(&quest, "Get file %s? ",finfo->name);
+
+ if (ctx->prompt && !yesno(quest)) return;
+
+ SAFE_FREE(quest);
+
+ if (!(finfo->attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ asprintf(&rname, "%s%s",ctx->remote_cur_dir,finfo->name);
+ do_get(ctx, rname, finfo->name, false);
+ SAFE_FREE(rname);
+ return;
+ }
+
+ /* handle directories */
+ saved_curdir = talloc_strdup(NULL, ctx->remote_cur_dir);
+
+ ctx->remote_cur_dir = talloc_asprintf_append_buffer(NULL, "%s\\", finfo->name);
+
+ string_replace(discard_const_p(char, finfo->name), '\\', '/');
+ if (ctx->lowercase) {
+ strlower(discard_const_p(char, finfo->name));
+ }
+
+ if (!directory_exist(finfo->name) &&
+ mkdir(finfo->name,0777) != 0) {
+ d_printf("failed to create directory %s\n",finfo->name);
+ return;
+ }
+
+ if (chdir(finfo->name) != 0) {
+ d_printf("failed to chdir to directory %s\n",finfo->name);
+ return;
+ }
+
+ mget_mask = talloc_asprintf(NULL, "%s*", ctx->remote_cur_dir);
+
+ do_list(ctx, mget_mask, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY,do_mget,false, true);
+ chdir("..");
+ talloc_free(ctx->remote_cur_dir);
+
+ ctx->remote_cur_dir = saved_curdir;
+}
+
+
+/****************************************************************************
+view the file using the pager
+****************************************************************************/
+static int cmd_more(struct smbclient_context *ctx, const char **args)
+{
+ char *rname;
+ char *pager_cmd;
+ char *lname;
+ char *pager;
+ int fd;
+ int rc = 0;
+
+ lname = talloc_asprintf(ctx, "%s/smbmore.XXXXXX",tmpdir());
+ fd = mkstemp(lname);
+ if (fd == -1) {
+ d_printf("failed to create temporary file for more\n");
+ return 1;
+ }
+ close(fd);
+
+ if (!args[1]) {
+ d_printf("more <filename>\n");
+ unlink(lname);
+ return 1;
+ }
+ rname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+ dos_clean_name(rname);
+
+ rc = do_get(ctx, rname, lname, false);
+
+ pager=getenv("PAGER");
+
+ pager_cmd = talloc_asprintf(ctx, "%s %s",(pager? pager:PAGER), lname);
+ system(pager_cmd);
+ unlink(lname);
+
+ return rc;
+}
+
+
+
+/****************************************************************************
+do a mget command
+****************************************************************************/
+static int cmd_mget(struct smbclient_context *ctx, const char **args)
+{
+ uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ char *mget_mask = NULL;
+ int i;
+
+ if (ctx->recurse)
+ attribute |= FILE_ATTRIBUTE_DIRECTORY;
+
+ for (i = 1; args[i]; i++) {
+ mget_mask = talloc_strdup(ctx,ctx->remote_cur_dir);
+ if(mget_mask[strlen(mget_mask)-1]!='\\')
+ mget_mask = talloc_append_string(ctx, mget_mask, "\\");
+
+ mget_mask = talloc_strdup(ctx, args[i]);
+ if (mget_mask[0] != '\\')
+ mget_mask = talloc_append_string(ctx, mget_mask, "\\");
+ do_list(ctx, mget_mask, attribute,do_mget,false,true);
+
+ talloc_free(mget_mask);
+ }
+
+ if (mget_mask == NULL) {
+ mget_mask = talloc_asprintf(ctx, "%s\\*", ctx->remote_cur_dir);
+ do_list(ctx, mget_mask, attribute,do_mget,false,true);
+ talloc_free(mget_mask);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+make a directory of name "name"
+****************************************************************************/
+static NTSTATUS do_mkdir(struct smbclient_context *ctx, char *name)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(status = smbcli_mkdir(ctx->cli->tree, name))) {
+ d_printf("%s making remote directory %s\n",
+ smbcli_errstr(ctx->cli->tree),name);
+ return status;
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+ Exit client.
+****************************************************************************/
+static int cmd_quit(struct smbclient_context *ctx, const char **args)
+{
+ talloc_free(ctx);
+ exit(0);
+ /* NOTREACHED */
+ return 0;
+}
+
+
+/****************************************************************************
+ make a directory
+ ****************************************************************************/
+static int cmd_mkdir(struct smbclient_context *ctx, const char **args)
+{
+ char *mask, *p;
+
+ if (!args[1]) {
+ if (!ctx->recurse)
+ d_printf("mkdir <dirname>\n");
+ return 1;
+ }
+
+ mask = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir,args[1]);
+
+ if (ctx->recurse) {
+ dos_clean_name(mask);
+
+ trim_string(mask,".",NULL);
+ for (p = strtok(mask,"/\\"); p; p = strtok(p, "/\\")) {
+ char *parent = talloc_strndup(ctx, mask, PTR_DIFF(p, mask));
+
+ if (NT_STATUS_IS_ERR(smbcli_chkpath(ctx->cli->tree, parent))) {
+ do_mkdir(ctx, parent);
+ }
+
+ talloc_free(parent);
+ }
+ } else {
+ do_mkdir(ctx, mask);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+show 8.3 name of a file
+****************************************************************************/
+static int cmd_altname(struct smbclient_context *ctx, const char **args)
+{
+ const char *altname;
+ char *name;
+
+ if (!args[1]) {
+ d_printf("altname <file>\n");
+ return 1;
+ }
+
+ name = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ if (!NT_STATUS_IS_OK(smbcli_qpathinfo_alt_name(ctx->cli->tree, name, &altname))) {
+ d_printf("%s getting alt name for %s\n",
+ smbcli_errstr(ctx->cli->tree),name);
+ return(false);
+ }
+ d_printf("%s\n", altname);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ put a single file
+ ****************************************************************************/
+static int do_put(struct smbclient_context *ctx, char *rname, char *lname, bool reput)
+{
+ int fnum;
+ XFILE *f;
+ size_t start = 0;
+ off_t nread = 0;
+ uint8_t *buf = NULL;
+ int maxwrite = ctx->io_bufsize;
+ int rc = 0;
+
+ struct timeval tp_start;
+ GetTimeOfDay(&tp_start);
+
+ if (reput) {
+ fnum = smbcli_open(ctx->cli->tree, rname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum >= 0) {
+ if (NT_STATUS_IS_ERR(smbcli_qfileinfo(ctx->cli->tree, fnum, NULL, &start, NULL, NULL, NULL, NULL, NULL)) &&
+ NT_STATUS_IS_ERR(smbcli_getattrE(ctx->cli->tree, fnum, NULL, &start, NULL, NULL, NULL))) {
+ d_printf("getattrib: %s\n",smbcli_errstr(ctx->cli->tree));
+ return 1;
+ }
+ }
+ } else {
+ fnum = smbcli_open(ctx->cli->tree, rname, O_RDWR|O_CREAT|O_TRUNC,
+ DENY_NONE);
+ }
+
+ if (fnum == -1) {
+ d_printf("%s opening remote file %s\n",smbcli_errstr(ctx->cli->tree),rname);
+ return 1;
+ }
+
+ /* allow files to be piped into smbclient
+ jdblair 24.jun.98
+
+ Note that in this case this function will exit(0) rather
+ than returning. */
+ if (!strcmp(lname, "-")) {
+ f = x_stdin;
+ /* size of file is not known */
+ } else {
+ f = x_fopen(lname,O_RDONLY, 0);
+ if (f && reput) {
+ if (x_tseek(f, start, SEEK_SET) == -1) {
+ d_printf("Error seeking local file\n");
+ return 1;
+ }
+ }
+ }
+
+ if (!f) {
+ d_printf("Error opening local file %s\n",lname);
+ return 1;
+ }
+
+
+ DEBUG(1,("putting file %s as %s ",lname,
+ rname));
+
+ buf = (uint8_t *)malloc(maxwrite);
+ if (!buf) {
+ d_printf("ERROR: Not enough memory!\n");
+ return 1;
+ }
+ while (!x_feof(f)) {
+ int n = maxwrite;
+ int ret;
+
+ if ((n = readfile(buf,n,f,ctx->translation)) < 1) {
+ if((n == 0) && x_feof(f))
+ break; /* Empty local file. */
+
+ d_printf("Error reading local file: %s\n", strerror(errno));
+ rc = 1;
+ break;
+ }
+
+ ret = smbcli_write(ctx->cli->tree, fnum, 0, buf, nread + start, n);
+
+ if (n != ret) {
+ d_printf("Error writing file: %s\n", smbcli_errstr(ctx->cli->tree));
+ rc = 1;
+ break;
+ }
+
+ nread += n;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_close(ctx->cli->tree, fnum))) {
+ d_printf("%s closing remote file %s\n",smbcli_errstr(ctx->cli->tree),rname);
+ x_fclose(f);
+ SAFE_FREE(buf);
+ return 1;
+ }
+
+
+ if (f != x_stdin) {
+ x_fclose(f);
+ }
+
+ SAFE_FREE(buf);
+
+ {
+ struct timeval tp_end;
+ int this_time;
+
+ GetTimeOfDay(&tp_end);
+ this_time =
+ (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+ (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ put_total_time_ms += this_time;
+ put_total_size += nread;
+
+ DEBUG(1,("(%3.1f kb/s) (average %3.1f kb/s)\n",
+ nread / (1.024*this_time + 1.0e-4),
+ put_total_size / (1.024*put_total_time_ms)));
+ }
+
+ if (f == x_stdin) {
+ talloc_free(ctx);
+ exit(0);
+ }
+
+ return rc;
+}
+
+
+
+/****************************************************************************
+ put a file
+ ****************************************************************************/
+static int cmd_put(struct smbclient_context *ctx, const char **args)
+{
+ char *lname;
+ char *rname;
+
+ if (!args[1]) {
+ d_printf("put <filename> [<remotename>]\n");
+ return 1;
+ }
+
+ lname = talloc_strdup(ctx, args[1]);
+
+ if (args[2])
+ rname = talloc_strdup(ctx, args[2]);
+ else
+ rname = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, lname);
+
+ dos_clean_name(rname);
+
+ /* allow '-' to represent stdin
+ jdblair, 24.jun.98 */
+ if (!file_exist(lname) && (strcmp(lname,"-"))) {
+ d_printf("%s does not exist\n",lname);
+ return 1;
+ }
+
+ return do_put(ctx, rname, lname, false);
+}
+
+/*************************************
+ File list structure
+*************************************/
+
+static struct file_list {
+ struct file_list *prev, *next;
+ char *file_path;
+ bool isdir;
+} *file_list;
+
+/****************************************************************************
+ Free a file_list structure
+****************************************************************************/
+
+static void free_file_list (struct file_list * list)
+{
+ struct file_list *tmp;
+
+ while (list)
+ {
+ tmp = list;
+ DLIST_REMOVE(list, list);
+ SAFE_FREE(tmp->file_path);
+ SAFE_FREE(tmp);
+ }
+}
+
+/****************************************************************************
+ seek in a directory/file list until you get something that doesn't start with
+ the specified name
+ ****************************************************************************/
+static bool seek_list(struct file_list *list, char *name)
+{
+ while (list) {
+ trim_string(list->file_path,"./","\n");
+ if (strncmp(list->file_path, name, strlen(name)) != 0) {
+ return(true);
+ }
+ list = list->next;
+ }
+
+ return(false);
+}
+
+/****************************************************************************
+ set the file selection mask
+ ****************************************************************************/
+static int cmd_select(struct smbclient_context *ctx, const char **args)
+{
+ talloc_free(ctx->fileselection);
+ ctx->fileselection = talloc_strdup(NULL, args[1]);
+
+ return 0;
+}
+
+/*******************************************************************
+ A readdir wrapper which just returns the file name.
+ ********************************************************************/
+static const char *readdirname(DIR *p)
+{
+ struct dirent *ptr;
+ char *dname;
+
+ if (!p)
+ return(NULL);
+
+ ptr = (struct dirent *)readdir(p);
+ if (!ptr)
+ return(NULL);
+
+ dname = ptr->d_name;
+
+#ifdef NEXT2
+ if (telldir(p) < 0)
+ return(NULL);
+#endif
+
+#ifdef HAVE_BROKEN_READDIR
+ /* using /usr/ucb/cc is BAD */
+ dname = dname - 2;
+#endif
+
+ {
+ static char *buf;
+ int len = NAMLEN(ptr);
+ buf = talloc_strndup(NULL, dname, len);
+ dname = buf;
+ }
+
+ return(dname);
+}
+
+/****************************************************************************
+ Recursive file matching function act as find
+ match must be always set to true when calling this function
+****************************************************************************/
+static int file_find(struct smbclient_context *ctx, struct file_list **list, const char *directory,
+ const char *expression, bool match)
+{
+ DIR *dir;
+ struct file_list *entry;
+ struct stat statbuf;
+ int ret;
+ char *path;
+ bool isdir;
+ const char *dname;
+
+ dir = opendir(directory);
+ if (!dir) return -1;
+
+ while ((dname = readdirname(dir))) {
+ if (ISDOT(dname) || ISDOTDOT(dname)) {
+ continue;
+ }
+
+ if (asprintf(&path, "%s/%s", directory, dname) <= 0) {
+ continue;
+ }
+
+ isdir = false;
+ if (!match || !gen_fnmatch(expression, dname)) {
+ if (ctx->recurse) {
+ ret = stat(path, &statbuf);
+ if (ret == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ isdir = true;
+ ret = file_find(ctx, list, path, expression, false);
+ }
+ } else {
+ d_printf("file_find: cannot stat file %s\n", path);
+ }
+
+ if (ret == -1) {
+ SAFE_FREE(path);
+ closedir(dir);
+ return -1;
+ }
+ }
+ entry = malloc_p(struct file_list);
+ if (!entry) {
+ d_printf("Out of memory in file_find\n");
+ closedir(dir);
+ return -1;
+ }
+ entry->file_path = path;
+ entry->isdir = isdir;
+ DLIST_ADD(*list, entry);
+ } else {
+ SAFE_FREE(path);
+ }
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+/****************************************************************************
+ mput some files
+ ****************************************************************************/
+static int cmd_mput(struct smbclient_context *ctx, const char **args)
+{
+ int i;
+
+ for (i = 1; args[i]; i++) {
+ int ret;
+ struct file_list *temp_list;
+ char *quest, *lname, *rname;
+
+ printf("%s\n", args[i]);
+
+ file_list = NULL;
+
+ ret = file_find(ctx, &file_list, ".", args[i], true);
+ if (ret) {
+ free_file_list(file_list);
+ continue;
+ }
+
+ quest = NULL;
+ lname = NULL;
+ rname = NULL;
+
+ for (temp_list = file_list; temp_list;
+ temp_list = temp_list->next) {
+
+ SAFE_FREE(lname);
+ if (asprintf(&lname, "%s/", temp_list->file_path) <= 0)
+ continue;
+ trim_string(lname, "./", "/");
+
+ /* check if it's a directory */
+ if (temp_list->isdir) {
+ /* if (!recurse) continue; */
+
+ SAFE_FREE(quest);
+ if (asprintf(&quest, "Put directory %s? ", lname) < 0) break;
+ if (ctx->prompt && !yesno(quest)) { /* No */
+ /* Skip the directory */
+ lname[strlen(lname)-1] = '/';
+ if (!seek_list(temp_list, lname))
+ break;
+ } else { /* Yes */
+ SAFE_FREE(rname);
+ if(asprintf(&rname, "%s%s", ctx->remote_cur_dir, lname) < 0) break;
+ dos_format(rname);
+ if (NT_STATUS_IS_ERR(smbcli_chkpath(ctx->cli->tree, rname)) &&
+ NT_STATUS_IS_ERR(do_mkdir(ctx, rname))) {
+ DEBUG (0, ("Unable to make dir, skipping..."));
+ /* Skip the directory */
+ lname[strlen(lname)-1] = '/';
+ if (!seek_list(temp_list, lname))
+ break;
+ }
+ }
+ continue;
+ } else {
+ SAFE_FREE(quest);
+ if (asprintf(&quest,"Put file %s? ", lname) < 0) break;
+ if (ctx->prompt && !yesno(quest)) /* No */
+ continue;
+
+ /* Yes */
+ SAFE_FREE(rname);
+ if (asprintf(&rname, "%s%s", ctx->remote_cur_dir, lname) < 0) break;
+ }
+
+ dos_format(rname);
+
+ do_put(ctx, rname, lname, false);
+ }
+ free_file_list(file_list);
+ SAFE_FREE(quest);
+ SAFE_FREE(lname);
+ SAFE_FREE(rname);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+ print a file
+ ****************************************************************************/
+static int cmd_print(struct smbclient_context *ctx, const char **args)
+{
+ char *lname, *rname;
+ char *p;
+
+ if (!args[1]) {
+ d_printf("print <filename>\n");
+ return 1;
+ }
+
+ lname = talloc_strdup(ctx, args[1]);
+
+ rname = talloc_strdup(ctx, lname);
+ p = strrchr_m(rname,'/');
+ if (p) {
+ slprintf(rname, sizeof(rname)-1, "%s-%d", p+1, (int)getpid());
+ }
+
+ if (strequal(lname,"-")) {
+ slprintf(rname, sizeof(rname)-1, "stdin-%d", (int)getpid());
+ }
+
+ return do_put(ctx, rname, lname, false);
+}
+
+
+static int cmd_rewrite(struct smbclient_context *ctx, const char **args)
+{
+ d_printf("REWRITE: command not implemented (FIXME!)\n");
+
+ return 0;
+}
+
+/****************************************************************************
+delete some files
+****************************************************************************/
+static int cmd_del(struct smbclient_context *ctx, const char **args)
+{
+ char *mask;
+ uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+
+ if (ctx->recurse)
+ attribute |= FILE_ATTRIBUTE_DIRECTORY;
+
+ if (!args[1]) {
+ d_printf("del <filename>\n");
+ return 1;
+ }
+ mask = talloc_asprintf(ctx,"%s%s", ctx->remote_cur_dir, args[1]);
+
+ if (NT_STATUS_IS_ERR(smbcli_unlink(ctx->cli->tree, mask))) {
+ d_printf("%s deleting remote file %s\n",smbcli_errstr(ctx->cli->tree),mask);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+delete a whole directory tree
+****************************************************************************/
+static int cmd_deltree(struct smbclient_context *ctx, const char **args)
+{
+ char *dname;
+ int ret;
+
+ if (!args[1]) {
+ d_printf("deltree <dirname>\n");
+ return 1;
+ }
+
+ dname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ ret = smbcli_deltree(ctx->cli->tree, dname);
+
+ if (ret == -1) {
+ printf("Failed to delete tree %s - %s\n", dname, smbcli_errstr(ctx->cli->tree));
+ return -1;
+ }
+
+ printf("Deleted %d files in %s\n", ret, dname);
+
+ return 0;
+}
+
+typedef struct {
+ const char *level_name;
+ enum smb_fsinfo_level level;
+} fsinfo_level_t;
+
+fsinfo_level_t fsinfo_levels[] = {
+ {"dskattr", RAW_QFS_DSKATTR},
+ {"allocation", RAW_QFS_ALLOCATION},
+ {"volume", RAW_QFS_VOLUME},
+ {"volumeinfo", RAW_QFS_VOLUME_INFO},
+ {"sizeinfo", RAW_QFS_SIZE_INFO},
+ {"deviceinfo", RAW_QFS_DEVICE_INFO},
+ {"attributeinfo", RAW_QFS_ATTRIBUTE_INFO},
+ {"unixinfo", RAW_QFS_UNIX_INFO},
+ {"volume-information", RAW_QFS_VOLUME_INFORMATION},
+ {"size-information", RAW_QFS_SIZE_INFORMATION},
+ {"device-information", RAW_QFS_DEVICE_INFORMATION},
+ {"attribute-information", RAW_QFS_ATTRIBUTE_INFORMATION},
+ {"quota-information", RAW_QFS_QUOTA_INFORMATION},
+ {"fullsize-information", RAW_QFS_FULL_SIZE_INFORMATION},
+ {"objectid", RAW_QFS_OBJECTID_INFORMATION},
+ {NULL, RAW_QFS_GENERIC}
+};
+
+
+static int cmd_fsinfo(struct smbclient_context *ctx, const char **args)
+{
+ union smb_fsinfo fsinfo;
+ NTSTATUS status;
+ fsinfo_level_t *fsinfo_level;
+
+ if (!args[1]) {
+ d_printf("fsinfo <level>, where level is one of following:\n");
+ fsinfo_level = fsinfo_levels;
+ while(fsinfo_level->level_name) {
+ d_printf("%s\n", fsinfo_level->level_name);
+ fsinfo_level++;
+ }
+ return 1;
+ }
+
+ fsinfo_level = fsinfo_levels;
+ while(fsinfo_level->level_name && !strequal(args[1],fsinfo_level->level_name)) {
+ fsinfo_level++;
+ }
+
+ if (!fsinfo_level->level_name) {
+ d_printf("wrong level name!\n");
+ return 1;
+ }
+
+ fsinfo.generic.level = fsinfo_level->level;
+ status = smb_raw_fsinfo(ctx->cli->tree, ctx, &fsinfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("fsinfo-level-%s - %s\n", fsinfo_level->level_name, nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("fsinfo-level-%s:\n", fsinfo_level->level_name);
+ switch(fsinfo.generic.level) {
+ case RAW_QFS_DSKATTR:
+ d_printf("\tunits_total: %hu\n",
+ (unsigned short) fsinfo.dskattr.out.units_total);
+ d_printf("\tblocks_per_unit: %hu\n",
+ (unsigned short) fsinfo.dskattr.out.blocks_per_unit);
+ d_printf("\tblocks_size: %hu\n",
+ (unsigned short) fsinfo.dskattr.out.block_size);
+ d_printf("\tunits_free: %hu\n",
+ (unsigned short) fsinfo.dskattr.out.units_free);
+ break;
+ case RAW_QFS_ALLOCATION:
+ d_printf("\tfs_id: %lu\n",
+ (unsigned long) fsinfo.allocation.out.fs_id);
+ d_printf("\tsectors_per_unit: %lu\n",
+ (unsigned long) fsinfo.allocation.out.sectors_per_unit);
+ d_printf("\ttotal_alloc_units: %lu\n",
+ (unsigned long) fsinfo.allocation.out.total_alloc_units);
+ d_printf("\tavail_alloc_units: %lu\n",
+ (unsigned long) fsinfo.allocation.out.avail_alloc_units);
+ d_printf("\tbytes_per_sector: %hu\n",
+ (unsigned short) fsinfo.allocation.out.bytes_per_sector);
+ break;
+ case RAW_QFS_VOLUME:
+ d_printf("\tserial_number: %lu\n",
+ (unsigned long) fsinfo.volume.out.serial_number);
+ d_printf("\tvolume_name: %s\n", fsinfo.volume.out.volume_name.s);
+ break;
+ case RAW_QFS_VOLUME_INFO:
+ case RAW_QFS_VOLUME_INFORMATION:
+ d_printf("\tcreate_time: %s\n",
+ nt_time_string(ctx,fsinfo.volume_info.out.create_time));
+ d_printf("\tserial_number: %lu\n",
+ (unsigned long) fsinfo.volume_info.out.serial_number);
+ d_printf("\tvolume_name: %s\n", fsinfo.volume_info.out.volume_name.s);
+ break;
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ d_printf("\ttotal_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.size_info.out.total_alloc_units);
+ d_printf("\tavail_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.size_info.out.avail_alloc_units);
+ d_printf("\tsectors_per_unit: %lu\n",
+ (unsigned long) fsinfo.size_info.out.sectors_per_unit);
+ d_printf("\tbytes_per_sector: %lu\n",
+ (unsigned long) fsinfo.size_info.out.bytes_per_sector);
+ break;
+ case RAW_QFS_DEVICE_INFO:
+ case RAW_QFS_DEVICE_INFORMATION:
+ d_printf("\tdevice_type: %lu\n",
+ (unsigned long) fsinfo.device_info.out.device_type);
+ d_printf("\tcharacteristics: 0x%lx\n",
+ (unsigned long) fsinfo.device_info.out.characteristics);
+ break;
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ case RAW_QFS_ATTRIBUTE_INFO:
+ d_printf("\tfs_attr: 0x%lx\n",
+ (unsigned long) fsinfo.attribute_info.out.fs_attr);
+ d_printf("\tmax_file_component_length: %lu\n",
+ (unsigned long) fsinfo.attribute_info.out.max_file_component_length);
+ d_printf("\tfs_type: %s\n", fsinfo.attribute_info.out.fs_type.s);
+ break;
+ case RAW_QFS_UNIX_INFO:
+ d_printf("\tmajor_version: %hu\n",
+ (unsigned short) fsinfo.unix_info.out.major_version);
+ d_printf("\tminor_version: %hu\n",
+ (unsigned short) fsinfo.unix_info.out.minor_version);
+ d_printf("\tcapability: 0x%llx\n",
+ (unsigned long long) fsinfo.unix_info.out.capability);
+ break;
+ case RAW_QFS_QUOTA_INFORMATION:
+ d_printf("\tunknown[3]: [%llu,%llu,%llu]\n",
+ (unsigned long long) fsinfo.quota_information.out.unknown[0],
+ (unsigned long long) fsinfo.quota_information.out.unknown[1],
+ (unsigned long long) fsinfo.quota_information.out.unknown[2]);
+ d_printf("\tquota_soft: %llu\n",
+ (unsigned long long) fsinfo.quota_information.out.quota_soft);
+ d_printf("\tquota_hard: %llu\n",
+ (unsigned long long) fsinfo.quota_information.out.quota_hard);
+ d_printf("\tquota_flags: 0x%llx\n",
+ (unsigned long long) fsinfo.quota_information.out.quota_flags);
+ break;
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ d_printf("\ttotal_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.full_size_information.out.total_alloc_units);
+ d_printf("\tcall_avail_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.full_size_information.out.call_avail_alloc_units);
+ d_printf("\tactual_avail_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.full_size_information.out.actual_avail_alloc_units);
+ d_printf("\tsectors_per_unit: %lu\n",
+ (unsigned long) fsinfo.full_size_information.out.sectors_per_unit);
+ d_printf("\tbytes_per_sector: %lu\n",
+ (unsigned long) fsinfo.full_size_information.out.bytes_per_sector);
+ break;
+ case RAW_QFS_OBJECTID_INFORMATION:
+ d_printf("\tGUID: %s\n",
+ GUID_string(ctx,&fsinfo.objectid_information.out.guid));
+ d_printf("\tunknown[6]: [%llu,%llu,%llu,%llu,%llu,%llu]\n",
+ (unsigned long long) fsinfo.objectid_information.out.unknown[0],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[1],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[2],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[3],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[4],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[5] );
+ break;
+ case RAW_QFS_GENERIC:
+ d_printf("\twrong level returned\n");
+ break;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+show as much information as possible about a file
+****************************************************************************/
+static int cmd_allinfo(struct smbclient_context *ctx, const char **args)
+{
+ char *fname;
+ union smb_fileinfo finfo;
+ NTSTATUS status;
+ int fnum;
+
+ if (!args[1]) {
+ d_printf("allinfo <filename>\n");
+ return 1;
+ }
+ fname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ /* first a ALL_INFO QPATHINFO */
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s - %s\n", fname, nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("\tcreate_time: %s\n", nt_time_string(ctx, finfo.all_info.out.create_time));
+ d_printf("\taccess_time: %s\n", nt_time_string(ctx, finfo.all_info.out.access_time));
+ d_printf("\twrite_time: %s\n", nt_time_string(ctx, finfo.all_info.out.write_time));
+ d_printf("\tchange_time: %s\n", nt_time_string(ctx, finfo.all_info.out.change_time));
+ d_printf("\tattrib: 0x%x\n", finfo.all_info.out.attrib);
+ d_printf("\talloc_size: %lu\n", (unsigned long)finfo.all_info.out.alloc_size);
+ d_printf("\tsize: %lu\n", (unsigned long)finfo.all_info.out.size);
+ d_printf("\tnlink: %u\n", finfo.all_info.out.nlink);
+ d_printf("\tdelete_pending: %u\n", finfo.all_info.out.delete_pending);
+ d_printf("\tdirectory: %u\n", finfo.all_info.out.directory);
+ d_printf("\tea_size: %u\n", finfo.all_info.out.ea_size);
+ d_printf("\tfname: '%s'\n", finfo.all_info.out.fname.s);
+
+ /* 8.3 name if any */
+ finfo.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("\talt_name: %s\n", finfo.alt_name_info.out.fname.s);
+ }
+
+ /* file_id if available */
+ finfo.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("\tfile_id %.0f\n",
+ (double)finfo.internal_information.out.file_id);
+ }
+
+ /* the EAs, if any */
+ finfo.generic.level = RAW_FILEINFO_ALL_EAS;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ int i;
+ for (i=0;i<finfo.all_eas.out.num_eas;i++) {
+ d_printf("\tEA[%d] flags=%d len=%d '%s'\n", i,
+ finfo.all_eas.out.eas[i].flags,
+ (int)finfo.all_eas.out.eas[i].value.length,
+ finfo.all_eas.out.eas[i].name.s);
+ }
+ }
+
+ /* streams, if available */
+ finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ int i;
+ for (i=0;i<finfo.stream_info.out.num_streams;i++) {
+ d_printf("\tstream %d:\n", i);
+ d_printf("\t\tsize %ld\n",
+ (long)finfo.stream_info.out.streams[i].size);
+ d_printf("\t\talloc size %ld\n",
+ (long)finfo.stream_info.out.streams[i].alloc_size);
+ d_printf("\t\tname %s\n", finfo.stream_info.out.streams[i].stream_name.s);
+ }
+ }
+
+ /* dev/inode if available */
+ finfo.generic.level = RAW_FILEINFO_COMPRESSION_INFORMATION;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("\tcompressed size %ld\n", (long)finfo.compression_info.out.compressed_size);
+ d_printf("\tformat %ld\n", (long)finfo.compression_info.out.format);
+ d_printf("\tunit_shift %ld\n", (long)finfo.compression_info.out.unit_shift);
+ d_printf("\tchunk_shift %ld\n", (long)finfo.compression_info.out.chunk_shift);
+ d_printf("\tcluster_shift %ld\n", (long)finfo.compression_info.out.cluster_shift);
+ }
+
+ /* shadow copies if available */
+ fnum = smbcli_open(ctx->cli->tree, fname, O_RDONLY, DENY_NONE);
+ if (fnum != -1) {
+ struct smb_shadow_copy info;
+ int i;
+ info.in.file.fnum = fnum;
+ info.in.max_data = ~0;
+ status = smb_raw_shadow_data(ctx->cli->tree, ctx, &info);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("\tshadow_copy: %u volumes %u names\n",
+ info.out.num_volumes, info.out.num_names);
+ for (i=0;i<info.out.num_names;i++) {
+ d_printf("\t%s\n", info.out.names[i]);
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = talloc_asprintf(ctx, "%s%s",
+ info.out.names[i], fname);
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ continue;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s - %s\n", finfo.generic.in.file.path,
+ nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("\t\tcreate_time: %s\n", nt_time_string(ctx, finfo.all_info.out.create_time));
+ d_printf("\t\twrite_time: %s\n", nt_time_string(ctx, finfo.all_info.out.write_time));
+ d_printf("\t\tchange_time: %s\n", nt_time_string(ctx, finfo.all_info.out.change_time));
+ d_printf("\t\tsize: %lu\n", (unsigned long)finfo.all_info.out.size);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+shows EA contents
+****************************************************************************/
+static int cmd_eainfo(struct smbclient_context *ctx, const char **args)
+{
+ char *fname;
+ union smb_fileinfo finfo;
+ NTSTATUS status;
+ int i;
+
+ if (!args[1]) {
+ d_printf("eainfo <filename>\n");
+ return 1;
+ }
+ fname = talloc_strdup(ctx, args[1]);
+
+ finfo.generic.level = RAW_FILEINFO_ALL_EAS;
+ finfo.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("RAW_FILEINFO_ALL_EAS - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("%s has %d EAs\n", fname, finfo.all_eas.out.num_eas);
+
+ for (i=0;i<finfo.all_eas.out.num_eas;i++) {
+ d_printf("\tEA[%d] flags=%d len=%d '%s'\n", i,
+ finfo.all_eas.out.eas[i].flags,
+ (int)finfo.all_eas.out.eas[i].value.length,
+ finfo.all_eas.out.eas[i].name.s);
+ fflush(stdout);
+ dump_data(0,
+ finfo.all_eas.out.eas[i].value.data,
+ finfo.all_eas.out.eas[i].value.length);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+show any ACL on a file
+****************************************************************************/
+static int cmd_acl(struct smbclient_context *ctx, const char **args)
+{
+ char *fname;
+ union smb_fileinfo query;
+ NTSTATUS status;
+ int fnum;
+
+ if (!args[1]) {
+ d_printf("acl <filename>\n");
+ return 1;
+ }
+ fname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ fnum = smbcli_nt_create_full(ctx->cli->tree, fname, 0,
+ SEC_STD_READ_CONTROL,
+ 0,
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE,
+ NTCREATEX_DISP_OPEN,
+ 0, 0);
+ if (fnum == -1) {
+ d_printf("%s - %s\n", fname, smbcli_errstr(ctx->cli->tree));
+ return -1;
+ }
+
+ query.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ query.query_secdesc.in.file.fnum = fnum;
+ query.query_secdesc.in.secinfo_flags = 0x7;
+
+ status = smb_raw_fileinfo(ctx->cli->tree, ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s - %s\n", fname, nt_errstr(status));
+ return 1;
+ }
+
+ NDR_PRINT_DEBUG(security_descriptor, query.query_secdesc.out.sd);
+
+ return 0;
+}
+
+/****************************************************************************
+lookup a name or sid
+****************************************************************************/
+static int cmd_lookup(struct smbclient_context *ctx, const char **args)
+{
+ NTSTATUS status;
+ struct dom_sid *sid;
+
+ if (!args[1]) {
+ d_printf("lookup <sid|name>\n");
+ return 1;
+ }
+
+ sid = dom_sid_parse_talloc(ctx, args[1]);
+ if (sid == NULL) {
+ const char *sidstr;
+ status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sidstr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("%s\n", sidstr);
+ } else {
+ const char *name;
+ status = smblsa_lookup_sid(ctx->cli, args[1], ctx, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupSids - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("%s\n", name);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+show privileges for a user
+****************************************************************************/
+static int cmd_privileges(struct smbclient_context *ctx, const char **args)
+{
+ NTSTATUS status;
+ struct dom_sid *sid;
+ struct lsa_RightSet rights;
+ unsigned i;
+
+ if (!args[1]) {
+ d_printf("privileges <sid|name>\n");
+ return 1;
+ }
+
+ sid = dom_sid_parse_talloc(ctx, args[1]);
+ if (sid == NULL) {
+ const char *sid_str;
+ status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sid_str);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
+ return 1;
+ }
+ sid = dom_sid_parse_talloc(ctx, sid_str);
+ }
+
+ status = smblsa_sid_privileges(ctx->cli, sid, ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_EnumAccountRights - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ for (i=0;i<rights.count;i++) {
+ d_printf("\t%s\n", rights.names[i].string);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+add privileges for a user
+****************************************************************************/
+static int cmd_addprivileges(struct smbclient_context *ctx, const char **args)
+{
+ NTSTATUS status;
+ struct dom_sid *sid;
+ struct lsa_RightSet rights;
+ int i;
+
+ if (!args[1]) {
+ d_printf("addprivileges <sid|name> <privilege...>\n");
+ return 1;
+ }
+
+ sid = dom_sid_parse_talloc(ctx, args[1]);
+ if (sid == NULL) {
+ const char *sid_str;
+ status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sid_str);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
+ return 1;
+ }
+ sid = dom_sid_parse_talloc(ctx, sid_str);
+ }
+
+ ZERO_STRUCT(rights);
+ for (i = 2; args[i]; i++) {
+ rights.names = talloc_realloc(ctx, rights.names,
+ struct lsa_StringLarge, rights.count+1);
+ rights.names[rights.count].string = talloc_strdup(ctx, args[i]);
+ rights.count++;
+ }
+
+
+ status = smblsa_sid_add_privileges(ctx->cli, sid, ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_AddAccountRights - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+delete privileges for a user
+****************************************************************************/
+static int cmd_delprivileges(struct smbclient_context *ctx, const char **args)
+{
+ NTSTATUS status;
+ struct dom_sid *sid;
+ struct lsa_RightSet rights;
+ int i;
+
+ if (!args[1]) {
+ d_printf("delprivileges <sid|name> <privilege...>\n");
+ return 1;
+ }
+
+ sid = dom_sid_parse_talloc(ctx, args[1]);
+ if (sid == NULL) {
+ const char *sid_str;
+ status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sid_str);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
+ return 1;
+ }
+ sid = dom_sid_parse_talloc(ctx, sid_str);
+ }
+
+ ZERO_STRUCT(rights);
+ for (i = 2; args[i]; i++) {
+ rights.names = talloc_realloc(ctx, rights.names,
+ struct lsa_StringLarge, rights.count+1);
+ rights.names[rights.count].string = talloc_strdup(ctx, args[i]);
+ rights.count++;
+ }
+
+
+ status = smblsa_sid_del_privileges(ctx->cli, sid, ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_RemoveAccountRights - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+static int cmd_open(struct smbclient_context *ctx, const char **args)
+{
+ char *mask;
+
+ if (!args[1]) {
+ d_printf("open <filename>\n");
+ return 1;
+ }
+ mask = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ smbcli_open(ctx->cli->tree, mask, O_RDWR, DENY_ALL);
+
+ return 0;
+}
+
+
+/****************************************************************************
+remove a directory
+****************************************************************************/
+static int cmd_rmdir(struct smbclient_context *ctx, const char **args)
+{
+ char *mask;
+
+ if (!args[1]) {
+ d_printf("rmdir <dirname>\n");
+ return 1;
+ }
+ mask = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ if (NT_STATUS_IS_ERR(smbcli_rmdir(ctx->cli->tree, mask))) {
+ d_printf("%s removing remote directory file %s\n",
+ smbcli_errstr(ctx->cli->tree),mask);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX hardlink.
+****************************************************************************/
+static int cmd_link(struct smbclient_context *ctx, const char **args)
+{
+ char *src,*dest;
+
+ if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+
+ if (!args[1] || !args[2]) {
+ d_printf("link <src> <dest>\n");
+ return 1;
+ }
+
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+ dest = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
+
+ if (NT_STATUS_IS_ERR(smbcli_unix_hardlink(ctx->cli->tree, src, dest))) {
+ d_printf("%s linking files (%s -> %s)\n", smbcli_errstr(ctx->cli->tree), src, dest);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX symlink.
+****************************************************************************/
+
+static int cmd_symlink(struct smbclient_context *ctx, const char **args)
+{
+ char *src,*dest;
+
+ if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ if (!args[1] || !args[2]) {
+ d_printf("symlink <src> <dest>\n");
+ return 1;
+ }
+
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+ dest = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
+
+ if (NT_STATUS_IS_ERR(smbcli_unix_symlink(ctx->cli->tree, src, dest))) {
+ d_printf("%s symlinking files (%s -> %s)\n",
+ smbcli_errstr(ctx->cli->tree), src, dest);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX chmod.
+****************************************************************************/
+
+static int cmd_chmod(struct smbclient_context *ctx, const char **args)
+{
+ char *src;
+ mode_t mode;
+
+ if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ if (!args[1] || !args[2]) {
+ d_printf("chmod mode file\n");
+ return 1;
+ }
+
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
+
+ mode = (mode_t)strtol(args[1], NULL, 8);
+
+ if (NT_STATUS_IS_ERR(smbcli_unix_chmod(ctx->cli->tree, src, mode))) {
+ d_printf("%s chmod file %s 0%o\n",
+ smbcli_errstr(ctx->cli->tree), src, (mode_t)mode);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX chown.
+****************************************************************************/
+
+static int cmd_chown(struct smbclient_context *ctx, const char **args)
+{
+ char *src;
+ uid_t uid;
+ gid_t gid;
+
+ if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ if (!args[1] || !args[2] || !args[3]) {
+ d_printf("chown uid gid file\n");
+ return 1;
+ }
+
+ uid = (uid_t)atoi(args[1]);
+ gid = (gid_t)atoi(args[2]);
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[3]);
+
+ if (NT_STATUS_IS_ERR(smbcli_unix_chown(ctx->cli->tree, src, uid, gid))) {
+ d_printf("%s chown file %s uid=%d, gid=%d\n",
+ smbcli_errstr(ctx->cli->tree), src, (int)uid, (int)gid);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+rename some files
+****************************************************************************/
+static int cmd_rename(struct smbclient_context *ctx, const char **args)
+{
+ char *src,*dest;
+
+ if (!args[1] || !args[2]) {
+ d_printf("rename <src> <dest>\n");
+ return 1;
+ }
+
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+ dest = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
+
+ if (NT_STATUS_IS_ERR(smbcli_rename(ctx->cli->tree, src, dest))) {
+ d_printf("%s renaming files\n",smbcli_errstr(ctx->cli->tree));
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+toggle the prompt flag
+****************************************************************************/
+static int cmd_prompt(struct smbclient_context *ctx, const char **args)
+{
+ ctx->prompt = !ctx->prompt;
+ DEBUG(2,("prompting is now %s\n",ctx->prompt?"on":"off"));
+
+ return 1;
+}
+
+
+/****************************************************************************
+set the newer than time
+****************************************************************************/
+static int cmd_newer(struct smbclient_context *ctx, const char **args)
+{
+ struct stat sbuf;
+
+ if (args[1] && (stat(args[1],&sbuf) == 0)) {
+ ctx->newer_than = sbuf.st_mtime;
+ DEBUG(1,("Getting files newer than %s",
+ asctime(localtime(&ctx->newer_than))));
+ } else {
+ ctx->newer_than = 0;
+ }
+
+ if (args[1] && ctx->newer_than == 0) {
+ d_printf("Error setting newer-than time\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+set the archive level
+****************************************************************************/
+static int cmd_archive(struct smbclient_context *ctx, const char **args)
+{
+ if (args[1]) {
+ ctx->archive_level = atoi(args[1]);
+ } else
+ d_printf("Archive level is %d\n",ctx->archive_level);
+
+ return 0;
+}
+
+/****************************************************************************
+toggle the lowercaseflag
+****************************************************************************/
+static int cmd_lowercase(struct smbclient_context *ctx, const char **args)
+{
+ ctx->lowercase = !ctx->lowercase;
+ DEBUG(2,("filename lowercasing is now %s\n",ctx->lowercase?"on":"off"));
+
+ return 0;
+}
+
+
+
+
+/****************************************************************************
+toggle the recurse flag
+****************************************************************************/
+static int cmd_recurse(struct smbclient_context *ctx, const char **args)
+{
+ ctx->recurse = !ctx->recurse;
+ DEBUG(2,("directory recursion is now %s\n",ctx->recurse?"on":"off"));
+
+ return 0;
+}
+
+/****************************************************************************
+toggle the translate flag
+****************************************************************************/
+static int cmd_translate(struct smbclient_context *ctx, const char **args)
+{
+ ctx->translation = !ctx->translation;
+ DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
+ ctx->translation?"on":"off"));
+
+ return 0;
+}
+
+
+/****************************************************************************
+do a printmode command
+****************************************************************************/
+static int cmd_printmode(struct smbclient_context *ctx, const char **args)
+{
+ if (args[1]) {
+ if (strequal(args[1],"text")) {
+ ctx->printmode = 0;
+ } else {
+ if (strequal(args[1],"graphics"))
+ ctx->printmode = 1;
+ else
+ ctx->printmode = atoi(args[1]);
+ }
+ }
+
+ switch(ctx->printmode)
+ {
+ case 0:
+ DEBUG(2,("the printmode is now text\n"));
+ break;
+ case 1:
+ DEBUG(2,("the printmode is now graphics\n"));
+ break;
+ default:
+ DEBUG(2,("the printmode is now %d\n", ctx->printmode));
+ break;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ do the lcd command
+ ****************************************************************************/
+static int cmd_lcd(struct smbclient_context *ctx, const char **args)
+{
+ char d[PATH_MAX];
+
+ if (args[1])
+ chdir(args[1]);
+ DEBUG(2,("the local directory is now %s\n",getcwd(d, PATH_MAX)));
+
+ return 0;
+}
+
+/****************************************************************************
+history
+****************************************************************************/
+static int cmd_history(struct smbclient_context *ctx, const char **args)
+{
+#if defined(HAVE_LIBREADLINE) && defined(HAVE_HISTORY_LIST)
+ HIST_ENTRY **hlist;
+ int i;
+
+ hlist = history_list();
+
+ for (i = 0; hlist && hlist[i]; i++) {
+ DEBUG(0, ("%d: %s\n", i, hlist[i]->line));
+ }
+#else
+ DEBUG(0,("no history without readline support\n"));
+#endif
+
+ return 0;
+}
+
+/****************************************************************************
+ get a file restarting at end of local file
+ ****************************************************************************/
+static int cmd_reget(struct smbclient_context *ctx, const char **args)
+{
+ char *local_name;
+ char *remote_name;
+
+ if (!args[1]) {
+ d_printf("reget <filename>\n");
+ return 1;
+ }
+ remote_name = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
+ dos_clean_name(remote_name);
+
+ if (args[2])
+ local_name = talloc_strdup(ctx, args[2]);
+ else
+ local_name = talloc_strdup(ctx, args[1]);
+
+ return do_get(ctx, remote_name, local_name, true);
+}
+
+/****************************************************************************
+ put a file restarting at end of local file
+ ****************************************************************************/
+static int cmd_reput(struct smbclient_context *ctx, const char **args)
+{
+ char *local_name;
+ char *remote_name;
+
+ if (!args[1]) {
+ d_printf("reput <filename>\n");
+ return 1;
+ }
+ local_name = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
+
+ if (!file_exist(local_name)) {
+ d_printf("%s does not exist\n", local_name);
+ return 1;
+ }
+
+ if (args[2])
+ remote_name = talloc_strdup(ctx, args[2]);
+ else
+ remote_name = talloc_strdup(ctx, args[1]);
+
+ dos_clean_name(remote_name);
+
+ return do_put(ctx, remote_name, local_name, true);
+}
+
+
+/*
+ return a string representing a share type
+*/
+static const char *share_type_str(uint32_t type)
+{
+ switch (type & 0xF) {
+ case STYPE_DISKTREE:
+ return "Disk";
+ case STYPE_PRINTQ:
+ return "Printer";
+ case STYPE_DEVICE:
+ return "Device";
+ case STYPE_IPC:
+ return "IPC";
+ default:
+ return "Unknown";
+ }
+}
+
+
+/*
+ display a list of shares from a level 1 share enum
+*/
+static void display_share_result(struct srvsvc_NetShareCtr1 *ctr1)
+{
+ int i;
+
+ for (i=0;i<ctr1->count;i++) {
+ struct srvsvc_NetShareInfo1 *info = ctr1->array+i;
+
+ printf("\t%-15s %-10.10s %s\n",
+ info->name,
+ share_type_str(info->type),
+ info->comment);
+ }
+}
+
+
+
+/****************************************************************************
+try and browse available shares on a host
+****************************************************************************/
+static bool browse_host(struct loadparm_context *lp_ctx,
+ struct event_context *ev_ctx,
+ const char *query_host)
+{
+ struct dcerpc_pipe *p;
+ char *binding;
+ NTSTATUS status;
+ struct srvsvc_NetShareEnumAll r;
+ uint32_t resume_handle = 0;
+ TALLOC_CTX *mem_ctx = talloc_init("browse_host");
+ struct srvsvc_NetShareCtr1 ctr1;
+
+ binding = talloc_asprintf(mem_ctx, "ncacn_np:%s", query_host);
+
+ status = dcerpc_pipe_connect(mem_ctx, &p, binding,
+ &ndr_table_srvsvc,
+ cmdline_credentials, ev_ctx,
+ lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Failed to connect to %s - %s\n",
+ binding, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.level = 1;
+ r.in.ctr.ctr1 = &ctr1;
+ r.in.max_buffer = ~0;
+ r.in.resume_handle = &resume_handle;
+
+ d_printf("\n\tSharename Type Comment\n");
+ d_printf("\t--------- ---- -------\n");
+
+ do {
+ ZERO_STRUCT(ctr1);
+ status = dcerpc_srvsvc_NetShareEnumAll(p, mem_ctx, &r);
+
+ if (NT_STATUS_IS_OK(status) &&
+ (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA) ||
+ W_ERROR_IS_OK(r.out.result)) &&
+ r.out.ctr.ctr1) {
+ display_share_result(r.out.ctr.ctr1);
+ resume_handle += r.out.ctr.ctr1->count;
+ }
+ } while (NT_STATUS_IS_OK(status) && W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA));
+
+ talloc_free(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("Failed NetShareEnumAll %s - %s/%s\n",
+ binding, nt_errstr(status), win_errstr(r.out.result));
+ return false;
+ }
+
+ return false;
+}
+
+/****************************************************************************
+try and browse available connections on a host
+****************************************************************************/
+static bool list_servers(const char *wk_grp)
+{
+ d_printf("REWRITE: list servers not implemented\n");
+ return false;
+}
+
+/* Some constants for completing filename arguments */
+
+#define COMPL_NONE 0 /* No completions */
+#define COMPL_REMOTE 1 /* Complete remote filename */
+#define COMPL_LOCAL 2 /* Complete local filename */
+
+static int cmd_help(struct smbclient_context *ctx, const char **args);
+
+/* This defines the commands supported by this client.
+ * NOTE: The "!" must be the last one in the list because it's fn pointer
+ * field is NULL, and NULL in that field is used in process_tok()
+ * (below) to indicate the end of the list. crh
+ */
+static struct
+{
+ const char *name;
+ int (*fn)(struct smbclient_context *ctx, const char **args);
+ const char *description;
+ char compl_args[2]; /* Completion argument info */
+} commands[] =
+{
+ {"?",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+ {"addprivileges",cmd_addprivileges,"<sid|name> <privilege...> add privileges for a user",{COMPL_NONE,COMPL_NONE}},
+ {"altname",cmd_altname,"<file> show alt name",{COMPL_NONE,COMPL_NONE}},
+ {"acl",cmd_acl,"<file> show file ACL",{COMPL_NONE,COMPL_NONE}},
+ {"allinfo",cmd_allinfo,"<file> show all possible info about a file",{COMPL_NONE,COMPL_NONE}},
+ {"archive",cmd_archive,"<level>\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit",{COMPL_NONE,COMPL_NONE}},
+ {"cancel",cmd_rewrite,"<jobid> cancel a print queue entry",{COMPL_NONE,COMPL_NONE}},
+ {"cd",cmd_cd,"[directory] change/report the remote directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"chmod",cmd_chmod,"<src> <mode> chmod a file using UNIX permission",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"chown",cmd_chown,"<src> <uid> <gid> chown a file using UNIX uids and gids",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"del",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"delprivileges",cmd_delprivileges,"<sid|name> <privilege...> remove privileges for a user",{COMPL_NONE,COMPL_NONE}},
+ {"deltree",cmd_deltree,"<dir> delete a whole directory tree",{COMPL_REMOTE,COMPL_NONE}},
+ {"dir",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"du",cmd_du,"<mask> computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"eainfo",cmd_eainfo,"<file> show EA contents for a file",{COMPL_NONE,COMPL_NONE}},
+ {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"fsinfo",cmd_fsinfo,"query file system info",{COMPL_NONE,COMPL_NONE}},
+ {"get",cmd_get,"<remote name> [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}},
+ {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+ {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}},
+ {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}},
+ {"link",cmd_link,"<src> <dest> create a UNIX hard link",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"lookup",cmd_lookup,"<sid|name> show SID for name or name for SID",{COMPL_NONE,COMPL_NONE}},
+ {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get",{COMPL_NONE,COMPL_NONE}},
+ {"ls",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"mask",cmd_select,"<mask> mask all filenames against this",{COMPL_REMOTE,COMPL_NONE}},
+ {"md",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
+ {"mget",cmd_mget,"<mask> get all the matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"mkdir",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
+ {"more",cmd_more,"<remote name> view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}},
+ {"mput",cmd_mput,"<mask> put all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"newer",cmd_newer,"<file> only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}},
+ {"open",cmd_open,"<mask> open a file",{COMPL_REMOTE,COMPL_NONE}},
+ {"privileges",cmd_privileges,"<user> show privileges for a user",{COMPL_NONE,COMPL_NONE}},
+ {"print",cmd_print,"<file name> print a file",{COMPL_NONE,COMPL_NONE}},
+ {"printmode",cmd_printmode,"<graphics or text> set the print mode",{COMPL_NONE,COMPL_NONE}},
+ {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput",{COMPL_NONE,COMPL_NONE}},
+ {"put",cmd_put,"<local name> [remote name] put a file",{COMPL_LOCAL,COMPL_REMOTE}},
+ {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)",{COMPL_NONE,COMPL_NONE}},
+ {"q",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"queue",cmd_rewrite,"show the print queue",{COMPL_NONE,COMPL_NONE}},
+ {"quit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"rd",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
+ {"recurse",cmd_recurse,"toggle directory recursion for mget and mput",{COMPL_NONE,COMPL_NONE}},
+ {"reget",cmd_reget,"<remote name> [local name] get a file restarting at end of local file",{COMPL_REMOTE,COMPL_LOCAL}},
+ {"rename",cmd_rename,"<src> <dest> rename some files",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"reput",cmd_reput,"<local name> [remote name] put a file restarting at end of remote file",{COMPL_LOCAL,COMPL_REMOTE}},
+ {"rm",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"rmdir",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
+ {"symlink",cmd_symlink,"<src> <dest> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}},
+
+ /* Yes, this must be here, see crh's comment above. */
+ {"!",NULL,"run a shell command on the local system",{COMPL_NONE,COMPL_NONE}},
+ {NULL,NULL,NULL,{COMPL_NONE,COMPL_NONE}}
+};
+
+
+/*******************************************************************
+ lookup a command string in the list of commands, including
+ abbreviations
+ ******************************************************************/
+static int process_tok(const char *tok)
+{
+ int i = 0, matches = 0;
+ int cmd=0;
+ int tok_len = strlen(tok);
+
+ while (commands[i].fn != NULL) {
+ if (strequal(commands[i].name,tok)) {
+ matches = 1;
+ cmd = i;
+ break;
+ } else if (strncasecmp(commands[i].name, tok, tok_len) == 0) {
+ matches++;
+ cmd = i;
+ }
+ i++;
+ }
+
+ if (matches == 0)
+ return(-1);
+ else if (matches == 1)
+ return(cmd);
+ else
+ return(-2);
+}
+
+/****************************************************************************
+help
+****************************************************************************/
+static int cmd_help(struct smbclient_context *ctx, const char **args)
+{
+ int i=0,j;
+
+ if (args[1]) {
+ if ((i = process_tok(args[1])) >= 0)
+ d_printf("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description);
+ } else {
+ while (commands[i].description) {
+ for (j=0; commands[i].description && (j<5); j++) {
+ d_printf("%-15s",commands[i].name);
+ i++;
+ }
+ d_printf("\n");
+ }
+ }
+ return 0;
+}
+
+static int process_line(struct smbclient_context *ctx, const char *cline);
+/****************************************************************************
+process a -c command string
+****************************************************************************/
+static int process_command_string(struct smbclient_context *ctx, const char *cmd)
+{
+ const char **lines;
+ int i, rc = 0;
+
+ lines = str_list_make(NULL, cmd, ";");
+ for (i = 0; lines[i]; i++) {
+ rc |= process_line(ctx, lines[i]);
+ }
+ talloc_free(lines);
+
+ return rc;
+}
+
+#define MAX_COMPLETIONS 100
+
+typedef struct {
+ char *dirmask;
+ char **matches;
+ int count, samelen;
+ const char *text;
+ int len;
+} completion_remote_t;
+
+static void completion_remote_filter(struct clilist_file_info *f, const char *mask, void *state)
+{
+ completion_remote_t *info = (completion_remote_t *)state;
+
+ if ((info->count < MAX_COMPLETIONS - 1) && (strncmp(info->text, f->name, info->len) == 0) && (!ISDOT(f->name)) && (!ISDOTDOT(f->name))) {
+ if ((info->dirmask[0] == 0) && !(f->attrib & FILE_ATTRIBUTE_DIRECTORY))
+ info->matches[info->count] = strdup(f->name);
+ else {
+ char *tmp;
+
+ if (info->dirmask[0] != 0)
+ tmp = talloc_asprintf(NULL, "%s/%s", info->dirmask, f->name);
+ else
+ tmp = talloc_strdup(NULL, f->name);
+
+ if (f->attrib & FILE_ATTRIBUTE_DIRECTORY)
+ tmp = talloc_append_string(NULL, tmp, "/");
+ info->matches[info->count] = tmp;
+ }
+ if (info->matches[info->count] == NULL)
+ return;
+ if (f->attrib & FILE_ATTRIBUTE_DIRECTORY)
+ smb_readline_ca_char(0);
+
+ if (info->count == 1)
+ info->samelen = strlen(info->matches[info->count]);
+ else
+ while (strncmp(info->matches[info->count], info->matches[info->count-1], info->samelen) != 0)
+ info->samelen--;
+ info->count++;
+ }
+}
+
+static char **remote_completion(const char *text, int len)
+{
+ char *dirmask;
+ int i;
+ completion_remote_t info;
+
+ info.samelen = len;
+ info.text = text;
+ info.len = len;
+
+ if (len >= PATH_MAX)
+ return(NULL);
+
+ info.matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!info.matches) return NULL;
+ info.matches[0] = NULL;
+
+ for (i = len-1; i >= 0; i--)
+ if ((text[i] == '/') || (text[i] == '\\'))
+ break;
+ info.text = text+i+1;
+ info.samelen = info.len = len-i-1;
+
+ if (i > 0) {
+ info.dirmask = talloc_strndup(NULL, text, i+1);
+ info.dirmask[i+1] = 0;
+ asprintf(&dirmask, "%s%*s*", rl_ctx->remote_cur_dir, i-1, text);
+ } else
+ asprintf(&dirmask, "%s*", rl_ctx->remote_cur_dir);
+
+ if (smbcli_list(rl_ctx->cli->tree, dirmask,
+ FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
+ completion_remote_filter, &info) < 0)
+ goto cleanup;
+
+ if (info.count == 2)
+ info.matches[0] = strdup(info.matches[1]);
+ else {
+ info.matches[0] = malloc_array_p(char, info.samelen+1);
+ if (!info.matches[0])
+ goto cleanup;
+ strncpy(info.matches[0], info.matches[1], info.samelen);
+ info.matches[0][info.samelen] = 0;
+ }
+ info.matches[info.count] = NULL;
+ return info.matches;
+
+cleanup:
+ for (i = 0; i < info.count; i++)
+ free(info.matches[i]);
+ free(info.matches);
+ return NULL;
+}
+
+static char **completion_fn(const char *text, int start, int end)
+{
+ smb_readline_ca_char(' ');
+
+ if (start) {
+ const char *buf, *sp;
+ int i;
+ char compl_type;
+
+ buf = smb_readline_get_line_buffer();
+ if (buf == NULL)
+ return NULL;
+
+ sp = strchr(buf, ' ');
+ if (sp == NULL)
+ return NULL;
+
+ for (i = 0; commands[i].name; i++)
+ if ((strncmp(commands[i].name, text, sp - buf) == 0) && (commands[i].name[sp - buf] == 0))
+ break;
+ if (commands[i].name == NULL)
+ return NULL;
+
+ while (*sp == ' ')
+ sp++;
+
+ if (sp == (buf + start))
+ compl_type = commands[i].compl_args[0];
+ else
+ compl_type = commands[i].compl_args[1];
+
+ if (compl_type == COMPL_REMOTE)
+ return remote_completion(text, end - start);
+ else /* fall back to local filename completion */
+ return NULL;
+ } else {
+ char **matches;
+ int i, len, samelen = 0, count=1;
+
+ matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!matches) return NULL;
+ matches[0] = NULL;
+
+ len = strlen(text);
+ for (i=0;commands[i].fn && count < MAX_COMPLETIONS-1;i++) {
+ if (strncmp(text, commands[i].name, len) == 0) {
+ matches[count] = strdup(commands[i].name);
+ if (!matches[count])
+ goto cleanup;
+ if (count == 1)
+ samelen = strlen(matches[count]);
+ else
+ while (strncmp(matches[count], matches[count-1], samelen) != 0)
+ samelen--;
+ count++;
+ }
+ }
+
+ switch (count) {
+ case 0: /* should never happen */
+ case 1:
+ goto cleanup;
+ case 2:
+ matches[0] = strdup(matches[1]);
+ break;
+ default:
+ matches[0] = malloc_array_p(char, samelen+1);
+ if (!matches[0])
+ goto cleanup;
+ strncpy(matches[0], matches[1], samelen);
+ matches[0][samelen] = 0;
+ }
+ matches[count] = NULL;
+ return matches;
+
+cleanup:
+ count--;
+ while (count >= 0) {
+ free(matches[count]);
+ count--;
+ }
+ free(matches);
+ return NULL;
+ }
+}
+
+/****************************************************************************
+make sure we swallow keepalives during idle time
+****************************************************************************/
+static void readline_callback(void)
+{
+ static time_t last_t;
+ time_t t;
+
+ t = time(NULL);
+
+ if (t - last_t < 5) return;
+
+ last_t = t;
+
+ smbcli_transport_process(rl_ctx->cli->transport);
+
+ if (rl_ctx->cli->tree) {
+ smbcli_chkpath(rl_ctx->cli->tree, "\\");
+ }
+}
+
+static int process_line(struct smbclient_context *ctx, const char *cline)
+{
+ const char **args;
+ int i;
+
+ /* and get the first part of the command */
+ args = str_list_make_shell(ctx, cline, NULL);
+ if (!args || !args[0])
+ return 0;
+
+ if ((i = process_tok(args[0])) >= 0) {
+ i = commands[i].fn(ctx, args);
+ } else if (i == -2) {
+ d_printf("%s: command abbreviation ambiguous\n",args[0]);
+ } else {
+ d_printf("%s: command not found\n",args[0]);
+ }
+
+ talloc_free(args);
+
+ return i;
+}
+
+/****************************************************************************
+process commands on stdin
+****************************************************************************/
+static int process_stdin(struct smbclient_context *ctx)
+{
+ int rc = 0;
+ while (1) {
+ /* display a prompt */
+ char *the_prompt = talloc_asprintf(ctx, "smb: %s> ", ctx->remote_cur_dir);
+ char *cline = smb_readline(the_prompt, readline_callback, completion_fn);
+ talloc_free(the_prompt);
+
+ if (!cline) break;
+
+ /* special case - first char is ! */
+ if (*cline == '!') {
+ system(cline + 1);
+ free(cline);
+ continue;
+ }
+
+ rc |= process_command_string(ctx, cline);
+ free(cline);
+
+ }
+
+ return rc;
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static bool do_connect(struct smbclient_context *ctx,
+ struct event_context *ev_ctx,
+ struct resolve_context *resolve_ctx,
+ const char *specified_server, const char **ports,
+ const char *specified_share,
+ struct cli_credentials *cred,
+ struct smbcli_options *options)
+{
+ NTSTATUS status;
+ char *server, *share;
+
+ rl_ctx = ctx; /* Ugly hack */
+
+ if (strncmp(specified_share, "\\\\", 2) == 0 ||
+ strncmp(specified_share, "//", 2) == 0) {
+ smbcli_parse_unc(specified_share, ctx, &server, &share);
+ } else {
+ share = talloc_strdup(ctx, specified_share);
+ server = talloc_strdup(ctx, specified_server);
+ }
+
+ ctx->remote_cur_dir = talloc_strdup(ctx, "\\");
+
+ status = smbcli_full_connection(ctx, &ctx->cli, server, ports,
+ share, NULL, cred, resolve_ctx,
+ ev_ctx, options);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Connection to \\\\%s\\%s failed - %s\n",
+ server, share, nt_errstr(status));
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+/****************************************************************************
+handle a -L query
+****************************************************************************/
+static int do_host_query(struct loadparm_context *lp_ctx,
+ struct event_context *ev_ctx,
+ const char *query_host,
+ const char *workgroup)
+{
+ browse_host(lp_ctx, ev_ctx, query_host);
+ list_servers(workgroup);
+ return(0);
+}
+
+
+/****************************************************************************
+handle a message operation
+****************************************************************************/
+static int do_message_op(const char *netbios_name, const char *desthost,
+ const char **destports, const char *destip,
+ int name_type,
+ struct event_context *ev_ctx,
+ struct resolve_context *resolve_ctx,
+ struct smbcli_options *options)
+{
+ struct nbt_name called, calling;
+ const char *server_name;
+ struct smbcli_state *cli;
+
+ make_nbt_name_client(&calling, netbios_name);
+
+ nbt_choose_called_name(NULL, &called, desthost, name_type);
+
+ server_name = destip ? destip : desthost;
+
+ if (!(cli = smbcli_state_init(NULL)) ||
+ !smbcli_socket_connect(cli, server_name, destports,
+ ev_ctx, resolve_ctx, options)) {
+ d_printf("Connection to %s failed\n", server_name);
+ return 1;
+ }
+
+ if (!smbcli_transport_establish(cli, &calling, &called)) {
+ d_printf("session request failed\n");
+ talloc_free(cli);
+ return 1;
+ }
+
+ send_message(cli, desthost);
+ talloc_free(cli);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ const char *base_directory = NULL;
+ const char *dest_ip = NULL;
+ int opt;
+ const char *query_host = NULL;
+ bool message = false;
+ const char *desthost = NULL;
+ poptContext pc;
+ const char *service = NULL;
+ int port = 0;
+ char *p;
+ int rc = 0;
+ int name_type = 0x20;
+ TALLOC_CTX *mem_ctx;
+ struct event_context *ev_ctx;
+ struct smbclient_context *ctx;
+ const char *cmdstr = NULL;
+ struct smbcli_options smb_options;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ { "message", 'M', POPT_ARG_STRING, NULL, 'M', "Send message", "HOST" },
+ { "ip-address", 'I', POPT_ARG_STRING, NULL, 'I', "Use this IP to connect to", "IP" },
+ { "stderr", 'E', POPT_ARG_NONE, NULL, 'E', "Write messages to stderr instead of stdout" },
+ { "list", 'L', POPT_ARG_STRING, NULL, 'L', "Get a list of shares available on a host", "HOST" },
+ { "directory", 'D', POPT_ARG_STRING, NULL, 'D', "Start from directory", "DIR" },
+ { "command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated commands" },
+ { "send-buffer", 'b', POPT_ARG_INT, NULL, 'b', "Changes the transmit/send buffer", "BYTES" },
+ { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", "PORT" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ mem_ctx = talloc_init("client.c/main");
+ if (!mem_ctx) {
+ d_printf("\nclient.c: Not enough memory\n");
+ exit(1);
+ }
+
+ ctx = talloc(mem_ctx, struct smbclient_context);
+ ctx->io_bufsize = 64512;
+
+ pc = poptGetContext("smbclient", argc, (const char **) argv, long_options, 0);
+ poptSetOtherOptionHelp(pc, "[OPTIONS] service <password>");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'M':
+ /* Messages are sent to NetBIOS name type 0x3
+ * (Messenger Service). Make sure we default
+ * to port 139 instead of port 445. srl,crh
+ */
+ name_type = 0x03;
+ desthost = strdup(poptGetOptArg(pc));
+ if( 0 == port ) port = 139;
+ message = true;
+ break;
+ case 'I':
+ dest_ip = poptGetOptArg(pc);
+ break;
+ case 'L':
+ query_host = strdup(poptGetOptArg(pc));
+ break;
+ case 'D':
+ base_directory = strdup(poptGetOptArg(pc));
+ break;
+ case 'b':
+ ctx->io_bufsize = MAX(1, atoi(poptGetOptArg(pc)));
+ break;
+ }
+ }
+
+ gensec_init(cmdline_lp_ctx);
+
+ if(poptPeekArg(pc)) {
+ char *s = strdup(poptGetArg(pc));
+
+ /* Convert any '/' characters in the service name to '\' characters */
+ string_replace(s, '/','\\');
+
+ service = s;
+
+ if (count_chars(s,'\\') < 3) {
+ d_printf("\n%s: Not enough '\\' characters in service\n",s);
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ if (poptPeekArg(pc)) {
+ cli_credentials_set_password(cmdline_credentials, poptGetArg(pc), CRED_SPECIFIED);
+ }
+
+ /*init_names(); */
+
+ if (!query_host && !service && !message) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ poptFreeContext(pc);
+
+ lp_smbcli_options(cmdline_lp_ctx, &smb_options);
+
+ ev_ctx = s4_event_context_init(talloc_autofree_context());
+
+ DEBUG( 3, ( "Client started (version %s).\n", SAMBA_VERSION_STRING ) );
+
+ if (query_host && (p=strchr_m(query_host,'#'))) {
+ *p = 0;
+ p++;
+ sscanf(p, "%x", &name_type);
+ }
+
+ if (query_host) {
+ rc = do_host_query(cmdline_lp_ctx, ev_ctx, query_host,
+ lp_workgroup(cmdline_lp_ctx));
+ return rc;
+ }
+
+ if (message) {
+ rc = do_message_op(lp_netbios_name(cmdline_lp_ctx), desthost,
+ lp_smb_ports(cmdline_lp_ctx), dest_ip,
+ name_type, ev_ctx,
+ lp_resolve_context(cmdline_lp_ctx),
+ &smb_options);
+ return rc;
+ }
+
+ if (!do_connect(ctx, ev_ctx, lp_resolve_context(cmdline_lp_ctx),
+ desthost, lp_smb_ports(cmdline_lp_ctx), service,
+ cmdline_credentials, &smb_options))
+ return 1;
+
+ if (base_directory)
+ do_cd(ctx, base_directory);
+
+ if (cmdstr) {
+ rc = process_command_string(ctx, cmdstr);
+ } else {
+ rc = process_stdin(ctx);
+ }
+
+ talloc_free(mem_ctx);
+
+ return rc;
+}
diff --git a/source4/client/config.mk b/source4/client/config.mk
new file mode 100644
index 0000000000..877544a09a
--- /dev/null
+++ b/source4/client/config.mk
@@ -0,0 +1,36 @@
+# client subsystem
+
+#################################
+# Start BINARY smbclient
+[BINARY::smbclient]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ SMBREADLINE \
+ LIBSAMBA-UTIL \
+ LIBCLI_SMB \
+ RPC_NDR_SRVSVC \
+ LIBCLI_LSA \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS \
+ LIBCLI_RAW
+# End BINARY smbclient
+#################################
+
+smbclient_OBJ_FILES = $(clientsrcdir)/client.o
+
+#################################
+# Start BINARY cifsdd
+[BINARY::cifsdd]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBCLI_SMB \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS
+# End BINARY sdd
+#################################
+
+cifsdd_OBJ_FILES = $(addprefix $(clientsrcdir)/, cifsdd.o cifsddio.o)
diff --git a/source4/client/mount.cifs.c b/source4/client/mount.cifs.c
new file mode 100644
index 0000000000..7167859d7b
--- /dev/null
+++ b/source4/client/mount.cifs.c
@@ -0,0 +1,557 @@
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <getopt.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <mntent.h>
+
+#define MOUNT_CIFS_VERSION "1"
+
+extern char *getusername(void);
+
+char * thisprogram;
+int verboseflag = 0;
+static int got_password = 0;
+static int got_user = 0;
+static int got_domain = 0;
+static int got_ip = 0;
+static int got_unc = 0;
+static int got_uid = 0;
+static int got_gid = 0;
+static char * user_name = NULL;
+char * mountpassword = NULL;
+
+
+void mount_cifs_usage()
+{
+ printf("\nUsage: %s remotetarget dir\n", thisprogram);
+ printf("\nMount the remotetarget, specified as either a UNC name or ");
+ printf(" CIFS URL, to the local directory, dir.\n");
+
+ exit(1);
+}
+
+/* caller frees username if necessary */
+char * getusername() {
+ char *username = NULL;
+ struct passwd *password = getpwuid(getuid());
+
+ if (password) {
+ username = password->pw_name;
+ }
+ return username;
+}
+
+char * parse_cifs_url(unc_name)
+{
+ printf("\ncifs url %s\n",unc_name);
+}
+
+int parse_options(char * options)
+{
+ char * data;
+ char * value = 0;
+
+ if (!options)
+ return 1;
+
+ while ((data = strsep(&options, ",")) != NULL) {
+ if (!*data)
+ continue;
+ if ((value = strchr(data, '=')) != NULL) {
+ *value++ = '\0';
+ }
+ if (strncmp(data, "user", 4) == 0) {
+ if (!value || !*value) {
+ printf("invalid or missing username\n");
+ return 1; /* needs_arg; */
+ }
+ if (strnlen(value, 260) < 260) {
+ got_user=1;
+ /* BB add check for format user%pass */
+ /* if(strchr(username%passw) got_password = 1) */
+ } else {
+ printf("username too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "pass", 4) == 0) {
+ if (!value || !*value) {
+ if(got_password) {
+ printf("password specified twice, ignoring second\n");
+ } else
+ got_password = 1;
+ } else if (strnlen(value, 17) < 17) {
+ got_password = 1;
+ } else {
+ printf("password too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "ip", 2) == 0) {
+ if (!value || !*value) {
+ printf("target ip address argument missing");
+ } else if (strnlen(value, 35) < 35) {
+ got_ip = 1;
+ } else {
+ printf("ip address too long\n");
+ return 1;
+ }
+ } else if ((strncmp(data, "unc", 3) == 0)
+ || (strncmp(data, "target", 6) == 0)
+ || (strncmp(data, "path", 4) == 0)) {
+ if (!value || !*value) {
+ printf("invalid path to network resource\n");
+ return 1; /* needs_arg; */
+ } else if(strnlen(value,5) < 5) {
+ printf("UNC name too short");
+ }
+
+ if (strnlen(value, 300) < 300) {
+ got_unc = 1;
+ if (strncmp(value, "//", 2) == 0) {
+ if(got_unc)
+ printf("unc name specified twice, ignoring second\n");
+ else
+ got_unc = 1;
+ } else if (strncmp(value, "\\\\", 2) != 0) {
+ printf("UNC Path does not begin with // or \\\\ \n");
+ return 1;
+ } else {
+ if(got_unc)
+ printf("unc name specified twice, ignoring second\n");
+ else
+ got_unc = 1;
+ }
+ } else {
+ printf("CIFS: UNC name too long\n");
+ return 1;
+ }
+ } else if ((strncmp(data, "domain", 3) == 0)
+ || (strncmp(data, "workgroup", 5) == 0)) {
+ if (!value || !*value) {
+ printf("CIFS: invalid domain name\n");
+ return 1; /* needs_arg; */
+ }
+ if (strnlen(value, 65) < 65) {
+ got_domain = 1;
+ } else {
+ printf("domain name too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "uid", 3) == 0) {
+ if (value && *value) {
+ got_uid = 1;
+ }
+ } else if (strncmp(data, "gid", 3) == 0) {
+ if (value && *value) {
+ got_gid = 1;
+ }
+ } /* else if (strnicmp(data, "file_mode", 4) == 0) {
+ if (value && *value) {
+ vol->file_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "dir_mode", 3) == 0) {
+ if (value && *value) {
+ vol->dir_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "port", 4) == 0) {
+ if (value && *value) {
+ vol->port =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "rsize", 5) == 0) {
+ if (value && *value) {
+ vol->rsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "wsize", 5) == 0) {
+ if (value && *value) {
+ vol->wsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "version", 3) == 0) {
+
+ } else if (strnicmp(data, "rw", 2) == 0) {
+
+ } else
+ printf("CIFS: Unknown mount option %s\n",data); */
+ }
+ return 0;
+}
+
+/* Note that caller frees the returned buffer if necessary */
+char * parse_server(char * unc_name)
+{
+ int length = strnlen(unc_name,1024);
+ char * share;
+ char * ipaddress_string = NULL;
+ struct hostent * host_entry;
+ struct in_addr server_ipaddr;
+ int rc,j;
+ char temp[64];
+
+ if(length > 1023) {
+ printf("mount error: UNC name too long");
+ return 0;
+ }
+ if (strncasecmp("cifs://",unc_name,7) == 0)
+ return parse_cifs_url(unc_name+7);
+ if (strncasecmp("smb://",unc_name,6) == 0) {
+ return parse_cifs_url(unc_name+6);
+ }
+
+ if(length < 3) {
+ /* BB add code to find DFS root here */
+ printf("\nMounting the DFS root for domain not implemented yet");
+ return 0;
+ } else {
+ /* BB add support for \\\\ not just // */
+ if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
+ printf("mount error: improperly formatted UNC name.");
+ printf(" %s does not begin with \\\\ or //\n",unc_name);
+ return 0;
+ } else {
+ unc_name[0] = '\\';
+ unc_name[1] = '\\';
+ unc_name += 2;
+ if ((share = strchr(unc_name, '/')) ||
+ (share = strchr(unc_name,'\\'))) {
+ *share = 0; /* temporarily terminate the string */
+ share += 1;
+ host_entry = gethostbyname(unc_name);
+ *(share - 1) = '\\'; /* put the slash back */
+/* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
+ if(host_entry == NULL) {
+ printf("mount error: could not find target server. TCP name %s not found ", unc_name);
+ printf(" rc = %d\n",rc);
+ return 0;
+ }
+ else {
+ /* BB should we pass an alternate version of the share name as Unicode */
+ /* BB what about ipv6? BB */
+ /* BB add retries with alternate servers in list */
+
+ memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
+
+ ipaddress_string = inet_ntoa(server_ipaddr);
+ if(ipaddress_string == NULL) {
+ printf("mount error: could not get valid ip address for target server\n");
+ return 0;
+ }
+ return ipaddress_string;
+ }
+ } else {
+ /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
+ printf("Mounting the DFS root for a particular server not implemented yet\n");
+ return 0;
+ }
+ }
+ }
+}
+
+static struct option longopts[] = {
+ { "all", 0, 0, 'a' },
+ { "help", 0, 0, 'h' },
+ { "read-only", 0, 0, 'r' },
+ { "ro", 0, 0, 'r' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "read-write", 0, 0, 'w' },
+ { "rw", 0, 0, 'w' },
+ { "options", 1, 0, 'o' },
+ { "types", 1, 0, 't' },
+ { "replace", 0, 0, 129 },
+ { "after", 0, 0, 130 },
+ { "before", 0, 0, 131 },
+ { "over", 0, 0, 132 },
+ { "move", 0, 0, 133 },
+ { "rsize",1, 0, 136 },
+ { "wsize",1, 0, 137 },
+ { "uid", 1, 0, 138},
+ { "gid", 1, 0, 139},
+ { "uuid",1,0,'U' },
+ { "user",1,0,140},
+ { "username",1,0,140},
+ { "dom",1,0,141},
+ { "domain",1,0,141},
+ { "password",1,0,142},
+ { NULL, 0, 0, 0 }
+};
+
+int main(int argc, char ** argv)
+{
+ int c;
+ int flags = MS_MANDLOCK | MS_MGC_VAL;
+ char * orgoptions = NULL;
+ char * share_name = NULL;
+ char * domain_name = NULL;
+ char * ipaddr = NULL;
+ char * uuid = NULL;
+ char * mountpoint;
+ char * options;
+ int rc,i;
+ int rsize = 0;
+ int wsize = 0;
+ int nomtab = 0;
+ int uid = 0;
+ int gid = 0;
+ int optlen = 0;
+ struct stat statbuf;
+ struct utsname sysinfo;
+ struct mntent mountent;
+ FILE * pmntfile;
+
+ /* setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE); */
+
+ if(argc && argv) {
+ thisprogram = argv[0];
+ }
+ if(thisprogram == NULL)
+ thisprogram = "mount.cifs";
+
+ uname(&sysinfo);
+ /* BB add workstation name and domain and pass down */
+/*#ifdef _GNU_SOURCE
+ printf(" node: %s machine: %s\n", sysinfo.nodename,sysinfo.machine);
+#endif*/
+ if(argc < 3)
+ mount_cifs_usage();
+ share_name = argv[1];
+ mountpoint = argv[2];
+ /* add sharename in opts string as unc= parm */
+
+ while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
+ longopts, NULL)) != -1) {
+ switch (c) {
+/* case 'a':
+ ++mount_all;
+ break;
+ case 'f':
+ ++fake;
+ break;
+ case 'F':
+ ++optfork;
+ break; */
+ case 'h': /* help */
+ mount_cifs_usage ();
+ break;
+/* case 'i':
+ external_allowed = 0;
+ break;
+ case 'l':
+ list_with_volumelabel = 1;
+ break;
+ case 'L':
+ volumelabel = optarg;
+ break; */
+ case 'n':
+ ++nomtab;
+ break;
+ case 'o':
+ if (orgoptions) {
+ orgoptions = strcat(orgoptions, ",");
+ orgoptions = strcat(orgoptions,optarg);
+ } else
+ orgoptions = strdup(optarg);
+ break;
+
+/* case 'O':
+ if (test_opts)
+ test_opts = xstrconcat3(test_opts, ",", optarg);
+ else
+ test_opts = xstrdup(optarg);
+ break;*/
+ case 'r': /* mount readonly */
+ flags |= MS_RDONLY;;
+ break;
+ case 'U':
+ uuid = optarg;
+ break;
+ case 'v':
+ ++verboseflag;
+ break;
+/* case 'V':
+ printf ("mount: %s\n", version);
+ exit (0);*/
+ case 'w':
+ flags &= ~MS_RDONLY;;
+ break;
+/* case 0:
+ break;
+
+ case 128:
+ mounttype = MS_BIND;
+ break;
+ case 129:
+ mounttype = MS_REPLACE;
+ break;
+ case 130:
+ mounttype = MS_AFTER;
+ break;
+ case 131:
+ mounttype = MS_BEFORE;
+ break;
+ case 132:
+ mounttype = MS_OVER;
+ break;
+ case 133:
+ mounttype = MS_MOVE;
+ break;
+ case 135:
+ mounttype = (MS_BIND | MS_REC);
+ break; */
+ case 136:
+ rsize = atoi(optarg) ;
+ break;
+ case 137:
+ wsize = atoi(optarg);
+ break;
+ case 138:
+ uid = atoi(optarg);
+ break;
+ case 139:
+ gid = atoi(optarg);
+ break;
+ case 140:
+ got_user = 1;
+ user_name = optarg;
+ break;
+ case 141:
+ domain_name = optarg;
+ break;
+ case 142:
+ got_password = 1;
+ mountpassword = optarg;
+ break;
+ case '?':
+ default:
+ mount_cifs_usage ();
+ }
+ }
+
+ /* canonicalize the path in argv[1]? */
+
+ if(stat (mountpoint, &statbuf)) {
+ printf("mount error: mount point %s does not exist\n",mountpoint);
+ return -1;
+ }
+ if (S_ISDIR(statbuf.st_mode) == 0) {
+ printf("mount error: mount point %s is not a directory\n",mountpoint);
+ return -1;
+ }
+
+ if(geteuid()) {
+ printf("mount error: permission denied, not superuser and cifs.mount not installed SUID\n");
+ return -1;
+ }
+
+ ipaddr = parse_server(share_name);
+/* if(share_name == NULL)
+ return 1; */
+ if (parse_options(strdup(orgoptions)))
+ return 1;
+
+ if(got_user == 0)
+ user_name = getusername();
+
+/* check username for user%password format */
+
+ if(got_password == 0) {
+ if (getenv("PASSWD")) {
+ mountpassword = malloc(33);
+ if(mountpassword) {
+ strncpy(mountpassword,getenv("PASSWD"),32);
+ got_password = 1;
+ }
+/* } else if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+ get_password_file();
+ got_password = 1;*/ /* BB add missing function */
+ } else {
+ mountpassword = getpass("Password: "); /* BB obsolete */
+ got_password = 1;
+ }
+ }
+ /* FIXME launch daemon (handles dfs name resolution and credential change)
+ remember to clear parms and overwrite password field before launching */
+ if(orgoptions) {
+ optlen = strlen(orgoptions);
+ } else
+ optlen = 0;
+ if(share_name)
+ optlen += strlen(share_name) + 4;
+ if(user_name)
+ optlen += strlen(user_name) + 6;
+ if(ipaddr)
+ optlen += strlen(ipaddr) + 4;
+ if(mountpassword)
+ optlen += strlen(mountpassword) + 6;
+ options = malloc(optlen + 10);
+
+ options[0] = 0;
+ strncat(options,"unc=",4);
+ strcat(options,share_name);
+ if(ipaddr) {
+ strncat(options,",ip=",4);
+ strcat(options,ipaddr);
+ }
+ if(user_name) {
+ strncat(options,",user=",6);
+ strcat(options,user_name);
+ }
+ if(mountpassword) {
+ strncat(options,",pass=",6);
+ strcat(options,mountpassword);
+ }
+ strncat(options,",ver=",5);
+ strcat(options,MOUNT_CIFS_VERSION);
+
+ if(orgoptions) {
+ strcat(options,",");
+ strcat(options,orgoptions);
+ }
+ /* printf("\noptions %s \n",options);*/
+ if(mount(share_name, mountpoint, "cifs", flags, options)) {
+ /* remember to kill daemon on error */
+ switch (errno) {
+ case 0:
+ printf("mount failed but no error number set\n");
+ return 0;
+ case ENODEV:
+ printf("mount error: cifs filesystem not supported by the system\n");
+ break;
+ default:
+ printf("mount error %d = %s",errno,strerror(errno));
+ }
+ printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
+ return -1;
+ } else {
+ pmntfile = setmntent(MOUNTED, "a+");
+ if(pmntfile) {
+ mountent.mnt_fsname = share_name;
+ mountent.mnt_dir = mountpoint;
+ mountent.mnt_type = "cifs";
+ mountent.mnt_opts = "";
+ mountent.mnt_freq = 0;
+ mountent.mnt_passno = 0;
+ rc = addmntent(pmntfile,&mountent);
+ endmntent(pmntfile);
+ } else {
+ printf("could not update mount table\n");
+ }
+ }
+ return 0;
+}
+
diff --git a/source4/client/smbmnt.c b/source4/client/smbmnt.c
new file mode 100644
index 0000000000..0d619a88fe
--- /dev/null
+++ b/source4/client/smbmnt.c
@@ -0,0 +1,306 @@
+/*
+ * smbmnt.c
+ *
+ * Copyright (C) 1995-1998 by Paal-Kr. Engstad and Volker Lendecke
+ * extensively modified by Tridge
+ *
+ */
+
+#include "includes.h"
+
+#include <mntent.h>
+#include <sys/utsname.h>
+
+#include <asm/types.h>
+#include <asm/posix_types.h>
+#include <linux/smb.h>
+#include <linux/smb_mount.h>
+#include <asm/unistd.h>
+
+#ifndef MS_MGC_VAL
+/* This may look strange but MS_MGC_VAL is what we are looking for and
+ is what we need from <linux/fs.h> under libc systems and is
+ provided in standard includes on glibc systems. So... We
+ switch on what we need... */
+#include <linux/fs.h>
+#endif
+
+static uid_t mount_uid;
+static gid_t mount_gid;
+static int mount_ro;
+static uint_t mount_fmask;
+static uint_t mount_dmask;
+static int user_mount;
+static char *options;
+
+static void
+help(void)
+{
+ printf("\n");
+ printf("Usage: smbmnt mount-point [options]\n");
+ printf("Version %s\n\n",VERSION);
+ printf("-s share share name on server\n"
+ "-r mount read-only\n"
+ "-u uid mount as uid\n"
+ "-g gid mount as gid\n"
+ "-f mask permission mask for files\n"
+ "-d mask permission mask for directories\n"
+ "-o options name=value, list of options\n"
+ "-h print this help text\n");
+}
+
+static int
+parse_args(int argc, char *argv[], struct smb_mount_data *data, char **share)
+{
+ int opt;
+
+ while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF)
+ {
+ switch (opt)
+ {
+ case 's':
+ *share = optarg;
+ break;
+ case 'u':
+ if (!user_mount) {
+ mount_uid = strtol(optarg, NULL, 0);
+ }
+ break;
+ case 'g':
+ if (!user_mount) {
+ mount_gid = strtol(optarg, NULL, 0);
+ }
+ break;
+ case 'r':
+ mount_ro = 1;
+ break;
+ case 'f':
+ mount_fmask = strtol(optarg, NULL, 8);
+ break;
+ case 'd':
+ mount_dmask = strtol(optarg, NULL, 8);
+ break;
+ case 'o':
+ options = optarg;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return 0;
+
+}
+
+static char *
+fullpath(const char *p)
+{
+ char path[MAXPATHLEN];
+
+ if (strlen(p) > MAXPATHLEN-1) {
+ return NULL;
+ }
+
+ if (realpath(p, path) == NULL) {
+ fprintf(stderr,"Failed to find real path for mount point\n");
+ exit(1);
+ }
+ return strdup(path);
+}
+
+/* Check whether user is allowed to mount on the specified mount point. If it's
+ OK then we change into that directory - this prevents race conditions */
+static int mount_ok(char *mount_point)
+{
+ struct stat st;
+
+ if (chdir(mount_point) != 0) {
+ return -1;
+ }
+
+ if (stat(".", &st) != 0) {
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ if ((getuid() != 0) &&
+ ((getuid() != st.st_uid) ||
+ ((st.st_mode & S_IRWXU) != S_IRWXU))) {
+ errno = EPERM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Tries to mount using the appropriate format. For 2.2 the struct,
+ for 2.4 the ascii version. */
+static int
+do_mount(char *share_name, uint_t flags, struct smb_mount_data *data)
+{
+ pstring opts;
+ struct utsname uts;
+ char *release, *major, *minor;
+ char *data1, *data2;
+
+ uname(&uts);
+ release = uts.release;
+ major = strtok(release, ".");
+ minor = strtok(NULL, ".");
+ if (major && minor && atoi(major) == 2 && atoi(minor) < 4) {
+ /* < 2.4, assume struct */
+ data1 = (char *) data;
+ data2 = opts;
+ } else {
+ /* >= 2.4, assume ascii but fall back on struct */
+ data1 = opts;
+ data2 = (char *) data;
+ }
+
+ slprintf(opts, sizeof(opts)-1,
+ "version=7,uid=%d,gid=%d,file_mode=0%o,dir_mode=0%o,%s",
+ data->uid, data->gid, data->file_mode, data->dir_mode,options);
+ if (mount(share_name, ".", "smbfs", flags, data1) == 0)
+ return 0;
+ return mount(share_name, ".", "smbfs", flags, data2);
+}
+
+ int main(int argc, char *argv[])
+{
+ char *mount_point, *share_name = NULL;
+ FILE *mtab;
+ int fd;
+ uint_t flags;
+ struct smb_mount_data data;
+ struct mntent ment;
+
+ memset(&data, 0, sizeof(struct smb_mount_data));
+
+ if (argc < 2) {
+ help();
+ exit(1);
+ }
+
+ if (argv[1][0] == '-') {
+ help();
+ exit(1);
+ }
+
+ if (getuid() != 0) {
+ user_mount = 1;
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "smbmnt must be installed suid root for direct user mounts (%d,%d)\n", getuid(), geteuid());
+ exit(1);
+ }
+
+ mount_uid = getuid();
+ mount_gid = getgid();
+ mount_fmask = umask(0);
+ umask(mount_fmask);
+ mount_fmask = ~mount_fmask;
+
+ mount_point = fullpath(argv[1]);
+
+ argv += 1;
+ argc -= 1;
+
+ if (mount_ok(mount_point) != 0) {
+ fprintf(stderr, "cannot mount on %s: %s\n",
+ mount_point, strerror(errno));
+ exit(1);
+ }
+
+ data.version = SMB_MOUNT_VERSION;
+
+ /* getuid() gives us the real uid, who may umount the fs */
+ data.mounted_uid = getuid();
+
+ if (parse_args(argc, argv, &data, &share_name) != 0) {
+ help();
+ return -1;
+ }
+
+ data.uid = mount_uid;
+ data.gid = mount_gid;
+ data.file_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_fmask;
+ data.dir_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_dmask;
+
+ if (mount_dmask == 0) {
+ data.dir_mode = data.file_mode;
+ if ((data.dir_mode & S_IRUSR) != 0)
+ data.dir_mode |= S_IXUSR;
+ if ((data.dir_mode & S_IRGRP) != 0)
+ data.dir_mode |= S_IXGRP;
+ if ((data.dir_mode & S_IROTH) != 0)
+ data.dir_mode |= S_IXOTH;
+ }
+
+ flags = MS_MGC_VAL;
+
+ if (mount_ro) flags |= MS_RDONLY;
+
+ if (do_mount(share_name, flags, &data) < 0) {
+ switch (errno) {
+ case ENODEV:
+ fprintf(stderr, "ERROR: smbfs filesystem not supported by the kernel\n");
+ break;
+ default:
+ perror("mount error");
+ }
+ fprintf(stderr, "Please refer to the smbmnt(8) manual page\n");
+ return -1;
+ }
+
+ ment.mnt_fsname = share_name ? share_name : "none";
+ ment.mnt_dir = mount_point;
+ ment.mnt_type = "smbfs";
+ ment.mnt_opts = "";
+ ment.mnt_freq = 0;
+ ment.mnt_passno= 0;
+
+ mount_point = ment.mnt_dir;
+
+ if (mount_point == NULL)
+ {
+ fprintf(stderr, "Mount point too long\n");
+ return -1;
+ }
+
+ if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
+ {
+ fprintf(stderr, "Can't get "MOUNTED"~ lock file");
+ return 1;
+ }
+ close(fd);
+
+ if ((mtab = setmntent(MOUNTED, "a+")) == NULL)
+ {
+ fprintf(stderr, "Can't open " MOUNTED);
+ return 1;
+ }
+
+ if (addmntent(mtab, &ment) == 1)
+ {
+ fprintf(stderr, "Can't write mount entry");
+ return 1;
+ }
+ if (fchmod(fileno(mtab), 0644) == -1)
+ {
+ fprintf(stderr, "Can't set perms on "MOUNTED);
+ return 1;
+ }
+ endmntent(mtab);
+
+ if (unlink(MOUNTED"~") == -1)
+ {
+ fprintf(stderr, "Can't remove "MOUNTED"~");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/source4/client/smbmount.c b/source4/client/smbmount.c
new file mode 100644
index 0000000000..9ab6c375f2
--- /dev/null
+++ b/source4/client/smbmount.c
@@ -0,0 +1,930 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMBFS mount program
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/passwd.h"
+
+#include <mntent.h>
+#include <asm/types.h>
+#include <linux/smb_fs.h>
+
+static pstring credentials;
+static pstring my_netbios_name;
+static pstring password;
+static pstring username;
+static pstring workgroup;
+static pstring mpoint;
+static pstring service;
+static pstring options;
+
+static struct in_addr dest_ip;
+static bool have_ip;
+static int smb_port = 0;
+static bool got_user;
+static bool got_pass;
+static uid_t mount_uid;
+static gid_t mount_gid;
+static int mount_ro;
+static uint_t mount_fmask;
+static uint_t mount_dmask;
+static bool use_kerberos;
+/* TODO: Add code to detect smbfs version in kernel */
+static bool status32_smbfs = false;
+
+static void usage(void);
+
+static void exit_parent(int sig)
+{
+ /* parent simply exits when child says go... */
+ exit(0);
+}
+
+static void daemonize(void)
+{
+ int j, status;
+ pid_t child_pid;
+
+ signal( SIGTERM, exit_parent );
+
+ if ((child_pid = sys_fork()) < 0) {
+ DEBUG(0,("could not fork\n"));
+ }
+
+ if (child_pid > 0) {
+ while( 1 ) {
+ j = waitpid( child_pid, &status, 0 );
+ if( j < 0 ) {
+ if( EINTR == errno ) {
+ continue;
+ }
+ status = errno;
+ }
+ break;
+ }
+
+ /* If we get here - the child exited with some error status */
+ if (WIFSIGNALED(status))
+ exit(128 + WTERMSIG(status));
+ else
+ exit(WEXITSTATUS(status));
+ }
+
+ signal( SIGTERM, SIG_DFL );
+ chdir("/");
+}
+
+static void close_our_files(int client_fd)
+{
+ int i;
+ struct rlimit limits;
+
+ getrlimit(RLIMIT_NOFILE,&limits);
+ for (i = 0; i< limits.rlim_max; i++) {
+ if (i == client_fd)
+ continue;
+ close(i);
+ }
+}
+
+static void usr1_handler(int x)
+{
+ return;
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct smbcli_state *do_connection(const char *the_service, bool unicode, int maxprotocol)
+{
+ struct smbcli_state *c;
+ struct nmb_name called, calling;
+ char *server_n;
+ struct in_addr ip;
+ pstring server;
+ char *share;
+
+ if (the_service[0] != '\\' || the_service[1] != '\\') {
+ usage();
+ exit(1);
+ }
+
+ pstrcpy(server, the_service+2);
+ share = strchr_m(server,'\\');
+ if (!share) {
+ usage();
+ exit(1);
+ }
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ make_nmb_name(&calling, my_netbios_name, 0x0);
+ choose_called_name(&called, server, 0x20);
+
+ again:
+ zero_ip(&ip);
+ if (have_ip) ip = dest_ip;
+
+ /* have to open a new connection */
+ if (!(c=smbcli_initialise(NULL)) || (smbcli_set_port(c, smb_port) != smb_port) ||
+ !smbcli_connect(c, server_n, &ip)) {
+ DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
+ if (c) {
+ talloc_free(c);
+ }
+ return NULL;
+ }
+
+ /* SPNEGO doesn't work till we get NTSTATUS error support */
+ /* But it is REQUIRED for kerberos authentication */
+ if(!use_kerberos) c->use_spnego = false;
+
+ /* The kernel doesn't yet know how to sign it's packets */
+ c->sign_info.allow_smb_signing = false;
+
+ /* Use kerberos authentication if specified */
+ c->use_kerberos = use_kerberos;
+
+ if (!smbcli_session_request(c, &calling, &called)) {
+ char *p;
+ DEBUG(0,("%d: session request to %s failed (%s)\n",
+ sys_getpid(), called.name, smbcli_errstr(c)));
+ talloc_free(c);
+ if ((p=strchr_m(called.name, '.'))) {
+ *p = 0;
+ goto again;
+ }
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NULL;
+ }
+
+ DEBUG(4,("%d: session request ok\n", sys_getpid()));
+
+ if (!smbcli_negprot(c, unicode, maxprotocol)) {
+ DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
+ talloc_free(c);
+ return NULL;
+ }
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(password, pass);
+ }
+ }
+
+ /* This should be right for current smbfs. Future versions will support
+ large files as well as unicode and oplocks. */
+ if (status32_smbfs) {
+ c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+ CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
+ }
+ else {
+ c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+ CAP_NT_FIND | CAP_STATUS32 |
+ CAP_LEVEL_II_OPLOCKS);
+ c->force_dos_errors = true;
+ }
+
+ if (!smbcli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup)) {
+ /* if a password was not supplied then try again with a
+ null username */
+ if (password[0] || !username[0] ||
+ !smbcli_session_setup(c, "", "", 0, "", 0, workgroup)) {
+ DEBUG(0,("%d: session setup failed: %s\n",
+ sys_getpid(), smbcli_errstr(c)));
+ talloc_free(c);
+ return NULL;
+ }
+ DEBUG(0,("Anonymous login successful\n"));
+ }
+
+ DEBUG(4,("%d: session setup ok\n", sys_getpid()));
+
+ if (!smbcli_tconX(c, share, "?????", password, strlen(password)+1)) {
+ DEBUG(0,("%d: tree connect failed: %s\n",
+ sys_getpid(), smbcli_errstr(c)));
+ talloc_free(c);
+ return NULL;
+ }
+
+ DEBUG(4,("%d: tconx ok\n", sys_getpid()));
+
+ got_pass = true;
+
+ return c;
+}
+
+
+/****************************************************************************
+unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
+ Code blatently stolen from smbumount.c
+ -mhw-
+****************************************************************************/
+static void smb_umount(const char *mount_point)
+{
+ int fd;
+ struct mntent *mnt;
+ FILE* mtab;
+ FILE* new_mtab;
+
+ /* Programmers Note:
+ This routine only gets called to the scene of a disaster
+ to shoot the survivors... A connection that was working
+ has now apparently failed. We have an active mount point
+ (presumably) that we need to dump. If we get errors along
+ the way - make some noise, but we are already turning out
+ the lights to exit anyways...
+ */
+ if (umount(mount_point) != 0) {
+ DEBUG(0,("%d: Could not umount %s: %s\n",
+ sys_getpid(), mount_point, strerror(errno)));
+ return;
+ }
+
+ if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
+ DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
+ return;
+ }
+
+ close(fd);
+
+ if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
+ DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
+ sys_getpid(), strerror(errno)));
+ return;
+ }
+
+#define MOUNTED_TMP MOUNTED".tmp"
+
+ if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
+ DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
+ sys_getpid(), strerror(errno)));
+ endmntent(mtab);
+ return;
+ }
+
+ while ((mnt = getmntent(mtab)) != NULL) {
+ if (strcmp(mnt->mnt_dir, mount_point) != 0) {
+ addmntent(new_mtab, mnt);
+ }
+ }
+
+ endmntent(mtab);
+
+ if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ DEBUG(0,("%d: Error changing mode of %s: %s\n",
+ sys_getpid(), MOUNTED_TMP, strerror(errno)));
+ return;
+ }
+
+ endmntent(new_mtab);
+
+ if (rename(MOUNTED_TMP, MOUNTED) < 0) {
+ DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
+ sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
+ return;
+ }
+
+ if (unlink(MOUNTED"~") == -1) {
+ DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
+ return;
+ }
+}
+
+
+/*
+ * Call the smbfs ioctl to install a connection socket,
+ * then wait for a signal to reconnect. Note that we do
+ * not exit after open_sockets() or send_login() errors,
+ * as the smbfs mount would then have no way to recover.
+ */
+static void send_fs_socket(struct loadparm_context *lp_ctx,
+ const char *the_service, const char *mount_point, struct smbcli_state *c)
+{
+ int fd, closed = 0, res = 1;
+ pid_t parentpid = getppid();
+ struct smb_conn_opt conn_options;
+
+ memset(&conn_options, 0, sizeof(conn_options));
+
+ while (1) {
+ if ((fd = open(mount_point, O_RDONLY)) < 0) {
+ DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
+ sys_getpid(), mount_point));
+ break;
+ }
+
+ conn_options.fd = c->fd;
+ conn_options.protocol = c->protocol;
+ conn_options.case_handling = SMB_CASE_DEFAULT;
+ conn_options.max_xmit = c->max_xmit;
+ conn_options.server_uid = c->vuid;
+ conn_options.tid = c->cnum;
+ conn_options.secmode = c->sec_mode;
+ conn_options.rawmode = 0;
+ conn_options.sesskey = c->sesskey;
+ conn_options.maxraw = 0;
+ conn_options.capabilities = c->capabilities;
+ conn_options.serverzone = c->serverzone/60;
+
+ res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
+ if (res != 0) {
+ DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
+ sys_getpid(), res));
+ close(fd);
+ break;
+ }
+
+ if (parentpid) {
+ /* Ok... We are going to kill the parent. Now
+ is the time to break the process group... */
+ setsid();
+ /* Send a signal to the parent to terminate */
+ kill(parentpid, SIGTERM);
+ parentpid = 0;
+ }
+
+ close(fd);
+
+ /* This looks wierd but we are only closing the userspace
+ side, the connection has already been passed to smbfs and
+ it has increased the usage count on the socket.
+
+ If we don't do this we will "leak" sockets and memory on
+ each reconnection we have to make. */
+ talloc_free(c);
+ c = NULL;
+
+ if (!closed) {
+ /* redirect stdout & stderr since we can't know that
+ the library functions we use are using DEBUG. */
+ if ( (fd = open("/dev/null", O_WRONLY)) < 0)
+ DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
+ close_our_files(fd);
+ if (fd >= 0) {
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+
+ /* here we are no longer interactive */
+ set_remote_machine_name("smbmount"); /* sneaky ... */
+ setup_logging("mount.smbfs", DEBUG_STDERR);
+ reopen_logs();
+ DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
+
+ closed = 1;
+ }
+
+ /* Wait for a signal from smbfs ... but don't continue
+ until we actually get a new connection. */
+ while (!c) {
+ CatchSignal(SIGUSR1, &usr1_handler);
+ pause();
+ DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
+ c = do_connection(the_service,
+ lp_unicode(lp_ctx),
+ lp_cli_maxprotocol(lp_ctx));
+ }
+ }
+
+ smb_umount(mount_point);
+ DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
+ exit(1);
+}
+
+
+/**
+ * Mount a smbfs
+ **/
+static void init_mount(struct loadparm_context *lp_ctx)
+{
+ char mount_point[MAXPATHLEN+1];
+ pstring tmp;
+ pstring svc2;
+ struct smbcli_state *c;
+ char *args[20];
+ int i, status;
+
+ if (realpath(mpoint, mount_point) == NULL) {
+ fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
+ return;
+ }
+
+
+ c = do_connection(service, lp_unicode(lp_ctx), lp_cli_maxprotocol(lp_ctx));
+ if (!c) {
+ fprintf(stderr,"SMB connection failed\n");
+ exit(1);
+ }
+
+ /*
+ Set up to return as a daemon child and wait in the parent
+ until the child say it's ready...
+ */
+ daemonize();
+
+ pstrcpy(svc2, service);
+ string_replace(svc2, '\\','/');
+ string_replace(svc2, ' ','_');
+
+ memset(args, 0, sizeof(args[0])*20);
+
+ i=0;
+ args[i++] = "smbmnt";
+
+ args[i++] = mount_point;
+ args[i++] = "-s";
+ args[i++] = svc2;
+
+ if (mount_ro) {
+ args[i++] = "-r";
+ }
+ if (mount_uid) {
+ slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
+ args[i++] = "-u";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (mount_gid) {
+ slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
+ args[i++] = "-g";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (mount_fmask) {
+ slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
+ args[i++] = "-f";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (mount_dmask) {
+ slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
+ args[i++] = "-d";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (options) {
+ args[i++] = "-o";
+ args[i++] = options;
+ }
+
+ if (sys_fork() == 0) {
+ char *smbmnt_path;
+
+ asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
+
+ if (file_exist(smbmnt_path)) {
+ execv(smbmnt_path, args);
+ fprintf(stderr,
+ "smbfs/init_mount: execv of %s failed. Error was %s.",
+ smbmnt_path, strerror(errno));
+ } else {
+ execvp("smbmnt", args);
+ fprintf(stderr,
+ "smbfs/init_mount: execv of %s failed. Error was %s.",
+ "smbmnt", strerror(errno));
+ }
+ free(smbmnt_path);
+ exit(1);
+ }
+
+ if (waitpid(-1, &status, 0) == -1) {
+ fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
+ /* FIXME: do some proper error handling */
+ exit(1);
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+ fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
+ /* FIXME: do some proper error handling */
+ exit(1);
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
+ exit(1);
+ }
+
+ /* Ok... This is the rubicon for that mount point... At any point
+ after this, if the connections fail and can not be reconstructed
+ for any reason, we will have to unmount the mount point. There
+ is no exit from the next call...
+ */
+ send_fs_socket(lp_ctx, service, mount_point, c);
+}
+
+
+/****************************************************************************
+get a password from a a file or file descriptor
+exit on failure (from smbclient, move to libsmb or shared .c file?)
+****************************************************************************/
+static void get_password_file(void)
+{
+ int fd = -1;
+ char *p;
+ bool close_it = false;
+ pstring spec;
+ char pass[128];
+
+ if ((p = getenv("PASSWD_FD")) != NULL) {
+ pstrcpy(spec, "descriptor ");
+ pstrcat(spec, p);
+ sscanf(p, "%d", &fd);
+ close_it = false;
+ } else if ((p = getenv("PASSWD_FILE")) != NULL) {
+ fd = open(p, O_RDONLY, 0);
+ pstrcpy(spec, p);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
+ spec, strerror(errno));
+ exit(1);
+ }
+ close_it = true;
+ }
+
+ for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
+ p && p - pass < sizeof(pass);) {
+ switch (read(fd, p, 1)) {
+ case 1:
+ if (*p != '\n' && *p != '\0') {
+ *++p = '\0'; /* advance p, and null-terminate pass */
+ break;
+ }
+ case 0:
+ if (p - pass) {
+ *p = '\0'; /* null-terminate it, just in case... */
+ p = NULL; /* then force the loop condition to become false */
+ break;
+ } else {
+ fprintf(stderr, "Error reading password from file %s: %s\n",
+ spec, "empty password\n");
+ exit(1);
+ }
+
+ default:
+ fprintf(stderr, "Error reading password from file %s: %s\n",
+ spec, strerror(errno));
+ exit(1);
+ }
+ }
+ pstrcpy(password, pass);
+ if (close_it)
+ close(fd);
+}
+
+/****************************************************************************
+get username and password from a credentials file
+exit on failure (from smbclient, move to libsmb or shared .c file?)
+****************************************************************************/
+static void read_credentials_file(char *filename)
+{
+ FILE *auth;
+ fstring buf;
+ uint16_t len = 0;
+ char *ptr, *val, *param;
+
+ if ((auth=sys_fopen(filename, "r")) == NULL)
+ {
+ /* fail if we can't open the credentials file */
+ DEBUG(0,("ERROR: Unable to open credentials file!\n"));
+ exit (-1);
+ }
+
+ while (!feof(auth))
+ {
+ /* get a line from the file */
+ if (!fgets (buf, sizeof(buf), auth))
+ continue;
+ len = strlen(buf);
+
+ if ((len) && (buf[len-1]=='\n'))
+ {
+ buf[len-1] = '\0';
+ len--;
+ }
+ if (len == 0)
+ continue;
+
+ /* break up the line into parameter & value.
+ will need to eat a little whitespace possibly */
+ param = buf;
+ if (!(ptr = strchr (buf, '=')))
+ continue;
+ val = ptr+1;
+ *ptr = '\0';
+
+ /* eat leading white space */
+ while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+ val++;
+
+ if (strwicmp("password", param) == 0)
+ {
+ pstrcpy(password, val);
+ got_pass = true;
+ }
+ else if (strwicmp("username", param) == 0) {
+ pstrcpy(username, val);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ }
+ fclose(auth);
+}
+
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+static void usage(void)
+{
+ printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
+
+ printf("Version %s\n\n",VERSION);
+
+ printf(
+"Options:\n\
+ username=<arg> SMB username\n\
+ password=<arg> SMB password\n\
+ credentials=<filename> file with username/password\n\
+ krb use kerberos (active directory)\n\
+ netbiosname=<arg> source NetBIOS name\n\
+ uid=<arg> mount uid or username\n\
+ gid=<arg> mount gid or groupname\n\
+ port=<arg> remote SMB port number\n\
+ fmask=<arg> file umask\n\
+ dmask=<arg> directory umask\n\
+ debug=<arg> debug level\n\
+ ip=<arg> destination host or IP address\n\
+ workgroup=<arg> workgroup on destination\n\
+ sockopt=<arg> TCP socket options\n\
+ scope=<arg> NetBIOS scope\n\
+ iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
+ codepage=<arg> server codepage (cp850)\n\
+ ttl=<arg> dircache time to live\n\
+ guest don't prompt for a password\n\
+ ro mount read-only\n\
+ rw mount read-write\n\
+\n\
+This command is designed to be run from within /bin/mount by giving\n\
+the option '-t smbfs'. For example:\n\
+ mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
+");
+}
+
+
+/****************************************************************************
+ Argument parsing for mount.smbfs interface
+ mount will call us like this:
+ mount.smbfs device mountpoint -o <options>
+
+ <options> is never empty, containing at least rw or ro
+ ****************************************************************************/
+static void parse_mount_smb(int argc, char **argv)
+{
+ int opt;
+ char *opts;
+ char *opteq;
+ extern char *optarg;
+ int val;
+ char *p;
+
+ /* FIXME: This function can silently fail if the arguments are
+ * not in the expected order.
+
+ > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
+ > requires that one gives "-o" before further options like username=...
+ > . Without -o, the username=.. setting is *silently* ignored. I've
+ > spent about an hour trying to find out why I couldn't log in now..
+
+ */
+
+
+ if (argc < 2 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ pstrcpy(service, argv[1]);
+ pstrcpy(mpoint, argv[2]);
+
+ /* Convert any '/' characters in the service name to
+ '\' characters */
+ string_replace(service, '/','\\');
+ argc -= 2;
+ argv += 2;
+
+ opt = getopt(argc, argv, "o:");
+ if(opt != 'o') {
+ return;
+ }
+
+ options[0] = 0;
+ p = options;
+
+ /*
+ * option parsing from nfsmount.c (util-linux-2.9u)
+ */
+ for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
+ DEBUG(3, ("opts: %s\n", opts));
+ if ((opteq = strchr_m(opts, '='))) {
+ val = atoi(opteq + 1);
+ *opteq = '\0';
+
+ if (!strcmp(opts, "username") ||
+ !strcmp(opts, "logon")) {
+ char *lp;
+ got_user = true;
+ pstrcpy(username,opteq+1);
+ if ((lp=strchr_m(username,'%'))) {
+ *lp = 0;
+ pstrcpy(password,lp+1);
+ got_pass = true;
+ memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
+ }
+ if ((lp=strchr_m(username,'/'))) {
+ *lp = 0;
+ pstrcpy(workgroup,lp+1);
+ }
+ } else if(!strcmp(opts, "passwd") ||
+ !strcmp(opts, "password")) {
+ pstrcpy(password,opteq+1);
+ got_pass = true;
+ memset(opteq+1,'X',strlen(password));
+ } else if(!strcmp(opts, "credentials")) {
+ pstrcpy(credentials,opteq+1);
+ } else if(!strcmp(opts, "netbiosname")) {
+ pstrcpy(my_netbios_name,opteq+1);
+ } else if(!strcmp(opts, "uid")) {
+ mount_uid = nametouid(opteq+1);
+ } else if(!strcmp(opts, "gid")) {
+ mount_gid = nametogid(opteq+1);
+ } else if(!strcmp(opts, "port")) {
+ smb_port = val;
+ } else if(!strcmp(opts, "fmask")) {
+ mount_fmask = strtol(opteq+1, NULL, 8);
+ } else if(!strcmp(opts, "dmask")) {
+ mount_dmask = strtol(opteq+1, NULL, 8);
+ } else if(!strcmp(opts, "debug")) {
+ DEBUGLEVEL = val;
+ } else if(!strcmp(opts, "ip")) {
+ dest_ip = interpret_addr2(opteq+1);
+ if (is_zero_ip(dest_ip)) {
+ fprintf(stderr,"Can't resolve address %s\n", opteq+1);
+ exit(1);
+ }
+ have_ip = true;
+ } else if(!strcmp(opts, "workgroup")) {
+ pstrcpy(workgroup,opteq+1);
+ } else if(!strcmp(opts, "sockopt")) {
+ lp_set_cmdline("socket options", opteq+1);
+ } else if(!strcmp(opts, "scope")) {
+ lp_set_cmdline("netbios scope", opteq+1);
+ } else {
+ slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
+ p += strlen(p);
+ }
+ } else {
+ val = 1;
+ if(!strcmp(opts, "nocaps")) {
+ fprintf(stderr, "Unhandled option: %s\n", opteq+1);
+ exit(1);
+ } else if(!strcmp(opts, "guest")) {
+ *password = '\0';
+ got_pass = true;
+ } else if(!strcmp(opts, "krb")) {
+#ifdef HAVE_KRB5
+
+ use_kerberos = true;
+ if(!status32_smbfs)
+ fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
+#else
+ fprintf(stderr,"No kerberos support compiled in\n");
+ exit(1);
+#endif
+ } else if(!strcmp(opts, "rw")) {
+ mount_ro = 0;
+ } else if(!strcmp(opts, "ro")) {
+ mount_ro = 1;
+ } else {
+ strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
+ p += strlen(opts);
+ *p++ = ',';
+ *p = 0;
+ }
+ }
+ }
+
+ if (!*service) {
+ usage();
+ exit(1);
+ }
+
+ if (p != options) {
+ *(p-1) = 0; /* remove trailing , */
+ DEBUG(3,("passthrough options '%s'\n", options));
+ }
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ char *p;
+ struct loadparm_context *lp_ctx;
+
+ DEBUGLEVEL = 1;
+
+ /* here we are interactive, even if run from autofs */
+ setup_logging("mount.smbfs",DEBUG_STDERR);
+
+#if 0 /* JRA - Urban says not needed ? */
+ /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
+ is to not announce any unicode capabilities as current smbfs does
+ not support it. */
+ p = getenv("CLI_FORCE_ASCII");
+ if (p && !strcmp(p, "false"))
+ unsetenv("CLI_FORCE_ASCII");
+ else
+ setenv("CLI_FORCE_ASCII", "true", 1);
+#endif
+
+ if (getenv("USER")) {
+ pstrcpy(username,getenv("USER"));
+
+ if ((p=strchr_m(username,'%'))) {
+ *p = 0;
+ pstrcpy(password,p+1);
+ got_pass = true;
+ memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
+ }
+ strupper(username);
+ }
+
+ if (getenv("PASSWD")) {
+ pstrcpy(password, getenv("PASSWD"));
+ got_pass = true;
+ }
+
+ if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+ get_password_file();
+ got_pass = true;
+ }
+
+ if (*username == 0 && getenv("LOGNAME")) {
+ pstrcpy(username,getenv("LOGNAME"));
+ }
+
+ lp_ctx = loadparm_init(talloc_autofree_context());
+
+ if (!lp_load(lp_ctx, dyn_CONFIGFILE)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ lp_config_file());
+ }
+
+ parse_mount_smb(argc, argv);
+
+ if (use_kerberos && !got_user) {
+ got_pass = true;
+ }
+
+ if (*credentials != 0) {
+ read_credentials_file(credentials);
+ }
+
+ DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
+
+ if (*workgroup == 0) {
+ pstrcpy(workgroup, lp_workgroup());
+ }
+
+ if (!*my_netbios_name) {
+ pstrcpy(my_netbios_name, myhostname());
+ }
+ strupper(my_netbios_name);
+
+ init_mount(lp_ctx);
+ return 0;
+}
diff --git a/source4/client/smbspool.c b/source4/client/smbspool.c
new file mode 100644
index 0000000000..df867d5fef
--- /dev/null
+++ b/source4/client/smbspool.c
@@ -0,0 +1,353 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB backend for the Common UNIX Printing System ("CUPS")
+ Copyright 1999 by Easy Software Products
+ Copyright Andrew Tridgell 1994-1998
+ Copyright Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/*
+ * Local functions...
+ */
+
+static void list_devices(void);
+static struct smbcli_state *smb_connect(const char *, const char *, const char **, const char *, const char *, const char *);
+static int smb_print(struct smbcli_state *, char *, FILE *);
+
+
+/*
+ * 'main()' - Main entry for SMB backend.
+ */
+
+ int /* O - Exit status */
+ main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int copies; /* Number of copies */
+ char uri[1024], /* URI */
+ *sep, /* Pointer to separator */
+ *password; /* Password */
+ const char *username, /* Username */
+ *server, /* Server name */
+ *printer; /* Printer name */
+ const char *workgroup; /* Workgroup */
+ FILE *fp; /* File to print */
+ int status=0; /* Status of LPD job */
+ struct smbcli_state *cli; /* SMB interface */
+ struct loadparm_context *lp_ctx;
+
+ /* we expect the URI in argv[0]. Detect the case where it is in argv[1] and cope */
+ if (argc > 2 && strncmp(argv[0],"smb://", 6) && !strncmp(argv[1],"smb://", 6)) {
+ argv++;
+ argc--;
+ }
+
+ if (argc == 1)
+ {
+ /*
+ * NEW! In CUPS 1.1 the backends are run with no arguments to list the
+ * available devices. These can be devices served by this backend
+ * or any other backends (i.e. you can have an SNMP backend that
+ * is only used to enumerate the available network printers... :)
+ */
+
+ list_devices();
+ return (0);
+ }
+
+ if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s [DEVICE_URI] job-id user title copies options [file]\n",
+ argv[0]);
+ fputs(" The DEVICE_URI environment variable can also contain the\n", stderr);
+ fputs(" destination printer:\n", stderr);
+ fputs("\n", stderr);
+ fputs(" smb://[username:password@][workgroup/]server/printer\n", stderr);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, print data from stdin...
+ */
+
+ if (argc == 6)
+ {
+ /*
+ * Print from Copy stdin to a temporary file...
+ */
+
+ fp = stdin;
+ copies = 1;
+ }
+ else if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: Unable to open print file");
+ return (1);
+ }
+ else
+ copies = atoi(argv[4]);
+
+ /*
+ * Find the URI...
+ */
+
+ if (strncmp(argv[0], "smb://", 6) == 0)
+ strncpy(uri, argv[0], sizeof(uri) - 1);
+ else if (getenv("DEVICE_URI") != NULL)
+ strncpy(uri, getenv("DEVICE_URI"), sizeof(uri) - 1);
+ else
+ {
+ fputs("ERROR: No device URI found in argv[0] or DEVICE_URI environment variable!\n", stderr);
+ return (1);
+ }
+
+ uri[sizeof(uri) - 1] = '\0';
+
+ /*
+ * Extract the destination from the URI...
+ */
+
+ if ((sep = strrchr_m(uri, '@')) != NULL)
+ {
+ username = uri + 6;
+ *sep++ = '\0';
+
+ server = sep;
+
+ /*
+ * Extract password as needed...
+ */
+
+ if ((password = strchr_m(username, ':')) != NULL)
+ *password++ = '\0';
+ else
+ password = "";
+ }
+ else
+ {
+ username = "";
+ password = "";
+ server = uri + 6;
+ }
+
+ if ((sep = strchr_m(server, '/')) == NULL)
+ {
+ fputs("ERROR: Bad URI - need printer name!\n", stderr);
+ return (1);
+ }
+
+ *sep++ = '\0';
+ printer = sep;
+
+ if ((sep = strchr_m(printer, '/')) != NULL)
+ {
+ /*
+ * Convert to smb://[username:password@]workgroup/server/printer...
+ */
+
+ *sep++ = '\0';
+
+ workgroup = server;
+ server = printer;
+ printer = sep;
+ }
+ else
+ workgroup = NULL;
+
+ /*
+ * Setup the SAMBA server state...
+ */
+
+ setup_logging(argv[0], DEBUG_STDOUT);
+
+ lp_ctx = loadparm_init(talloc_autofree_context());
+
+ if (!lp_load(lp_ctx, dyn_CONFIGFILE)) {
+ fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", lp_config_file());
+ return (1);
+ }
+
+ if (workgroup == NULL)
+ workgroup = lp_workgroup(lp_ctx);
+
+ do
+ {
+ if ((cli = smb_connect(workgroup, server, lp_smb_ports(lp_ctx), printer, username, password)) == NULL)
+ {
+ if (getenv("CLASS") == NULL)
+ {
+ fprintf(stderr, "ERROR: Unable to connect to SAMBA host, will retry in 60 seconds...");
+ sleep (60);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to connect to SAMBA host, trying next printer...");
+ return (1);
+ }
+ }
+ }
+ while (cli == NULL);
+
+ /*
+ * Now that we are connected to the server, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (argc < 7)
+ CatchSignal(SIGTERM, SIG_IGN);
+
+ /*
+ * Queue the job...
+ */
+
+ for (i = 0; i < copies; i ++)
+ if ((status = smb_print(cli, argv[3] /* title */, fp)) != 0)
+ break;
+
+ talloc_free(cli);
+
+ /*
+ * Return the queue status...
+ */
+
+ return (status);
+}
+
+
+/*
+ * 'list_devices()' - List the available printers seen on the network...
+ */
+
+static void
+list_devices(void)
+{
+ /*
+ * Eventually, search the local workgroup for available hosts and printers.
+ */
+
+ puts("network smb \"Unknown\" \"Windows Printer via SAMBA\"");
+}
+
+
+/*
+ * 'smb_connect()' - Return a connection to a server.
+ */
+
+static struct smbcli_state * /* O - SMB connection */
+smb_connect(const char *workgroup, /* I - Workgroup */
+ const char *server, /* I - Server */
+ const char **ports, /* I - Ports */
+ const char *share, /* I - Printer */
+ const char *username, /* I - Username */
+ const char *password) /* I - Password */
+{
+ struct smbcli_state *c; /* New connection */
+ char *myname; /* Client name */
+ NTSTATUS nt_status;
+
+ /*
+ * Get the names and addresses of the client and server...
+ */
+
+ myname = get_myname();
+
+ nt_status = smbcli_full_connection(NULL, &c, myname, server, ports, share,
+ NULL, username, workgroup, password, NULL);
+
+ free(myname);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ fprintf(stderr, "ERROR: Connection failed with error %s\n", nt_errstr(nt_status));
+ return NULL;
+ }
+
+ /*
+ * Return the new connection...
+ */
+
+ return (c);
+}
+
+
+/*
+ * 'smb_print()' - Queue a job for printing using the SMB protocol.
+ */
+
+static int /* O - 0 = success, non-0 = failure */
+smb_print(struct smbcli_state *cli, /* I - SMB connection */
+ char *title, /* I - Title/job name */
+ FILE *fp) /* I - File to print */
+{
+ int fnum; /* File number */
+ int nbytes, /* Number of bytes read */
+ tbytes; /* Total bytes read */
+ char buffer[8192], /* Buffer for copy */
+ *ptr; /* Pointer into tile */
+
+
+ /*
+ * Sanitize the title...
+ */
+
+ for (ptr = title; *ptr; ptr ++)
+ if (!isalnum((int)*ptr) && !isspace((int)*ptr))
+ *ptr = '_';
+
+ /*
+ * Open the printer device...
+ */
+
+ if ((fnum = smbcli_open(cli, title, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE)) == -1)
+ {
+ fprintf(stderr, "ERROR: %s opening remote file %s\n",
+ smbcli_errstr(cli), title);
+ return (1);
+ }
+
+ /*
+ * Copy the file to the printer...
+ */
+
+ if (fp != stdin)
+ rewind(fp);
+
+ tbytes = 0;
+
+ while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+ {
+ if (smbcli_write(cli, fnum, 0, buffer, tbytes, nbytes) != nbytes)
+ {
+ fprintf(stderr, "ERROR: Error writing file: %s\n", smbcli_errstr(cli));
+ break;
+ }
+
+ tbytes += nbytes;
+ }
+
+ if (!smbcli_close(cli, fnum))
+ {
+ fprintf(stderr, "ERROR: %s closing remote file %s\n",
+ smbcli_errstr(cli), title);
+ return (1);
+ }
+ else
+ return (0);
+}
diff --git a/source4/client/smbumount.c b/source4/client/smbumount.c
new file mode 100644
index 0000000000..9ea3083a6f
--- /dev/null
+++ b/source4/client/smbumount.c
@@ -0,0 +1,186 @@
+/*
+ * smbumount.c
+ *
+ * Copyright (C) 1995-1998 by Volker Lendecke
+ *
+ */
+
+#include "includes.h"
+
+#include <mntent.h>
+
+#include <asm/types.h>
+#include <asm/posix_types.h>
+#include <linux/smb.h>
+#include <linux/smb_mount.h>
+#include <linux/smb_fs.h>
+
+/* This is a (hopefully) temporary hack due to the fact that
+ sizeof( uid_t ) != sizeof( __kernel_uid_t ) under glibc.
+ This may change in the future and smb.h may get fixed in the
+ future. In the mean time, it's ugly hack time - get over it.
+*/
+#undef SMB_IOC_GETMOUNTUID
+#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_uid_t)
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0400000
+#endif
+
+static void
+usage(void)
+{
+ printf("usage: smbumount mountpoint\n");
+}
+
+static int
+umount_ok(const char *mount_point)
+{
+ /* we set O_NOFOLLOW to prevent users playing games with symlinks to
+ umount filesystems they don't own */
+ int fid = open(mount_point, O_RDONLY|O_NOFOLLOW, 0);
+ __kernel_uid_t mount_uid;
+
+ if (fid == -1) {
+ fprintf(stderr, "Could not open %s: %s\n",
+ mount_point, strerror(errno));
+ return -1;
+ }
+
+ if (ioctl(fid, SMB_IOC_GETMOUNTUID, &mount_uid) != 0) {
+ fprintf(stderr, "%s probably not smb-filesystem\n",
+ mount_point);
+ return -1;
+ }
+
+ if ((getuid() != 0)
+ && (mount_uid != getuid())) {
+ fprintf(stderr, "You are not allowed to umount %s\n",
+ mount_point);
+ return -1;
+ }
+
+ close(fid);
+ return 0;
+}
+
+/* Make a canonical pathname from PATH. Returns a freshly malloced string.
+ It is up the *caller* to ensure that the PATH is sensible. i.e.
+ canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
+ is not a legal pathname for ``/dev/fd0'' Anything we cannot parse
+ we return unmodified. */
+static char *
+canonicalize (char *path)
+{
+ char *canonical = malloc (PATH_MAX + 1);
+
+ if (!canonical) {
+ fprintf(stderr, "Error! Not enough memory!\n");
+ return NULL;
+ }
+
+ if (strlen(path) > PATH_MAX) {
+ fprintf(stderr, "Mount point string too long\n");
+ return NULL;
+ }
+
+ if (path == NULL)
+ return NULL;
+
+ if (realpath (path, canonical))
+ return canonical;
+
+ strncpy (canonical, path, PATH_MAX);
+ canonical[PATH_MAX] = '\0';
+ return canonical;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ char* mount_point;
+ struct mntent *mnt;
+ FILE* mtab;
+ FILE* new_mtab;
+
+ if (argc != 2) {
+ usage();
+ exit(1);
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "smbumount must be installed suid root\n");
+ exit(1);
+ }
+
+ mount_point = canonicalize(argv[1]);
+
+ if (mount_point == NULL)
+ {
+ exit(1);
+ }
+
+ if (umount_ok(mount_point) != 0) {
+ exit(1);
+ }
+
+ if (umount(mount_point) != 0) {
+ fprintf(stderr, "Could not umount %s: %s\n",
+ mount_point, strerror(errno));
+ exit(1);
+ }
+
+ if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
+ {
+ fprintf(stderr, "Can't get "MOUNTED"~ lock file");
+ return 1;
+ }
+ close(fd);
+
+ if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
+ fprintf(stderr, "Can't open " MOUNTED ": %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+#define MOUNTED_TMP MOUNTED".tmp"
+
+ if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
+ fprintf(stderr, "Can't open " MOUNTED_TMP ": %s\n",
+ strerror(errno));
+ endmntent(mtab);
+ return 1;
+ }
+
+ while ((mnt = getmntent(mtab)) != NULL) {
+ if (strcmp(mnt->mnt_dir, mount_point) != 0) {
+ addmntent(new_mtab, mnt);
+ }
+ }
+
+ endmntent(mtab);
+
+ if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ fprintf(stderr, "Error changing mode of %s: %s\n",
+ MOUNTED_TMP, strerror(errno));
+ exit(1);
+ }
+
+ endmntent(new_mtab);
+
+ if (rename(MOUNTED_TMP, MOUNTED) < 0) {
+ fprintf(stderr, "Cannot rename %s to %s: %s\n",
+ MOUNTED, MOUNTED_TMP, strerror(errno));
+ exit(1);
+ }
+
+ if (unlink(MOUNTED"~") == -1)
+ {
+ fprintf(stderr, "Can't remove "MOUNTED"~");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/source4/client/tests/test_cifsdd.sh b/source4/client/tests/test_cifsdd.sh
new file mode 100755
index 0000000000..08bfb25e80
--- /dev/null
+++ b/source4/client/tests/test_cifsdd.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# Basic script to make sure that cifsdd can do both local and remote I/O.
+
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_cifsdd.sh SERVER USERNAME PASSWORD DOMAIN
+EOF
+exit 1;
+fi
+
+SERVER=$1
+USERNAME=$2
+PASSWORD=$3
+DOMAIN=$4
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+samba4bindir=`dirname $0`/../../bin
+DD=$samba4bindir/cifsdd
+
+SHARE=tmp
+DEBUGLEVEL=1
+
+runcopy() {
+ message="$1"
+ shift
+
+ testit "$message" $VALGRIND $DD $CONFIGURATION --debuglevel=$DEBUGLEVEL -W "$DOMAIN" -U "$USERNAME"%"$PASSWORD" \
+ "$@" || failed=`expr $failed + 1`
+}
+
+compare() {
+ testit "$1" cmp "$2" "$3" || failed=`expr $failed + 1`
+}
+
+sourcepath=tempfile.src.$$
+destpath=tempfile.dst.$$
+
+# Create a source file with arbitrary contents
+dd if=$DD of=$sourcepath bs=1024 count=50 > /dev/null
+
+ls -l $sourcepath
+
+for bs in 512 4k 48k ; do
+
+echo "Testing $bs block size ..."
+
+# Check whether we can do local IO
+runcopy "Testing local -> local copy" if=$sourcepath of=$destpath bs=$bs
+compare "Checking local differences" $sourcepath $destpath
+
+# Check whether we can do a round trip
+runcopy "Testing local -> remote copy" \
+ if=$sourcepath of=//$SERVER/$SHARE/$sourcepath bs=$bs
+runcopy "Testing remote -> local copy" \
+ if=//$SERVER/$SHARE/$sourcepath of=$destpath bs=$bs
+compare "Checking differences" $sourcepath $destpath
+
+# Check that copying within the remote server works
+runcopy "Testing local -> remote copy" \
+ if=//$SERVER/$SHARE/$sourcepath of=//$SERVER/$SHARE/$sourcepath bs=$bs
+runcopy "Testing remote -> remote copy" \
+ if=//$SERVER/$SHARE/$sourcepath of=//$SERVER/$SHARE/$destpath bs=$bs
+runcopy "Testing remote -> local copy" \
+ if=//$SERVER/$SHARE/$destpath of=$destpath bs=$bs
+compare "Checking differences" $sourcepath $destpath
+
+done
+
+rm -f $sourcepath $destpath
+
+exit $failed
diff --git a/source4/client/tests/test_smbclient.sh b/source4/client/tests/test_smbclient.sh
new file mode 100755
index 0000000000..7ff03ba6e2
--- /dev/null
+++ b/source4/client/tests/test_smbclient.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+# Blackbox tests for smbclient
+# Copyright (C) 2006-2007 Jelmer Vernooij <jelmer@samba.org>
+# Copyright (C) 2006-2007 Andrew Bartlett <abartlet@samba.org>
+
+if [ $# -lt 5 ]; then
+cat <<EOF
+Usage: test_smbclient.sh SERVER USERNAME PASSWORD DOMAIN PREFIX
+EOF
+exit 1;
+fi
+
+SERVER=$1
+USERNAME=$2
+PASSWORD=$3
+DOMAIN=$4
+PREFIX=$5
+shift 5
+failed=0
+
+samba4bindir=`dirname $0`/../../bin
+smbclient=$samba4bindir/smbclient
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+runcmd() {
+ name="$1"
+ cmd="$2"
+ shift
+ shift
+ echo "test: $name"
+ $VALGRIND $smbclient $CONFIGURATION //$SERVER/tmp -c "$cmd" -W "$DOMAIN" -U"$USERNAME%$PASSWORD" $@
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "success: $name"
+ else
+ echo "failure: $name"
+ fi
+ return $status
+}
+
+testit "share and server list" $VALGRIND $smbclient -L $SERVER $CONFIGURATION -W "$DOMAIN" -U"$USERNAME%$PASSWORD" $@ || failed=`expr $failed + 1`
+
+testit "share and server list anonymously" $VALGRIND $smbclient -N -L $SERVER $CONFIGURATION $@ || failed=`expr $failed + 1`
+
+# Generate random file
+cat >tmpfile<<EOF
+foo
+bar
+bloe
+blah
+EOF
+
+# put that file
+runcmd "MPutting file" 'mput tmpfile' || failed=`expr $failed + 1`
+# check file info
+runcmd "Getting alternative name" 'altname tmpfile'|| failed=`expr $failed + 1`
+# run allinfo on that file
+runcmd "Checking info on file" 'allinfo tmpfile'|| failed=`expr $failed + 1`
+# get that file
+mv tmpfile tmpfile-old
+runcmd "MGetting file" 'mget tmpfile' || failed=`expr $failed + 1`
+# remove that file
+runcmd "Removing file" 'rm tmpfile' || failed=`expr $failed + 1`
+# compare locally
+testit "Comparing files" diff tmpfile-old tmpfile || failed=`expr $failed + 1`
+# create directory
+# cd to directory
+# cd to top level directory
+# remove directory
+runcmd "Creating directory, Changing directory, Going back" 'mkdir bla; cd bla; cd ..; rmdir bla' || failed=`expr $failed + 1`
+# enable recurse, create nested directory
+runcmd "Creating nested directory" 'mkdir bla/bloe' || failed=`expr $failed + 1`
+# remove child directory
+runcmd "Removing directory" 'rmdir bla/bloe' || failed=`expr $failed + 1`
+# remove parent directory
+runcmd "Removing directory" 'rmdir bla'|| failed=`expr $failed + 1`
+# enable recurse, create nested directory
+runcmd "Creating nested directory" 'mkdir bla' || failed=`expr $failed + 1`
+# rename bla to bla2
+runcmd "rename of nested directory" 'rename bla bla2' || failed=`expr $failed + 1`
+# deltree
+runcmd "deltree of nested directory" 'deltree bla2' || failed=`expr $failed + 1`
+# run fsinfo
+runcmd "Getting file system info" 'fsinfo allocation'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo volume'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo volumeinfo'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo sizeinfo'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo deviceinfo'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo attributeinfo'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo volume-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo size-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo device-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo attribute-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo quota-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo fullsize-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo objectid'|| failed=`expr $failed + 1`
+
+# put that file
+runcmd "Putting file" 'put tmpfile'|| failed=`expr $failed + 1`
+# get that file
+mv tmpfile tmpfile-old
+runcmd "Getting file" 'get tmpfile'|| failed=`expr $failed + 1`
+runcmd "Getting file EA info" 'eainfo tmpfile'|| failed=`expr $failed + 1`
+# remove that file
+runcmd "Removing file" 'rm tmpfile' || failed=`expr $failed + 1`
+# compare locally
+testit "Comparing files" diff tmpfile-old tmpfile || failed=`expr $failed + 1`
+# put that file
+runcmd "Putting file with different name" 'put tmpfile tmpfilex' || failed=`expr $failed + 1`
+# get that file
+runcmd "Getting file again" 'get tmpfilex' || failed=`expr $failed + 1`
+# compare locally
+testit "Comparing files" diff tmpfilex tmpfile || failed=`expr $failed + 1`
+# remove that file
+runcmd "Removing file" 'rm tmpfilex'|| failed=`expr $failed + 1`
+
+runcmd "Lookup name" "lookup $DOMAIN\\$USERNAME" || failed=`expr $failed + 1`
+
+#Fails unless there are privilages
+#runcmd "Lookup privs of name" "privileges $DOMAIN\\$USERNAME" || failed=`expr $failed + 1`
+
+# do some simple operations using old protocol versions
+runcmd "List directory with LANMAN1" 'ls' -m LANMAN1 || failed=`expr $failed + 1`
+runcmd "List directory with LANMAN2" 'ls' -m LANMAN2 || failed=`expr $failed + 1`
+
+runcmd "Print current working directory" 'pwd'|| failed=`expr $failed + 1`
+
+(
+ echo "password=$PASSWORD"
+ echo "username=$USERNAME"
+ echo "domain=$DOMAIN"
+) > tmpauthfile
+
+testit "Test login with --authentication-file" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp --authentication-file=tmpauthfile || failed=`expr $failed + 1`
+
+PASSWD_FILE="tmppassfile"
+echo "$PASSWORD" > $PASSWD_FILE
+export PASSWD_FILE
+testit "Test login with PASSWD_FILE" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp -W "$DOMAIN" -U"$USERNAME" || failed=`expr $failed + 1`
+PASSWD_FILE=""
+export PASSWD_FILE
+unset PASSWD_FILE
+
+PASSWD="$PASSWORD"
+export PASSWD
+testit "Test login with PASSWD" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp -W "$DOMAIN" -U"$USERNAME" || failed=`expr $failed + 1`
+
+oldUSER=$USER
+USER="$USERNAME"
+export USER
+testit "Test login with USER and PASSWD" $VALGRIND $smbclient -k no -c 'ls' $CONFIGURATION //$SERVER/tmp -W "$DOMAIN" || failed=`expr $failed + 1`
+PASSWD=
+export PASSWD
+unset PASSWD
+USER=$oldUSER
+export USER
+
+rm -f tmpfile tmpfile-old tmpfilex tmpauthfile tmppassfile
+exit $failed
diff --git a/source4/client/tree.c b/source4/client/tree.c
new file mode 100644
index 0000000000..035192126c
--- /dev/null
+++ b/source4/client/tree.c
@@ -0,0 +1,809 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client GTK+ tree-based application
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2001
+ Copyright (C) John Terpstra 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* example-gtk+ application, ripped off from the gtk+ tree.c sample */
+
+#include <stdio.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include "libsmbclient.h"
+
+static GtkWidget *clist;
+
+struct tree_data {
+
+ guint32 type; /* Type of tree item, an SMBC_TYPE */
+ char name[256]; /* May need to change this later */
+
+};
+
+void error_message(gchar *message) {
+
+ GtkWidget *dialog, *label, *okay_button;
+
+ /* Create the widgets */
+
+ dialog = gtk_dialog_new();
+ gtk_window_set_modal(GTK_WINDOW(dialog), True);
+ label = gtk_label_new (message);
+ okay_button = gtk_button_new_with_label("Okay");
+
+ /* Ensure that the dialog box is destroyed when the user clicks ok. */
+
+ gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy), dialog);
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
+ okay_button);
+
+ /* Add the label, and show everything we've added to the dialog. */
+
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+ label);
+ gtk_widget_show_all (dialog);
+}
+
+/*
+ * We are given a widget, and we want to retrieve its URL so we
+ * can do a directory listing.
+ *
+ * We walk back up the tree, picking up pieces until we hit a server or
+ * workgroup type and return a path from there
+ */
+
+static char path_string[1024];
+
+char *get_path(GtkWidget *item)
+{
+ GtkWidget *p = item;
+ struct tree_data *pd;
+ char *comps[1024]; /* We keep pointers to the components here */
+ int i = 0, j, level,type;
+
+ /* Walk back up the tree, getting the private data */
+
+ level = GTK_TREE(item->parent)->level;
+
+ /* Pick up this item's component info */
+
+ pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(item));
+
+ comps[i++] = pd->name;
+ type = pd->type;
+
+ while (level > 0 && type != SMBC_SERVER && type != SMBC_WORKGROUP) {
+
+ /* Find the parent and extract the data etc ... */
+
+ p = GTK_WIDGET(p->parent);
+ p = GTK_WIDGET(GTK_TREE(p)->tree_owner);
+
+ pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(p));
+
+ level = GTK_TREE(item->parent)->level;
+
+ comps[i++] = pd->name;
+ type = pd->type;
+
+ }
+
+ /*
+ * Got a list of comps now, should check that we did not hit a workgroup
+ * when we got other things as well ... Later
+ *
+ * Now, build the path
+ */
+
+ snprintf(path_string, sizeof(path_string), "smb:/");
+
+ for (j = i - 1; j >= 0; j--) {
+
+ strncat(path_string, "/", sizeof(path_string) - strlen(path_string));
+ strncat(path_string, comps[j], sizeof(path_string) - strlen(path_string));
+
+ }
+
+ fprintf(stdout, "Path string = %s\n", path_string);
+
+ return path_string;
+
+}
+
+struct tree_data *make_tree_data(guint32 type, const char *name)
+{
+ struct tree_data *p = malloc_p(struct tree_data);
+
+ if (p) {
+
+ p->type = type;
+ strncpy(p->name, name, sizeof(p->name));
+
+ }
+
+ return p;
+
+}
+
+/* Note that this is called every time the user clicks on an item,
+ whether it is already selected or not. */
+static void cb_select_child (GtkWidget *root_tree, GtkWidget *child,
+ GtkWidget *subtree)
+{
+ gint dh, err, dirlen;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+ struct stat st1;
+ char path[1024], path1[1024];
+
+ g_print ("select_child called for root tree %p, subtree %p, child %p\n",
+ root_tree, subtree, child);
+
+ /* Now, figure out what it is, and display it in the clist ... */
+
+ gtk_clist_clear(GTK_CLIST(clist)); /* Clear the CLIST */
+
+ /* Now, get the private data for the subtree */
+
+ strncpy(path, get_path(child), 1024);
+
+ if ((dh = smbc_opendir(path)) < 0) { /* Handle error */
+
+ g_print("cb_select_child: Could not open dir %s, %s\n", path,
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) {
+
+ g_print("cb_select_child: Could not read dir %s, %s\n", path,
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while (err > 0) {
+ gchar col1[128], col2[128], col3[128], col4[128];
+ gchar *rowdata[4] = {col1, col2, col3, col4};
+
+ dirlen = dirp->dirlen;
+
+ /* Format each of the items ... */
+
+ strncpy(col1, dirp->name, 128);
+
+ col2[0] = col3[0] = col4[0] = (char)0;
+
+ switch (dirp->smbc_type) {
+
+ case SMBC_WORKGROUP:
+
+ break;
+
+ case SMBC_SERVER:
+
+ strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+
+ break;
+
+ case SMBC_FILE_SHARE:
+
+ strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+
+ break;
+
+ case SMBC_PRINTER_SHARE:
+
+ strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+ break;
+
+ case SMBC_COMMS_SHARE:
+
+ break;
+
+ case SMBC_IPC_SHARE:
+
+ break;
+
+ case SMBC_DIR:
+ case SMBC_FILE:
+
+ /* Get stats on the file/dir and see what we have */
+
+ if (!ISDOT(dirp->name) && !ISDOTDOT(dirp->name)) {
+
+ strncpy(path1, path, sizeof(path1));
+ strncat(path1, "/", sizeof(path) - strlen(path));
+ strncat(path1, dirp->name, sizeof(path) - strlen(path));
+
+ if (smbc_stat(path1, &st1) < 0) {
+
+ if (errno != EBUSY) {
+
+ g_print("cb_select_child: Could not stat file %s, %s\n", path1,
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+ else {
+
+ strncpy(col2, "Device or resource busy", sizeof(col2));
+
+ }
+ }
+ else {
+ /* Now format each of the relevant things ... */
+
+ snprintf(col2, sizeof(col2), "%c%c%c%c%c%c%c%c%c(%0X)",
+ (st1.st_mode&S_IRUSR?'r':'-'),
+ (st1.st_mode&S_IWUSR?'w':'-'),
+ (st1.st_mode&S_IXUSR?'x':'-'),
+ (st1.st_mode&S_IRGRP?'r':'-'),
+ (st1.st_mode&S_IWGRP?'w':'-'),
+ (st1.st_mode&S_IXGRP?'x':'-'),
+ (st1.st_mode&S_IROTH?'r':'-'),
+ (st1.st_mode&S_IWOTH?'w':'-'),
+ (st1.st_mode&S_IXOTH?'x':'-'),
+ st1.st_mode);
+ snprintf(col3, sizeof(col3), "%u", st1.st_size);
+ snprintf(col4, sizeof(col4), "%s", ctime(&st1.st_mtime));
+ }
+ }
+
+ break;
+
+ default:
+
+ break;
+ }
+
+ gtk_clist_append(GTK_CLIST(clist), rowdata);
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+}
+
+/* Note that this is never called */
+static void cb_unselect_child( GtkWidget *root_tree,
+ GtkWidget *child,
+ GtkWidget *subtree )
+{
+ g_print ("unselect_child called for root tree %p, subtree %p, child %p\n",
+ root_tree, subtree, child);
+}
+
+/* for all the GtkItem:: and GtkTreeItem:: signals */
+static void cb_itemsignal( GtkWidget *item,
+ gchar *signame )
+{
+ GtkWidget *real_tree, *aitem, *subtree;
+ gchar *name;
+ GtkLabel *label;
+ gint dh, err, dirlen, level;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+
+ label = GTK_LABEL (GTK_BIN (item)->child);
+ /* Get the text of the label */
+ gtk_label_get (label, &name);
+
+ level = GTK_TREE(item->parent)->level;
+
+ /* Get the level of the tree which the item is in */
+ g_print ("%s called for item %s->%p, level %d\n", signame, name,
+ item, GTK_TREE (item->parent)->level);
+
+ real_tree = GTK_TREE_ITEM_SUBTREE(item); /* Get the subtree */
+
+ if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
+ char server[128];
+
+ if ((dh = smbc_opendir(get_path(item))) < 0) { /* Handle error */
+ gchar errmsg[256];
+
+ g_print("cb_itemsignal: Could not open dir %s, %s\n", get_path(item),
+ strerror(errno));
+
+ slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not open dir %s, %s\n", get_path(item), strerror(errno));
+
+ error_message(errmsg);
+
+ /* gtk_main_quit();*/
+
+ return;
+
+ }
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) { /* An error, report it */
+ gchar errmsg[256];
+
+ g_print("cb_itemsignal: Could not read dir smbc://, %s\n",
+ strerror(errno));
+
+ slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not read dir smbc://, %s\n", strerror(errno));
+
+ error_message(errmsg);
+
+ /* gtk_main_quit();*/
+
+ return;
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while (err > 0) {
+ struct tree_data *my_data;
+
+ dirlen = dirp->dirlen;
+
+ my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+ if (!my_data) {
+
+ g_print("Could not allocate space for tree_data: %s\n",
+ dirp->name);
+
+ gtk_main_quit();
+ return;
+
+ }
+
+ aitem = gtk_tree_item_new_with_label(dirp->name);
+
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(aitem), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(aitem), "expand",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+ gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+ /* Add it to the parent tree */
+ gtk_tree_append (GTK_TREE(real_tree), aitem);
+
+ gtk_widget_show (aitem);
+
+ gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
+
+ fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+ if (dirp->smbc_type != SMBC_FILE &&
+ dirp->smbc_type != SMBC_IPC_SHARE &&
+ (!ISDOT(dirp->name)) &&
+ (!ISDOTDOT(dirp->name))){
+
+ subtree = gtk_tree_new();
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
+
+ gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+ }
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+ smbc_closedir(dh);
+
+ }
+ else if (strncmp(signame, "collapse", 8) == 0) {
+ GtkWidget *subtree = gtk_tree_new();
+
+ gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+ }
+
+}
+
+static void cb_selection_changed( GtkWidget *tree )
+{
+ GList *i;
+
+ g_print ("selection_change called for tree %p\n", tree);
+ g_print ("selected objects are:\n");
+
+ i = GTK_TREE_SELECTION(tree);
+ while (i){
+ gchar *name;
+ GtkLabel *label;
+ GtkWidget *item;
+
+ /* Get a GtkWidget pointer from the list node */
+ item = GTK_WIDGET (i->data);
+ label = GTK_LABEL (GTK_BIN (item)->child);
+ gtk_label_get (label, &name);
+ g_print ("\t%s on level %d\n", name, GTK_TREE
+ (item->parent)->level);
+ i = i->next;
+ }
+}
+
+/*
+ * Expand or collapse the whole network ...
+ */
+static void cb_wholenet(GtkWidget *item, gchar *signame)
+{
+ GtkWidget *real_tree, *aitem, *subtree;
+ gchar *name;
+ GtkLabel *label;
+ gint dh, err, dirlen;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+
+ label = GTK_LABEL (GTK_BIN (item)->child);
+ gtk_label_get (label, &name);
+ g_print ("%s called for item %s->%p, level %d\n", signame, name,
+ item, GTK_TREE (item->parent)->level);
+
+ real_tree = GTK_TREE_ITEM_SUBTREE(item); /* Get the subtree */
+
+ if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
+
+ if ((dh = smbc_opendir("smb://")) < 0) { /* Handle error */
+
+ g_print("cb_wholenet: Could not open dir smbc://, %s\n",
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) { /* An error, report it */
+
+ g_print("cb_wholenet: Could not read dir smbc://, %s\n",
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while (err > 0) {
+ struct tree_data *my_data;
+
+ dirlen = dirp->dirlen;
+
+ my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+ aitem = gtk_tree_item_new_with_label(dirp->name);
+
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(aitem), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(aitem), "expand",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+ gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+
+ gtk_tree_append (GTK_TREE(real_tree), aitem);
+ /* Show it - this can be done at any time */
+ gtk_widget_show (aitem);
+
+ gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
+
+ fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+ subtree = gtk_tree_new();
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
+
+ gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+ smbc_closedir(dh);
+
+ }
+ else { /* Must be collapse ... FIXME ... */
+ GtkWidget *subtree = gtk_tree_new();
+
+ gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+
+ }
+
+}
+
+/* Should put up a dialog box to ask the user for username and password */
+
+static void
+auth_fn(const char *server, const char *share,
+ char *workgroup, int wgmaxlen, char *username, int unmaxlen,
+ char *password, int pwmaxlen)
+{
+
+ strncpy(username, "test", unmaxlen);
+ strncpy(password, "test", pwmaxlen);
+
+}
+
+static char *col_titles[] = {
+ "Name", "Attributes", "Size", "Modification Date",
+};
+
+int main( int argc,
+ char *argv[] )
+{
+ GtkWidget *window, *scrolled_win, *scrolled_win2, *tree;
+ GtkWidget *subtree, *item, *main_hbox, *r_pane, *l_pane;
+ gint err, dh;
+ gint i;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+
+ gtk_init (&argc, &argv);
+
+ /* Init the smbclient library */
+
+ err = smbc_init(auth_fn, 10);
+
+ /* Print an error response ... */
+
+ if (err < 0) {
+
+ fprintf(stderr, "smbc_init returned %s (%i)\nDo you have a ~/.smb/smb.conf file?\n", strerror(errno), errno);
+ exit(1);
+
+ }
+
+ /* a generic toplevel window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name(window, "main browser window");
+ gtk_signal_connect (GTK_OBJECT(window), "delete_event",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_window_set_title(GTK_WINDOW(window), "The Linux Windows Network Browser");
+ gtk_widget_set_usize(GTK_WIDGET(window), 750, -1);
+ gtk_container_set_border_width (GTK_CONTAINER(window), 5);
+
+ gtk_widget_show (window);
+
+ /* A container for the two panes ... */
+
+ main_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(main_hbox), 1);
+ gtk_container_add(GTK_CONTAINER(window), main_hbox);
+
+ gtk_widget_show(main_hbox);
+
+ l_pane = gtk_hpaned_new();
+ gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
+ r_pane = gtk_hpaned_new();
+ gtk_paned_gutter_size(GTK_PANED(r_pane), (GTK_PANED(r_pane))->handle_size);
+ gtk_container_add(GTK_CONTAINER(main_hbox), l_pane);
+ gtk_widget_show(l_pane);
+
+ /* A generic scrolled window */
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_usize (scrolled_win, 150, 200);
+ gtk_container_add (GTK_CONTAINER(l_pane), scrolled_win);
+ gtk_widget_show (scrolled_win);
+
+ /* Another generic scrolled window */
+ scrolled_win2 = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win2),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_usize (scrolled_win2, 150, 200);
+ gtk_paned_add2 (GTK_PANED(l_pane), scrolled_win2);
+ gtk_widget_show (scrolled_win2);
+
+ /* Create the root tree */
+ tree = gtk_tree_new();
+ g_print ("root tree is %p\n", tree);
+ /* connect all GtkTree:: signals */
+ gtk_signal_connect (GTK_OBJECT(tree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), tree);
+ gtk_signal_connect (GTK_OBJECT(tree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+ gtk_signal_connect (GTK_OBJECT(tree), "selection_changed",
+ GTK_SIGNAL_FUNC(cb_selection_changed), tree);
+ /* Add it to the scrolled window */
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrolled_win),
+ tree);
+ /* Set the selection mode */
+ gtk_tree_set_selection_mode (GTK_TREE(tree),
+ GTK_SELECTION_MULTIPLE);
+ /* Show it */
+ gtk_widget_show (tree);
+
+ /* Now, create a clist and attach it to the second pane */
+
+ clist = gtk_clist_new_with_titles(4, col_titles);
+
+ gtk_container_add (GTK_CONTAINER(scrolled_win2), clist);
+
+ gtk_widget_show(clist);
+
+ /* Now, build the top level display ... */
+
+ if ((dh = smbc_opendir("smb:///")) < 0) {
+
+ fprintf(stderr, "Could not list default workgroup: smb:///: %s\n",
+ strerror(errno));
+
+ exit(1);
+
+ }
+
+ /* Create a tree item for Whole Network */
+
+ item = gtk_tree_item_new_with_label ("Whole Network");
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(item), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(item), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(item), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(item), "expand",
+ GTK_SIGNAL_FUNC(cb_wholenet), "expand");
+ gtk_signal_connect (GTK_OBJECT(item), "collapse",
+ GTK_SIGNAL_FUNC(cb_wholenet), "collapse");
+ /* Add it to the parent tree */
+ gtk_tree_append (GTK_TREE(tree), item);
+ /* Show it - this can be done at any time */
+ gtk_widget_show (item);
+
+ subtree = gtk_tree_new(); /* A subtree for Whole Network */
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+
+ /* Now, get the items in smb:/// and add them to the tree */
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) { /* Handle the error */
+
+ fprintf(stderr, "Could not read directory for smbc:///: %s\n",
+ strerror(errno));
+
+ exit(1);
+
+ }
+
+ fprintf(stdout, "Dir len: %u\n", err);
+
+ while (err > 0) { /* Extract each entry and make a sub-tree */
+ struct tree_data *my_data;
+ int dirlen = dirp->dirlen;
+
+ my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+ item = gtk_tree_item_new_with_label(dirp->name);
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(item), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(item), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(item), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(item), "expand",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+ gtk_signal_connect (GTK_OBJECT(item), "collapse",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+ /* Add it to the parent tree */
+ gtk_tree_append (GTK_TREE(tree), item);
+ /* Show it - this can be done at any time */
+ gtk_widget_show (item);
+
+ gtk_object_set_user_data(GTK_OBJECT(item), (gpointer)my_data);
+
+ fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+ subtree = gtk_tree_new();
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+ smbc_closedir(dh); /* FIXME, check for error :-) */
+
+ /* Show the window and loop endlessly */
+ gtk_main();
+ return 0;
+}
+/* example-end */