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/cmdline.h"
25 #include "libcli/resolve/resolve.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "lib/events/events.h"
30 #include "param/param.h"
32 const char * const PROGNAME
= "cifsdd";
34 #define SYNTAX_EXIT_CODE 1 /* Invokation syntax or logic error. */
35 #define EOM_EXIT_CODE 9 /* Out of memory error. */
36 #define FILESYS_EXIT_CODE 10 /* File manipulation error. */
37 #define IOERROR_EXIT_CODE 11 /* Error during IO phase. */
39 struct dd_stats_record dd_stats
;
42 static int dd_sigusr1
;
44 static void dd_handle_signal(int sig
)
59 /* ------------------------------------------------------------------------- */
60 /* Argument handling. */
61 /* ------------------------------------------------------------------------- */
64 enum argtype arg_type
;
65 const char * arg_name
;
67 { ARG_NUMERIC
, "COUNT" },
69 { ARG_PATHNAME
, "FILE" },
70 { ARG_BOOL
, "BOOLEAN" },
73 static const char * argtype_str(enum argtype arg_type
)
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
[] =
91 .arg_help
= "force ibs and obs to SIZE bytes",
96 .arg_help
= "read SIZE bytes at a time",
100 .arg_type
= ARG_SIZE
,
101 .arg_help
= "write SIZE bytes at a time",
106 .arg_type
= ARG_NUMERIC
,
107 .arg_help
= "copy COUNT input blocks",
111 .arg_type
= ARG_NUMERIC
,
112 .arg_help
= "skip COUNT blocks at start of output",
116 .arg_type
= ARG_NUMERIC
,
117 .arg_help
= "skip COUNT blocks at start of input",
122 .arg_type
= ARG_PATHNAME
,
123 .arg_help
= "read input from FILE",
127 .arg_type
= ARG_PATHNAME
,
128 .arg_help
= "write output to FILE",
132 .arg_name
= "direct",
133 .arg_type
= ARG_BOOL
,
134 .arg_help
= "use direct I/O if non-zero",
138 .arg_type
= ARG_BOOL
,
139 .arg_help
= "use synchronous writes if non-zero",
142 .arg_name
= "oplock",
143 .arg_type
= ARG_BOOL
,
144 .arg_help
= "take oplocks on the input and output files",
147 /* FIXME: We should support using iflags and oflags for setting oplock and I/O
148 * options. This would make us compatible with GNU dd.
152 static struct argdef
* find_named_arg(const char * arg
)
156 for (i
= 0; i
< ARRAY_SIZE(args
); ++i
) {
157 if (strwicmp(arg
, args
[i
].arg_name
) == 0) {
165 int set_arg_argv(const char * argv
)
172 if ((name
= strdup(argv
)) == NULL
) {
176 if ((val
= strchr(name
, '=')) == NULL
) {
177 fprintf(stderr
, "%s: malformed argument \"%s\"\n",
185 if ((arg
= find_named_arg(name
)) == NULL
) {
186 fprintf(stderr
, "%s: ignoring unknown argument \"%s\"\n",
191 /* Found a matching name; convert the variable argument. */
192 switch (arg
->arg_type
) {
194 if (!conv_str_u64(val
, &arg
->arg_val
.nval
)) {
199 if (!conv_str_size_error(val
, &arg
->arg_val
.nval
)) {
204 if (!conv_str_bool(val
, &arg
->arg_val
.bval
)) {
209 if (!(arg
->arg_val
.pval
= strdup(val
))) {
214 fprintf(stderr
, "%s: argument \"%s\" is of "
215 "unknown type\n", PROGNAME
, name
);
227 void set_arg_val(const char * name
, ...)
233 if ((arg
= find_named_arg(name
)) == NULL
) {
237 /* Found a matching name; convert the variable argument. */
238 switch (arg
->arg_type
) {
241 arg
->arg_val
.nval
= va_arg(ap
, uint64_t);
244 arg
->arg_val
.bval
= va_arg(ap
, int);
247 arg
->arg_val
.pval
= va_arg(ap
, char *);
248 if (arg
->arg_val
.pval
) {
249 arg
->arg_val
.pval
= strdup(arg
->arg_val
.pval
);
253 fprintf(stderr
, "%s: argument \"%s\" is of "
254 "unknown type\n", PROGNAME
, name
);
262 fprintf(stderr
, "%s: ignoring unknown argument \"%s\"\n",
268 bool check_arg_bool(const char * name
)
272 if ((arg
= find_named_arg(name
)) &&
273 (arg
->arg_type
== ARG_BOOL
)) {
274 return(arg
->arg_val
.bval
);
277 DEBUG(0, ("invalid argument name: %s", name
));
282 uint64_t check_arg_numeric(const char * name
)
286 if ((arg
= find_named_arg(name
)) &&
287 (arg
->arg_type
== ARG_NUMERIC
|| arg
->arg_type
== ARG_SIZE
)) {
288 return(arg
->arg_val
.nval
);
291 DEBUG(0, ("invalid argument name: %s", name
));
296 const char * check_arg_pathname(const char * name
)
300 if ((arg
= find_named_arg(name
)) &&
301 (arg
->arg_type
== ARG_PATHNAME
)) {
302 return(arg
->arg_val
.pval
);
305 DEBUG(0, ("invalid argument name: %s", name
));
310 static void dump_args(void)
314 DEBUG(10, ("dumping argument values:\n"));
315 for (i
= 0; i
< ARRAY_SIZE(args
); ++i
) {
316 switch (args
[i
].arg_type
) {
319 DEBUG(10, ("\t%s=%llu\n", args
[i
].arg_name
,
320 (unsigned long long)args
[i
].arg_val
.nval
));
323 DEBUG(10, ("\t%s=%s\n", args
[i
].arg_name
,
324 args
[i
].arg_val
.bval
? "yes" : "no"));
327 DEBUG(10, ("\t%s=%s\n", args
[i
].arg_name
,
328 args
[i
].arg_val
.pval
?
329 args
[i
].arg_val
.pval
:
338 static void cifsdd_help_message(poptContext pctx
,
339 enum poptCallbackReason preason
,
340 struct poptOption
* poption
,
344 static const char notes
[] =
345 "FILE can be a local filename or a UNC path of the form //server/share/path.\n";
350 if (poption
->shortName
!= '?') {
351 poptPrintUsage(pctx
, stdout
, 0);
352 fprintf(stdout
, " [dd options]\n");
356 poptPrintHelp(pctx
, stdout
, 0);
357 fprintf(stdout
, "\nCIFS dd options:\n");
359 for (i
= 0; i
< ARRAY_SIZE(args
); ++i
) {
360 if (args
[i
].arg_name
== NULL
) {
364 snprintf(prefix
, sizeof(prefix
), "%s=%-*s",
366 (int)(sizeof(prefix
) - strlen(args
[i
].arg_name
) - 2),
367 argtype_str(args
[i
].arg_type
));
368 prefix
[sizeof(prefix
) - 1] = '\0';
369 fprintf(stdout
, " %s%s\n", prefix
, args
[i
].arg_help
);
372 fprintf(stdout
, "\n%s\n", notes
);
376 /* ------------------------------------------------------------------------- */
377 /* Main block copying routine. */
378 /* ------------------------------------------------------------------------- */
380 static void print_transfer_stats(void)
382 if (DEBUGLEVEL
> 0) {
383 printf("%llu+%llu records in (%llu bytes)\n"
384 "%llu+%llu records out (%llu bytes)\n",
385 (unsigned long long)dd_stats
.in
.fblocks
,
386 (unsigned long long)dd_stats
.in
.pblocks
,
387 (unsigned long long)dd_stats
.in
.bytes
,
388 (unsigned long long)dd_stats
.out
.fblocks
,
389 (unsigned long long)dd_stats
.out
.pblocks
,
390 (unsigned long long)dd_stats
.out
.bytes
);
392 printf("%llu+%llu records in\n%llu+%llu records out\n",
393 (unsigned long long)dd_stats
.in
.fblocks
,
394 (unsigned long long)dd_stats
.in
.pblocks
,
395 (unsigned long long)dd_stats
.out
.fblocks
,
396 (unsigned long long)dd_stats
.out
.pblocks
);
400 static struct dd_iohandle
* open_file(struct resolve_context
*resolve_ctx
,
401 struct tevent_context
*ev
,
402 const char * which
, const char **ports
,
403 struct smbcli_options
*smb_options
,
404 const char *socket_options
,
405 struct smbcli_session_options
*smb_session_options
,
406 struct gensec_settings
*gensec_settings
)
409 const char * path
= NULL
;
410 struct dd_iohandle
* handle
= NULL
;
412 if (check_arg_bool("direct")) {
413 options
|= DD_DIRECT_IO
;
416 if (check_arg_bool("sync")) {
417 options
|= DD_SYNC_IO
;
420 if (check_arg_bool("oplock")) {
421 options
|= DD_OPLOCK
;
424 if (strcmp(which
, "if") == 0) {
425 path
= check_arg_pathname("if");
426 handle
= dd_open_path(resolve_ctx
, ev
, path
, ports
,
427 check_arg_numeric("ibs"), options
,
429 smb_options
, smb_session_options
,
431 } else if (strcmp(which
, "of") == 0) {
433 path
= check_arg_pathname("of");
434 handle
= dd_open_path(resolve_ctx
, ev
, path
, ports
,
435 check_arg_numeric("obs"), options
,
437 smb_options
, smb_session_options
,
445 fprintf(stderr
, "%s: failed to open %s\n", PROGNAME
, path
);
451 static int copy_files(struct tevent_context
*ev
, struct loadparm_context
*lp_ctx
)
453 uint8_t * iobuf
; /* IO buffer. */
454 uint64_t iomax
; /* Size of the IO buffer. */
455 uint64_t data_size
; /* Amount of data in the IO buffer. */
461 struct dd_iohandle
* ifile
;
462 struct dd_iohandle
* ofile
;
464 struct smbcli_options options
;
465 struct smbcli_session_options session_options
;
467 ibs
= check_arg_numeric("ibs");
468 obs
= check_arg_numeric("obs");
469 count
= check_arg_numeric("count");
471 lpcfg_smbcli_options(lp_ctx
, &options
);
472 lpcfg_smbcli_session_options(lp_ctx
, &session_options
);
474 /* Allocate IO buffer. We need more than the max IO size because we
475 * could accumulate a remainder if ibs and obs don't match.
477 iomax
= 2 * MAX(ibs
, obs
);
478 if ((iobuf
= malloc_array_p(uint8_t, iomax
)) == NULL
) {
480 "%s: failed to allocate IO buffer of %llu bytes\n",
481 PROGNAME
, (unsigned long long)iomax
);
482 return(EOM_EXIT_CODE
);
485 options
.max_xmit
= MAX(ibs
, obs
);
487 DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
488 (unsigned long long)iomax
, options
.max_xmit
));
490 if (!(ifile
= open_file(lpcfg_resolve_context(lp_ctx
), ev
, "if",
491 lpcfg_smb_ports(lp_ctx
), &options
,
492 lpcfg_socket_options(lp_ctx
),
494 lpcfg_gensec_settings(lp_ctx
, lp_ctx
)))) {
496 return(FILESYS_EXIT_CODE
);
499 if (!(ofile
= open_file(lpcfg_resolve_context(lp_ctx
), ev
, "of",
500 lpcfg_smb_ports(lp_ctx
), &options
,
501 lpcfg_socket_options(lp_ctx
),
503 lpcfg_gensec_settings(lp_ctx
, lp_ctx
)))) {
505 return(FILESYS_EXIT_CODE
);
508 /* Seek the files to their respective starting points. */
509 ifile
->io_seek(ifile
, check_arg_numeric("skip") * ibs
);
510 ofile
->io_seek(ofile
, check_arg_numeric("seek") * obs
);
512 DEBUG(4, ("max xmit was negotiated to be %d\n", options
.max_xmit
));
514 for (data_size
= 0;;) {
516 /* Handle signals. We are somewhat compatible with GNU dd.
517 * SIGINT makes us stop, but still print transfer statistics.
518 * SIGUSR1 makes us print transfer statistics but we continue
526 print_transfer_stats();
530 if (ifile
->io_flags
& DD_END_OF_FILE
) {
531 DEBUG(4, ("flushing %llu bytes at EOF\n",
532 (unsigned long long)data_size
));
533 while (data_size
> 0) {
534 if (!dd_flush_block(ofile
, iobuf
,
537 return(IOERROR_EXIT_CODE
);
543 /* Try and read enough blocks of ibs bytes to be able write
544 * out one of obs bytes.
546 if (!dd_fill_block(ifile
, iobuf
, &data_size
, obs
, ibs
)) {
548 return(IOERROR_EXIT_CODE
);
551 if (data_size
== 0) {
553 SMB_ASSERT(ifile
->io_flags
& DD_END_OF_FILE
);
556 /* Stop reading when we hit the block count. */
557 if (dd_stats
.in
.bytes
>= (ibs
* count
)) {
558 ifile
->io_flags
|= DD_END_OF_FILE
;
561 /* If we wanted to be a legitimate dd, we would do character
562 * conversions and other shenanigans here.
565 /* Flush what we read in units of obs bytes. We want to have
566 * at least obs bytes in the IO buffer but might not if the
570 !dd_flush_block(ofile
, iobuf
, &data_size
, obs
)) {
572 return(IOERROR_EXIT_CODE
);
578 print_transfer_stats();
582 /* ------------------------------------------------------------------------- */
584 /* ------------------------------------------------------------------------- */
586 struct poptOption cifsddHelpOptions
[] = {
590 .argInfo
= POPT_ARG_CALLBACK
,
591 .arg
= (void *)&cifsdd_help_message
,
598 .descrip
= "Show this help message",
604 .descrip
= "Display brief usage message",
609 int main(int argc
, char *argv
[])
611 const char **const_argv
= discard_const_p(const char *, argv
);
613 const char ** dd_args
;
614 TALLOC_CTX
*mem_ctx
= NULL
;
615 struct loadparm_context
*lp_ctx
= NULL
;
616 struct tevent_context
*ev
;
621 struct poptOption poptions
[] = {
623 { NULL
, '\0', POPT_ARG_INCLUDE_TABLE
, cifsddHelpOptions
,
624 0, "Help options:", NULL
},
626 POPT_COMMON_CONNECTION
627 POPT_COMMON_CREDENTIALS
634 set_arg_val("bs", (uint64_t)4096);
635 set_arg_val("ibs", (uint64_t)4096);
636 set_arg_val("obs", (uint64_t)4096);
638 set_arg_val("count", (uint64_t)-1);
639 set_arg_val("seek", (uint64_t)0);
640 set_arg_val("skip", (uint64_t)0);
642 set_arg_val("if", NULL
);
643 set_arg_val("of", NULL
);
645 set_arg_val("direct", false);
646 set_arg_val("sync", false);
647 set_arg_val("oplock", false);
649 mem_ctx
= talloc_init("cifsdd.c/main");
650 if (mem_ctx
== NULL
) {
651 d_printf("Not enough memory\n");
655 ok
= samba_cmdline_init(mem_ctx
,
656 SAMBA_CMDLINE_CONFIG_CLIENT
,
657 false /* require_smbconf */);
659 DBG_ERR("Failed to init cmdline parser!\n");
660 TALLOC_FREE(mem_ctx
);
664 pctx
= samba_popt_get_context(getprogname(), argc
, const_argv
, poptions
, 0);
666 DBG_ERR("Failed to setup popt context!\n");
667 TALLOC_FREE(mem_ctx
);
671 while ((i
= poptGetNextOpt(pctx
)) != -1) {
673 case POPT_ERROR_BADOPT
:
674 fprintf(stderr
, "\nInvalid option %s: %s\n\n",
675 poptBadOption(pctx
, 0), poptStrerror(i
));
676 poptPrintUsage(pctx
, stderr
, 0);
681 for (dd_args
= poptGetArgs(pctx
); dd_args
&& *dd_args
; ++dd_args
) {
683 if (!set_arg_argv(*dd_args
)) {
684 fprintf(stderr
, "%s: invalid option: %s\n",
686 exit(SYNTAX_EXIT_CODE
);
689 /* "bs" has the side-effect of setting "ibs" and "obs". */
690 if (strncmp(*dd_args
, "bs=", 3) == 0) {
691 uint64_t bs
= check_arg_numeric("bs");
692 set_arg_val("ibs", bs
);
693 set_arg_val("obs", bs
);
697 poptFreeContext(pctx
);
698 samba_cmdline_burn(argc
, argv
);
700 ev
= s4_event_context_init(mem_ctx
);
701 lp_ctx
= samba_cmdline_get_lp_ctx();
706 if (check_arg_numeric("ibs") == 0 || check_arg_numeric("obs") == 0) {
707 fprintf(stderr
, "%s: block sizes must be greater that zero\n",
709 talloc_free(mem_ctx
);
710 exit(SYNTAX_EXIT_CODE
);
713 if (check_arg_pathname("if") == NULL
) {
714 fprintf(stderr
, "%s: missing input filename\n", PROGNAME
);
715 talloc_free(mem_ctx
);
716 exit(SYNTAX_EXIT_CODE
);
719 if (check_arg_pathname("of") == NULL
) {
720 fprintf(stderr
, "%s: missing output filename\n", PROGNAME
);
721 talloc_free(mem_ctx
);
722 exit(SYNTAX_EXIT_CODE
);
725 CatchSignal(SIGINT
, dd_handle_signal
);
726 CatchSignal(SIGUSR1
, dd_handle_signal
);
727 rc
= copy_files(ev
, lp_ctx
);
729 talloc_free(mem_ctx
);
733 /* vim: set sw=8 sts=8 ts=8 tw=79 : */