3 Main program, argument handling and block copying.
5 Copyright (C) James Peach 2005-2006
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "system/filesys.h"
23 #include "auth/gensec/gensec.h"
24 #include "lib/cmdline/popt_common.h"
25 #include "libcli/resolve/resolve.h"
26 #include "libcli/raw/libcliraw.h"
29 #include "param/param.h"
31 const char * const PROGNAME = "cifsdd";
33 #define SYNTAX_EXIT_CODE 1 /* Invokation syntax or logic error. */
34 #define EOM_EXIT_CODE 9 /* Out of memory error. */
35 #define FILESYS_EXIT_CODE 10 /* File manipulation error. */
36 #define IOERROR_EXIT_CODE 11 /* Error during IO phase. */
38 struct dd_stats_record dd_stats;
41 static int dd_sigusr1;
43 static void dd_handle_signal(int sig)
58 /* ------------------------------------------------------------------------- */
59 /* Argument handling. */
60 /* ------------------------------------------------------------------------- */
62 static const char * argtype_str(enum argtype arg_type)
65 enum argtype arg_type;
66 const char * arg_name;
69 { ARG_NUMERIC, "COUNT" },
71 { ARG_PATHNAME, "FILE" },
72 { ARG_BOOL, "BOOLEAN" },
77 for (i = 0; i < ARRAY_SIZE(names); ++i) {
78 if (arg_type == names[i].arg_type) {
79 return(names[i].arg_name);
86 static struct argdef args[] =
88 { "bs", ARG_SIZE, "force ibs and obs to SIZE bytes" },
89 { "ibs", ARG_SIZE, "read SIZE bytes at a time" },
90 { "obs", ARG_SIZE, "write SIZE bytes at a time" },
92 { "count", ARG_NUMERIC, "copy COUNT input blocks" },
93 { "seek",ARG_NUMERIC, "skip COUNT blocks at start of output" },
94 { "skip",ARG_NUMERIC, "skip COUNT blocks at start of input" },
96 { "if", ARG_PATHNAME, "read input from FILE" },
97 { "of", ARG_PATHNAME, "write output to FILE" },
99 { "direct", ARG_BOOL, "use direct I/O if non-zero" },
100 { "sync", ARG_BOOL, "use synchronous writes if non-zero" },
101 { "oplock", ARG_BOOL, "take oplocks on the input and output files" },
103 /* FIXME: We should support using iflags and oflags for setting oplock and I/O
104 * options. This would make us compatible with GNU dd.
108 static struct argdef * find_named_arg(const char * arg)
112 for (i = 0; i < ARRAY_SIZE(args); ++i) {
113 if (strwicmp(arg, args[i].arg_name) == 0) {
121 int set_arg_argv(const char * argv)
128 if ((name = strdup(argv)) == NULL) {
132 if ((val = strchr(name, '=')) == NULL) {
133 fprintf(stderr, "%s: malformed argument \"%s\"\n",
141 if ((arg = find_named_arg(name)) == NULL) {
142 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
147 /* Found a matching name; convert the variable argument. */
148 switch (arg->arg_type) {
150 if (!conv_str_u64(val, &arg->arg_val.nval)) {
155 if (!conv_str_size(val, &arg->arg_val.nval)) {
160 if (!conv_str_bool(val, &arg->arg_val.bval)) {
165 if (!(arg->arg_val.pval = strdup(val))) {
170 fprintf(stderr, "%s: argument \"%s\" is of "
171 "unknown type\n", PROGNAME, name);
183 void set_arg_val(const char * name, ...)
189 if ((arg = find_named_arg(name)) == NULL) {
193 /* Found a matching name; convert the variable argument. */
194 switch (arg->arg_type) {
197 arg->arg_val.nval = va_arg(ap, uint64_t);
200 arg->arg_val.bval = va_arg(ap, int);
203 arg->arg_val.pval = va_arg(ap, char *);
204 if (arg->arg_val.pval) {
205 arg->arg_val.pval = strdup(arg->arg_val.pval);
209 fprintf(stderr, "%s: argument \"%s\" is of "
210 "unknown type\n", PROGNAME, name);
218 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
224 bool check_arg_bool(const char * name)
228 if ((arg = find_named_arg(name)) &&
229 (arg->arg_type == ARG_BOOL)) {
230 return(arg->arg_val.bval);
233 DEBUG(0, ("invalid argument name: %s", name));
238 uint64_t check_arg_numeric(const char * name)
242 if ((arg = find_named_arg(name)) &&
243 (arg->arg_type == ARG_NUMERIC || arg->arg_type == ARG_SIZE)) {
244 return(arg->arg_val.nval);
247 DEBUG(0, ("invalid argument name: %s", name));
252 const char * check_arg_pathname(const char * name)
256 if ((arg = find_named_arg(name)) &&
257 (arg->arg_type == ARG_PATHNAME)) {
258 return(arg->arg_val.pval);
261 DEBUG(0, ("invalid argument name: %s", name));
266 static void dump_args(void)
270 DEBUG(10, ("dumping argument values:\n"));
271 for (i = 0; i < ARRAY_SIZE(args); ++i) {
272 switch (args[i].arg_type) {
275 DEBUG(10, ("\t%s=%llu\n", args[i].arg_name,
276 (unsigned long long)args[i].arg_val.nval));
279 DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
280 args[i].arg_val.bval ? "yes" : "no"));
283 DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
284 args[i].arg_val.pval ?
285 args[i].arg_val.pval :
294 static void cifsdd_help_message(poptContext pctx,
295 enum poptCallbackReason preason,
296 struct poptOption * poption,
300 static const char notes[] =
301 "FILE can be a local filename or a UNC path of the form //server/share/path.\n";
306 if (poption->shortName != '?') {
307 poptPrintUsage(pctx, stdout, 0);
308 fprintf(stdout, " [dd options]\n");
312 poptPrintHelp(pctx, stdout, 0);
313 fprintf(stdout, "\nCIFS dd options:\n");
315 for (i = 0; i < ARRAY_SIZE(args); ++i) {
316 if (args[i].arg_name == NULL) {
320 snprintf(prefix, sizeof(prefix), "%s=%-*s",
322 (int)(sizeof(prefix) - strlen(args[i].arg_name) - 2),
323 argtype_str(args[i].arg_type));
324 prefix[sizeof(prefix) - 1] = '\0';
325 fprintf(stdout, " %s%s\n", prefix, args[i].arg_help);
328 fprintf(stdout, "\n%s\n", notes);
332 /* ------------------------------------------------------------------------- */
333 /* Main block copying routine. */
334 /* ------------------------------------------------------------------------- */
336 static void print_transfer_stats(void)
338 if (DEBUGLEVEL > 0) {
339 printf("%llu+%llu records in (%llu bytes)\n"
340 "%llu+%llu records out (%llu bytes)\n",
341 (unsigned long long)dd_stats.in.fblocks,
342 (unsigned long long)dd_stats.in.pblocks,
343 (unsigned long long)dd_stats.in.bytes,
344 (unsigned long long)dd_stats.out.fblocks,
345 (unsigned long long)dd_stats.out.pblocks,
346 (unsigned long long)dd_stats.out.bytes);
348 printf("%llu+%llu records in\n%llu+%llu records out\n",
349 (unsigned long long)dd_stats.in.fblocks,
350 (unsigned long long)dd_stats.in.pblocks,
351 (unsigned long long)dd_stats.out.fblocks,
352 (unsigned long long)dd_stats.out.pblocks);
356 static struct dd_iohandle * open_file(struct resolve_context *resolve_ctx,
357 const char * which, const char **ports,
358 struct smbcli_options *smb_options)
361 const char * path = NULL;
362 struct dd_iohandle * handle = NULL;
364 if (check_arg_bool("direct")) {
365 options |= DD_DIRECT_IO;
368 if (check_arg_bool("sync")) {
369 options |= DD_SYNC_IO;
372 if (check_arg_bool("oplock")) {
373 options |= DD_OPLOCK;
376 if (strcmp(which, "if") == 0) {
377 path = check_arg_pathname("if");
378 handle = dd_open_path(resolve_ctx, path, ports,
379 check_arg_numeric("ibs"), options,
381 } else if (strcmp(which, "of") == 0) {
383 path = check_arg_pathname("of");
384 handle = dd_open_path(resolve_ctx, path, ports,
385 check_arg_numeric("obs"), options,
393 fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
399 static int copy_files(struct loadparm_context *lp_ctx)
401 uint8_t * iobuf; /* IO buffer. */
402 uint64_t iomax; /* Size of the IO buffer. */
403 uint64_t data_size; /* Amount of data in the IO buffer. */
409 struct dd_iohandle * ifile;
410 struct dd_iohandle * ofile;
412 struct smbcli_options options;
414 ibs = check_arg_numeric("ibs");
415 obs = check_arg_numeric("obs");
416 count = check_arg_numeric("count");
418 lp_smbcli_options(lp_ctx, &options);
420 /* Allocate IO buffer. We need more than the max IO size because we
421 * could accumulate a remainder if ibs and obs don't match.
423 iomax = 2 * MAX(ibs, obs);
424 if ((iobuf = malloc_array_p(uint8_t, iomax)) == NULL) {
426 "%s: failed to allocate IO buffer of %llu bytes\n",
427 PROGNAME, (unsigned long long)iomax);
428 return(EOM_EXIT_CODE);
431 options.max_xmit = MAX(ibs, obs);
433 DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
434 (unsigned long long)iomax, options.max_xmit));
436 if (!(ifile = open_file(lp_resolve_context(lp_ctx), "if",
437 lp_smb_ports(lp_ctx), &options))) {
438 return(FILESYS_EXIT_CODE);
441 if (!(ofile = open_file(lp_resolve_context(lp_ctx), "of",
442 lp_smb_ports(lp_ctx), &options))) {
443 return(FILESYS_EXIT_CODE);
446 /* Seek the files to their respective starting points. */
447 ifile->io_seek(ifile, check_arg_numeric("skip") * ibs);
448 ofile->io_seek(ofile, check_arg_numeric("seek") * obs);
450 DEBUG(4, ("max xmit was negotiated to be %d\n", options.max_xmit));
452 for (data_size = 0;;) {
454 /* Handle signals. We are somewhat compatible with GNU dd.
455 * SIGINT makes us stop, but still print transfer statistics.
456 * SIGUSR1 makes us print transfer statistics but we continue
464 print_transfer_stats();
468 if (ifile->io_flags & DD_END_OF_FILE) {
469 DEBUG(4, ("flushing %llu bytes at EOF\n",
470 (unsigned long long)data_size));
471 while (data_size > 0) {
472 if (!dd_flush_block(ofile, iobuf,
474 return(IOERROR_EXIT_CODE);
480 /* Try and read enough blocks of ibs bytes to be able write
481 * out one of obs bytes.
483 if (!dd_fill_block(ifile, iobuf, &data_size, obs, ibs)) {
484 return(IOERROR_EXIT_CODE);
487 if (data_size == 0) {
489 SMB_ASSERT(ifile->io_flags & DD_END_OF_FILE);
492 /* Stop reading when we hit the block count. */
493 if (dd_stats.in.bytes >= (ibs * count)) {
494 ifile->io_flags |= DD_END_OF_FILE;
497 /* If we wanted to be a legitimate dd, we would do character
498 * conversions and other shenanigans here.
501 /* Flush what we read in units of obs bytes. We want to have
502 * at least obs bytes in the IO buffer but might not if the
506 !dd_flush_block(ofile, iobuf, &data_size, obs)) {
507 return(IOERROR_EXIT_CODE);
512 print_transfer_stats();
516 /* ------------------------------------------------------------------------- */
518 /* ------------------------------------------------------------------------- */
520 struct poptOption cifsddHelpOptions[] = {
521 { NULL, '\0', POPT_ARG_CALLBACK, (void *)&cifsdd_help_message, '\0', NULL, NULL },
522 { "help", '?', 0, NULL, '?', "Show this help message", NULL },
523 { "usage", '\0', 0, NULL, 'u', "Display brief usage message", NULL },
527 int main(int argc, const char ** argv)
530 const char ** dd_args;
533 struct poptOption poptions[] = {
535 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cifsddHelpOptions,
536 0, "Help options:", NULL },
538 POPT_COMMON_CONNECTION
539 POPT_COMMON_CREDENTIALS
545 set_arg_val("bs", (uint64_t)4096);
546 set_arg_val("ibs", (uint64_t)4096);
547 set_arg_val("obs", (uint64_t)4096);
549 set_arg_val("count", (uint64_t)-1);
550 set_arg_val("seek", (uint64_t)0);
551 set_arg_val("seek", (uint64_t)0);
553 set_arg_val("if", NULL);
554 set_arg_val("of", NULL);
556 set_arg_val("direct", false);
557 set_arg_val("sync", false);
558 set_arg_val("oplock", false);
560 pctx = poptGetContext(PROGNAME, argc, argv, poptions, 0);
561 while ((i = poptGetNextOpt(pctx)) != -1) {
565 for (dd_args = poptGetArgs(pctx); dd_args && *dd_args; ++dd_args) {
567 if (!set_arg_argv(*dd_args)) {
568 fprintf(stderr, "%s: invalid option: %s\n",
570 exit(SYNTAX_EXIT_CODE);
573 /* "bs" has the side-effect of setting "ibs" and "obs". */
574 if (strncmp(*dd_args, "bs=", 3) == 0) {
575 uint64_t bs = check_arg_numeric("bs");
576 set_arg_val("ibs", bs);
577 set_arg_val("obs", bs);
581 gensec_init(cmdline_lp_ctx);
584 if (check_arg_numeric("ibs") == 0 || check_arg_numeric("ibs") == 0) {
585 fprintf(stderr, "%s: block sizes must be greater that zero\n",
587 exit(SYNTAX_EXIT_CODE);
590 if (check_arg_pathname("if") == NULL) {
591 fprintf(stderr, "%s: missing input filename\n", PROGNAME);
592 exit(SYNTAX_EXIT_CODE);
595 if (check_arg_pathname("of") == NULL) {
596 fprintf(stderr, "%s: missing output filename\n", PROGNAME);
597 exit(SYNTAX_EXIT_CODE);
600 CatchSignal(SIGINT, dd_handle_signal);
601 CatchSignal(SIGUSR1, dd_handle_signal);
602 return(copy_files(cmdline_lp_ctx));
605 /* vim: set sw=8 sts=8 ts=8 tw=79 : */