mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
r13255: New CIFS dd client for use in performance testing. The guts of this is
in client/cifsdd*, which implements a minimal implementation of dd. The
IO path is careful to always perform IO at the requested block size.
There is a very basic test suite in script/tests/test_cifsdd.sh which
covers local and remote IO at a variety of block sizes.
Added to lib/util_str.c is a small set of conv_str_*() functions to
convert strings to the corresponding type.
smbcli_parse_unc is modified to insert NULL terminators after its
hostname and sharename parameters. This allows it to correctly parse a
path of the form //foo/share/path/file.
(This used to be commit cd2f94a658
)
This commit is contained in:
parent
99f0659f67
commit
60f8666ae8
599
source4/client/cifsdd.c
Normal file
599
source4/client/cifsdd.c
Normal file
@ -0,0 +1,599 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "lib/cmdline/popt_common.h"
|
||||
|
||||
#include "cifsdd.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.
|
||||
*/
|
||||
};
|
||||
|
||||
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, BOOL);
|
||||
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 const 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(const char * which)
|
||||
{
|
||||
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(path, check_arg_numeric("ibs"),
|
||||
options);
|
||||
} else if (strcmp(which, "of") == 0) {
|
||||
options |= DD_WRITE;
|
||||
path = check_arg_pathname("of");
|
||||
handle = dd_open_path(path, check_arg_numeric("obs"),
|
||||
options);
|
||||
} else {
|
||||
SMB_ASSERT(0);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
|
||||
}
|
||||
|
||||
return(handle);
|
||||
}
|
||||
|
||||
static void set_max_xmit(uint64_t iomax)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)iomax);
|
||||
lp_set_cmdline("max xmit", buf);
|
||||
}
|
||||
|
||||
static int copy_files(void)
|
||||
{
|
||||
uint8_t * iobuf; /* IO buffer. */
|
||||
uint64_t iomax; /* Size of the IO buffer. */
|
||||
uint64_t iosz; /* Amount of data in the IO buffer. */
|
||||
|
||||
uint64_t ibs;
|
||||
uint64_t obs;
|
||||
uint64_t count;
|
||||
|
||||
struct dd_iohandle * ifile;
|
||||
struct dd_iohandle * ofile;
|
||||
|
||||
ibs = check_arg_numeric("ibs");
|
||||
obs = check_arg_numeric("obs");
|
||||
count = check_arg_numeric("count");
|
||||
|
||||
/* 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(iomax)) == NULL) {
|
||||
fprintf(stderr,
|
||||
"%s: failed to allocate IO buffer of %llu bytes\n",
|
||||
PROGNAME, (unsigned long long)iomax);
|
||||
return(EOM_EXIT_CODE);
|
||||
}
|
||||
|
||||
set_max_xmit(MAX(ibs, obs));
|
||||
|
||||
DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
|
||||
(unsigned long long)iomax, lp_max_xmit()));
|
||||
|
||||
if (!(ifile = open_file("if"))) {
|
||||
return(FILESYS_EXIT_CODE);
|
||||
}
|
||||
|
||||
if (!(ofile = open_file("of"))) {
|
||||
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", lp_max_xmit()));
|
||||
|
||||
for (iosz = 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)iosz));
|
||||
while (iosz > 0) {
|
||||
if (!dd_flush_block(ofile, iobuf,
|
||||
&iosz, 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, &iosz, obs, ibs)) {
|
||||
return(IOERROR_EXIT_CODE);
|
||||
}
|
||||
|
||||
if (iosz == 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 (!dd_flush_block(ofile, iobuf, &iosz, 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 },
|
||||
POPT_TABLEEND
|
||||
} ;
|
||||
|
||||
int main(int argc, const char ** argv)
|
||||
{
|
||||
int i;
|
||||
const char ** dd_args;
|
||||
|
||||
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
|
||||
POPT_TABLEEND
|
||||
};
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
gensec_init();
|
||||
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());
|
||||
}
|
||||
|
||||
/* vim: set sw=8 sts=8 ts=8 tw=79 : */
|
99
source4/client/cifsdd.h
Normal file
99
source4/client/cifsdd.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
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 dd_iohandle * dd_open_path(const char * path,
|
||||
uint64_t iosz, int options);
|
||||
BOOL dd_fill_block(struct dd_iohandle * h, uint8_t * buf,
|
||||
uint64_t * bufsz, uint64_t needsz, uint64_t blocksz);
|
||||
BOOL dd_flush_block(struct dd_iohandle * h, uint8_t * buf,
|
||||
uint64_t * bufsz, uint64_t blocksz);
|
||||
|
||||
/* vim: set sw=8 sts=8 ts=8 tw=79 : */
|
456
source4/client/cifsddio.c
Normal file
456
source4/client/cifsddio.c
Normal file
@ -0,0 +1,456 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#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 iosz, 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)
|
||||
oflags |= O_DIRECT;
|
||||
|
||||
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 smb_handle
|
||||
{
|
||||
struct dd_iohandle h;
|
||||
struct smbcli_state * cli;
|
||||
int fnum;
|
||||
uint64_t offset;
|
||||
};
|
||||
|
||||
#define IO_HANDLE_TO_SMB(h) ((struct smb_handle *)(h))
|
||||
|
||||
BOOL smb_seek_func(void * handle, uint64_t offset)
|
||||
{
|
||||
IO_HANDLE_TO_SMB(handle)->offset = offset;
|
||||
return(True);
|
||||
}
|
||||
|
||||
BOOL smb_read_func(void * handle, uint8_t * buf,
|
||||
uint64_t wanted, uint64_t * actual)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
union smb_read r;
|
||||
struct smb_handle * smbh;
|
||||
|
||||
ZERO_STRUCT(r);
|
||||
smbh = IO_HANDLE_TO_SMB(handle);
|
||||
|
||||
r.generic.level = RAW_READ_READX;
|
||||
r.readx.in.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);
|
||||
}
|
||||
|
||||
BOOL smb_write_func(void * handle, uint8_t * buf,
|
||||
uint64_t wanted, uint64_t * actual)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
union smb_write w;
|
||||
struct smb_handle * smbh;
|
||||
|
||||
ZERO_STRUCT(w);
|
||||
smbh = IO_HANDLE_TO_SMB(handle);
|
||||
|
||||
w.generic.level = RAW_WRITE_WRITEX;
|
||||
w.writex.in.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(const char * host, const char * share)
|
||||
{
|
||||
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, share,
|
||||
NULL /* devtype */, cmdline_credentials, NULL /* events */);
|
||||
|
||||
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.fnum);
|
||||
}
|
||||
|
||||
static struct dd_iohandle * open_smb_handle(const char * host, const char * share,
|
||||
const char * path, uint64_t iosz, int options)
|
||||
{
|
||||
struct smb_handle * smbh;
|
||||
|
||||
DEBUG(4, ("opening SMB stream to //%s/%s for %s\n",
|
||||
host, share, path));
|
||||
|
||||
if ((smbh = talloc_zero(NULL, struct smb_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(host, share)) == 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(const char * path, uint64_t iosz, int options)
|
||||
{
|
||||
if (file_exist(path)) {
|
||||
return(open_fd_handle(path, iosz, 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_smb_handle(host, share, remain,
|
||||
iosz, options));
|
||||
}
|
||||
|
||||
return(open_fd_handle(path, iosz, options));
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill the buffer till it has at least needsz bytes. Use read operations of
|
||||
* blocksz bytes. Return the number of bytes read and fill bufsz with the new
|
||||
* buffer size.
|
||||
*
|
||||
* NOTE: The IO buffer is guaranteed to be big enough to fit needsz + blocksz
|
||||
* bytes into it.
|
||||
*/
|
||||
BOOL dd_fill_block(struct dd_iohandle * h, uint8_t * buf,
|
||||
uint64_t * bufsz, uint64_t needsz, uint64_t blocksz)
|
||||
{
|
||||
uint64_t readsz;
|
||||
|
||||
SMB_ASSERT(blocksz > 0);
|
||||
SMB_ASSERT(needsz > 0);
|
||||
|
||||
while (*bufsz < needsz) {
|
||||
|
||||
if (!h->io_read(h, buf + (*bufsz), blocksz, &readsz)) {
|
||||
return(False);
|
||||
}
|
||||
|
||||
if (readsz == 0) {
|
||||
h->io_flags |= DD_END_OF_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG(6, ("added %llu bytes to IO buffer (need %llu bytes)\n",
|
||||
(unsigned long long)readsz, (unsigned long long)needsz));
|
||||
|
||||
*bufsz += readsz;
|
||||
dd_stats.in.bytes += readsz;
|
||||
|
||||
if (readsz == blocksz) {
|
||||
dd_stats.in.fblocks++;
|
||||
} else {
|
||||
DEBUG(3, ("partial read of %llu bytes (expected %llu)\n",
|
||||
(unsigned long long)readsz, (unsigned long long)blocksz));
|
||||
dd_stats.in.pblocks++;
|
||||
}
|
||||
}
|
||||
|
||||
return(True);
|
||||
}
|
||||
|
||||
/* Flush a buffer that contains bufsz bytes. Use writes of blocksz to do it,
|
||||
* and shift any remaining bytes back to the head of the buffer when there are
|
||||
* no more blocksz sized IOs left.
|
||||
*/
|
||||
BOOL dd_flush_block(struct dd_iohandle * h, uint8_t * buf,
|
||||
uint64_t * bufsz, uint64_t blocksz)
|
||||
{
|
||||
uint64_t writesz;
|
||||
uint64_t totalsz = 0;
|
||||
|
||||
SMB_ASSERT(blocksz > 0);
|
||||
|
||||
/* We have explicitly been asked to write a partial block. */
|
||||
if ((*bufsz) < blocksz) {
|
||||
|
||||
if (!h->io_write(h, buf, *bufsz, &writesz)) {
|
||||
return(False);
|
||||
}
|
||||
|
||||
if (writesz == 0) {
|
||||
fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
|
||||
PROGNAME);
|
||||
return(False);
|
||||
}
|
||||
|
||||
totalsz += writesz;
|
||||
dd_stats.out.bytes += writesz;
|
||||
dd_stats.out.pblocks++;
|
||||
}
|
||||
|
||||
/* Write as many full blocks as there are in the buffer. */
|
||||
while (((*bufsz) - totalsz) >= blocksz) {
|
||||
|
||||
if (!h->io_write(h, buf + totalsz, blocksz, &writesz)) {
|
||||
return(False);
|
||||
}
|
||||
|
||||
if (writesz == 0) {
|
||||
fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
|
||||
PROGNAME);
|
||||
return(False);
|
||||
}
|
||||
|
||||
if (writesz == blocksz) {
|
||||
dd_stats.out.fblocks++;
|
||||
} else {
|
||||
dd_stats.out.pblocks++;
|
||||
}
|
||||
|
||||
totalsz += writesz;
|
||||
dd_stats.out.bytes += writesz;
|
||||
|
||||
DEBUG(6, ("flushed %llu bytes from IO buffer of %llu bytes (%llu remain)\n",
|
||||
(unsigned long long)blocksz, (unsigned long long)blocksz,
|
||||
(unsigned long long)(blocksz - totalsz)));
|
||||
}
|
||||
|
||||
SMB_ASSERT(totalsz > 0);
|
||||
|
||||
/* We have flushed as much of the IO buffer as we can while
|
||||
* still doing blocksz-sized operations. Shift any remaining data
|
||||
* to the front of the IO buffer.
|
||||
*/
|
||||
if ((*bufsz) > totalsz) {
|
||||
uint64_t remain = (*bufsz) - totalsz;
|
||||
|
||||
DEBUG(3, ("shifting %llu remainder bytes to IO buffer head\n",
|
||||
(unsigned long long)remain));
|
||||
|
||||
memmove(buf, buf + totalsz, remain);
|
||||
(*bufsz) = remain;
|
||||
} else if ((*bufsz) == totalsz) {
|
||||
(*bufsz) = 0;
|
||||
} else {
|
||||
/* Else buffer contains bufsz bytes that we will append
|
||||
* to next time round.
|
||||
*/
|
||||
DEBUG(3, ("%llu unflushed bytes left in IO buffer\n",
|
||||
(unsigned long long)(*bufsz)));
|
||||
}
|
||||
|
||||
return(True);
|
||||
}
|
||||
|
||||
/* vim: set sw=8 sts=8 ts=8 tw=79 : */
|
@ -18,3 +18,20 @@ REQUIRED_SUBSYSTEMS = \
|
||||
POPT_CREDENTIALS
|
||||
# End BINARY smbclient
|
||||
#################################
|
||||
|
||||
#################################
|
||||
# Start BINARY cifsdd
|
||||
[BINARY::cifsdd]
|
||||
INSTALLDIR = BINDIR
|
||||
OBJ_FILES = \
|
||||
cifsdd.o \
|
||||
cifsddio.o
|
||||
REQUIRED_SUBSYSTEMS = \
|
||||
CONFIG \
|
||||
LIBSMB \
|
||||
LIBPOPT \
|
||||
POPT_SAMBA \
|
||||
POPT_CREDENTIALS
|
||||
# End BINARY sdd
|
||||
#################################
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
Copyright (C) Andrew Tridgell 1992-2001
|
||||
Copyright (C) Simo Sorce 2001-2002
|
||||
Copyright (C) Martin Pool 2003
|
||||
Copyright (C) James Peach 2005
|
||||
|
||||
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
|
||||
@ -1136,3 +1137,73 @@ BOOL set_boolean(const char *boolean_string, BOOL *boolean)
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL conv_str_bool(const char * str, BOOL * val)
|
||||
{
|
||||
char * end = NULL;
|
||||
long lval;
|
||||
|
||||
if (str == NULL || *str == '\0') {
|
||||
return False;
|
||||
}
|
||||
|
||||
lval = strtol(str, &end, 10 /* base */);
|
||||
if (end == NULL || *end != '\0' || end == str) {
|
||||
return set_boolean(str, val);
|
||||
}
|
||||
|
||||
*val = (lval) ? True : False;
|
||||
return True;
|
||||
}
|
||||
|
||||
/* Convert a size specification like 16K into an integral number of bytes. */
|
||||
BOOL conv_str_size(const char * str, uint64_t * val)
|
||||
{
|
||||
char * end = NULL;
|
||||
unsigned long long lval;
|
||||
|
||||
if (str == NULL || *str == '\0') {
|
||||
return False;
|
||||
}
|
||||
|
||||
lval = strtoull(str, &end, 10 /* base */);
|
||||
if (end == NULL || end == str) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (*end) {
|
||||
if (strwicmp(end, "K") == 0) {
|
||||
lval *= 1024ULL;
|
||||
} else if (strwicmp(end, "M") == 0) {
|
||||
lval *= (1024ULL * 1024ULL);
|
||||
} else if (strwicmp(end, "G") == 0) {
|
||||
lval *= (1024ULL * 1024ULL * 1024ULL);
|
||||
} else if (strwicmp(end, "T") == 0) {
|
||||
lval *= (1024ULL * 1024ULL * 1024ULL * 1024ULL);
|
||||
} else if (strwicmp(end, "P") == 0) {
|
||||
lval *= (1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL);
|
||||
} else {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
*val = (uint64_t)lval;
|
||||
return True;
|
||||
}
|
||||
|
||||
BOOL conv_str_u64(const char * str, uint64_t * val)
|
||||
{
|
||||
char * end = NULL;
|
||||
unsigned long long lval;
|
||||
|
||||
if (str == NULL || *str == '\0') {
|
||||
return False;
|
||||
}
|
||||
|
||||
lval = strtoull(str, &end, 10 /* base */);
|
||||
if (end == NULL || *end != '\0' || end == str) {
|
||||
return False;
|
||||
}
|
||||
|
||||
*val = (uint64_t)lval;
|
||||
return True;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
client connect/disconnect routines
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003-2005
|
||||
Copyright (C) James Peach 2005
|
||||
|
||||
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
|
||||
@ -175,11 +176,33 @@ struct smbcli_state *smbcli_state_init(TALLOC_CTX *mem_ctx)
|
||||
return talloc_zero(mem_ctx, struct smbcli_state);
|
||||
}
|
||||
|
||||
/* Insert a NULL at the first separator of the given path and return a pointer
|
||||
* to the location it was inserted at.
|
||||
*/
|
||||
static char *
|
||||
terminate_path_at_separator(char * path)
|
||||
{
|
||||
char * p;
|
||||
|
||||
if ((p = strchr_m(path, '/'))) {
|
||||
*p = '\0';
|
||||
return(p);
|
||||
}
|
||||
|
||||
if ((p = strchr_m(path, '\\'))) {
|
||||
*p = '\0';
|
||||
return(p);
|
||||
}
|
||||
|
||||
/* No terminator. Return pointer to the last byte. */
|
||||
return(p + strlen(path));
|
||||
}
|
||||
|
||||
/*
|
||||
parse a //server/share type UNC name
|
||||
*/
|
||||
BOOL smbcli_parse_unc(const char *unc_name, TALLOC_CTX *mem_ctx,
|
||||
const char **hostname, const char **sharename)
|
||||
char **hostname, char **sharename)
|
||||
{
|
||||
char *p;
|
||||
|
||||
@ -189,13 +212,10 @@ BOOL smbcli_parse_unc(const char *unc_name, TALLOC_CTX *mem_ctx,
|
||||
}
|
||||
|
||||
*hostname = talloc_strdup(mem_ctx, &unc_name[2]);
|
||||
p = strchr_m(&(*hostname)[2],'/');
|
||||
if (!p) {
|
||||
p = strchr_m(&(*hostname)[2],'\\');
|
||||
if (!p) return False;
|
||||
}
|
||||
*p = 0;
|
||||
p = terminate_path_at_separator(*hostname);
|
||||
|
||||
*sharename = talloc_strdup(mem_ctx, p+1);
|
||||
p = terminate_path_at_separator(*sharename);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
84
source4/script/tests/test_cifsdd.sh
Executable file
84
source4/script/tests/test_cifsdd.sh
Executable file
@ -0,0 +1,84 @@
|
||||
#!/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
|
||||
|
||||
DD=bin/cifsdd
|
||||
|
||||
SHARE=tmp
|
||||
DEBUGLEVEL=4
|
||||
|
||||
failed=0
|
||||
|
||||
failtest() {
|
||||
failed=`expr $failed + 1`
|
||||
}
|
||||
|
||||
runcopy() {
|
||||
message="$1"
|
||||
shift
|
||||
|
||||
testit "$message" $DD --debuglevel=$DEBUGLEVEL -W "$DOMAIN" -U "$USERNAME"%"$PASSWORD" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
compare() {
|
||||
if [ -r $1 -a -r $2 ] ; then
|
||||
sum1=`sum $1`
|
||||
sum2=`sum $2`
|
||||
|
||||
[[ x"$sum1" = x"$sum2" ]]
|
||||
else
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
incdir=`dirname $0`
|
||||
. $incdir/test_functions.sh
|
||||
|
||||
sourcepath=tempfile.src.$$
|
||||
destpath=tempfile.dst.$$
|
||||
|
||||
# Create a source file with arbitrary contents
|
||||
cp $DD $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 || failtest
|
||||
compare $sourcepath $destpath || failtest
|
||||
|
||||
# Check whether we can do a round trip
|
||||
runcopy "Testing local -> remote copy" \
|
||||
if=$sourcepath of=//$SERVER/$SHARE/$sourcepath bs=$bs || failtest
|
||||
runcopy "Testing remote ->local copy" \
|
||||
if=//$SERVER/$SHARE/$sourcepath of=$destpath bs=$bs || failtest
|
||||
compare $sourcepath $destpath || failtest
|
||||
|
||||
# Check that copying within the remote server works
|
||||
runcopy "Testing local -> remote copy" \
|
||||
if=//$SERVER/$SHARE/$sourcepath of=//$SERVER/$SHARE/$sourcepath bs=$bs || failtest
|
||||
runcopy "Testing remote -> remote copy" \
|
||||
if=//$SERVER/$SHARE/$sourcepath of=//$SERVER/$SHARE/$destpath bs=$bs || failtest
|
||||
runcopy "Testing remote ->local copy" \
|
||||
if=//$SERVER/$SHARE/$destpath of=$destpath bs=$bs || failtest
|
||||
compare $sourcepath $destpath || failtest
|
||||
|
||||
done
|
||||
|
||||
rm -f $sourcepath $destpath
|
||||
|
||||
testok $0 $failed
|
@ -10,3 +10,4 @@
|
||||
$SRCDIR/script/tests/test_local.sh || failed=`expr $failed + $?`
|
||||
$SRCDIR/script/tests/test_pidl.sh || failed=`expr $failed + $?`
|
||||
$SRCDIR/script/tests/test_smbclient.sh $SERVER $USERNAME $PASSWORD $DOMAIN $PREFIX || failed=`expr $failed + $?`
|
||||
$SRCDIR/script/tests/test_cifsdd.sh $SERVER $USERNAME $PASSWORD $DOMAIN || failed=`expr $failed + $?`
|
||||
|
2
source4/script/tests/tests_client.sh
Executable file
2
source4/script/tests/tests_client.sh
Executable file
@ -0,0 +1,2 @@
|
||||
$SRCDIR/script/tests/test_smbclient.sh $SERVER $USERNAME $PASSWORD $DOMAIN $PREFIX || failed=`expr $failed + $?`
|
||||
$SRCDIR/script/tests/test_cifsdd.sh $SERVER $USERNAME $PASSWORD $DOMAIN || failed=`expr $failed + $?`
|
Loading…
Reference in New Issue
Block a user