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 2 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, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "system/filesys.h"
24 #include "auth/gensec/gensec.h"
25 #include "lib/cmdline/popt_common.h"
29 const char * const PROGNAME
= "cifsdd";
31 #define SYNTAX_EXIT_CODE 1 /* Invokation syntax or logic error. */
32 #define EOM_EXIT_CODE 9 /* Out of memory error. */
33 #define FILESYS_EXIT_CODE 10 /* File manipulation error. */
34 #define IOERROR_EXIT_CODE 11 /* Error during IO phase. */
36 struct dd_stats_record dd_stats
;
39 static int dd_sigusr1
;
41 static void dd_handle_signal(int sig
)
56 /* ------------------------------------------------------------------------- */
57 /* Argument handling. */
58 /* ------------------------------------------------------------------------- */
60 static const char * argtype_str(enum argtype arg_type
)
63 enum argtype arg_type
;
64 const char * arg_name
;
67 { ARG_NUMERIC
, "COUNT" },
69 { ARG_PATHNAME
, "FILE" },
70 { ARG_BOOL
, "BOOLEAN" },
75 for (i
= 0; i
< ARRAY_SIZE(names
); ++i
) {
76 if (arg_type
== names
[i
].arg_type
) {
77 return(names
[i
].arg_name
);
84 static struct argdef args
[] =
86 { "bs", ARG_SIZE
, "force ibs and obs to SIZE bytes" },
87 { "ibs", ARG_SIZE
, "read SIZE bytes at a time" },
88 { "obs", ARG_SIZE
, "write SIZE bytes at a time" },
90 { "count", ARG_NUMERIC
, "copy COUNT input blocks" },
91 { "seek",ARG_NUMERIC
, "skip COUNT blocks at start of output" },
92 { "skip",ARG_NUMERIC
, "skip COUNT blocks at start of input" },
94 { "if", ARG_PATHNAME
, "read input from FILE" },
95 { "of", ARG_PATHNAME
, "write output to FILE" },
97 { "direct", ARG_BOOL
, "use direct I/O if non-zero" },
98 { "sync", ARG_BOOL
, "use synchronous writes if non-zero" },
99 { "oplock", ARG_BOOL
, "take oplocks on the input and output files" },
101 /* FIXME: We should support using iflags and oflags for setting oplock and I/O
102 * options. This would make us compatible with GNU dd.
106 struct argdef
* find_named_arg(const char * arg
)
110 for (i
= 0; i
< ARRAY_SIZE(args
); ++i
) {
111 if (strwicmp(arg
, args
[i
].arg_name
) == 0) {
119 int set_arg_argv(const char * argv
)
126 if ((name
= strdup(argv
)) == NULL
) {
130 if ((val
= strchr(name
, '=')) == NULL
) {
131 fprintf(stderr
, "%s: malformed argument \"%s\"\n",
139 if ((arg
= find_named_arg(name
)) == NULL
) {
140 fprintf(stderr
, "%s: ignoring unknown argument \"%s\"\n",
145 /* Found a matching name; convert the variable argument. */
146 switch (arg
->arg_type
) {
148 if (!conv_str_u64(val
, &arg
->arg_val
.nval
)) {
153 if (!conv_str_size(val
, &arg
->arg_val
.nval
)) {
158 if (!conv_str_bool(val
, &arg
->arg_val
.bval
)) {
163 if (!(arg
->arg_val
.pval
= strdup(val
))) {
168 fprintf(stderr
, "%s: argument \"%s\" is of "
169 "unknown type\n", PROGNAME
, name
);
181 void set_arg_val(const char * name
, ...)
187 if ((arg
= find_named_arg(name
)) == NULL
) {
191 /* Found a matching name; convert the variable argument. */
192 switch (arg
->arg_type
) {
195 arg
->arg_val
.nval
= va_arg(ap
, uint64_t);
198 arg
->arg_val
.bval
= va_arg(ap
, int);
201 arg
->arg_val
.pval
= va_arg(ap
, char *);
202 if (arg
->arg_val
.pval
) {
203 arg
->arg_val
.pval
= strdup(arg
->arg_val
.pval
);
207 fprintf(stderr
, "%s: argument \"%s\" is of "
208 "unknown type\n", PROGNAME
, name
);
216 fprintf(stderr
, "%s: ignoring unknown argument \"%s\"\n",
222 BOOL
check_arg_bool(const char * name
)
226 if ((arg
= find_named_arg(name
)) &&
227 (arg
->arg_type
== ARG_BOOL
)) {
228 return(arg
->arg_val
.bval
);
231 DEBUG(0, ("invalid argument name: %s", name
));
236 uint64_t check_arg_numeric(const char * name
)
240 if ((arg
= find_named_arg(name
)) &&
241 (arg
->arg_type
== ARG_NUMERIC
|| arg
->arg_type
== ARG_SIZE
)) {
242 return(arg
->arg_val
.nval
);
245 DEBUG(0, ("invalid argument name: %s", name
));
250 const char * check_arg_pathname(const char * name
)
254 if ((arg
= find_named_arg(name
)) &&
255 (arg
->arg_type
== ARG_PATHNAME
)) {
256 return(arg
->arg_val
.pval
);
259 DEBUG(0, ("invalid argument name: %s", name
));
264 static void dump_args(void)
268 DEBUG(10, ("dumping argument values:\n"));
269 for (i
= 0; i
< ARRAY_SIZE(args
); ++i
) {
270 switch (args
[i
].arg_type
) {
273 DEBUG(10, ("\t%s=%llu\n", args
[i
].arg_name
,
274 (unsigned long long)args
[i
].arg_val
.nval
));
277 DEBUG(10, ("\t%s=%s\n", args
[i
].arg_name
,
278 args
[i
].arg_val
.bval
? "yes" : "no"));
281 DEBUG(10, ("\t%s=%s\n", args
[i
].arg_name
,
282 args
[i
].arg_val
.pval
?
283 args
[i
].arg_val
.pval
:
292 static void cifsdd_help_message(poptContext pctx
,
293 enum poptCallbackReason preason
,
294 struct poptOption
* poption
,
298 static const char notes
[] =
299 "FILE can be a local filename or a UNC path of the form //server/share/path.\n";
304 if (poption
->shortName
!= '?') {
305 poptPrintUsage(pctx
, stdout
, 0);
306 fprintf(stdout
, " [dd options]\n");
310 poptPrintHelp(pctx
, stdout
, 0);
311 fprintf(stdout
, "\nCIFS dd options:\n");
313 for (i
= 0; i
< ARRAY_SIZE(args
); ++i
) {
314 if (args
[i
].arg_name
== NULL
) {
318 snprintf(prefix
, sizeof(prefix
), "%s=%-*s",
320 (int)(sizeof(prefix
) - strlen(args
[i
].arg_name
) - 2),
321 argtype_str(args
[i
].arg_type
));
322 prefix
[sizeof(prefix
) - 1] = '\0';
323 fprintf(stdout
, " %s%s\n", prefix
, args
[i
].arg_help
);
326 fprintf(stdout
, "\n%s\n", notes
);
330 /* ------------------------------------------------------------------------- */
331 /* Main block copying routine. */
332 /* ------------------------------------------------------------------------- */
334 static void print_transfer_stats(void)
336 if (DEBUGLEVEL
> 0) {
337 printf("%llu+%llu records in (%llu bytes)\n"
338 "%llu+%llu records out (%llu bytes)\n",
339 (unsigned long long)dd_stats
.in
.fblocks
,
340 (unsigned long long)dd_stats
.in
.pblocks
,
341 (unsigned long long)dd_stats
.in
.bytes
,
342 (unsigned long long)dd_stats
.out
.fblocks
,
343 (unsigned long long)dd_stats
.out
.pblocks
,
344 (unsigned long long)dd_stats
.out
.bytes
);
346 printf("%llu+%llu records in\n%llu+%llu records out\n",
347 (unsigned long long)dd_stats
.in
.fblocks
,
348 (unsigned long long)dd_stats
.in
.pblocks
,
349 (unsigned long long)dd_stats
.out
.fblocks
,
350 (unsigned long long)dd_stats
.out
.pblocks
);
354 static struct dd_iohandle
* open_file(const char * which
)
357 const char * path
= NULL
;
358 struct dd_iohandle
* handle
= NULL
;
360 if (check_arg_bool("direct")) {
361 options
|= DD_DIRECT_IO
;
364 if (check_arg_bool("sync")) {
365 options
|= DD_SYNC_IO
;
368 if (check_arg_bool("oplock")) {
369 options
|= DD_OPLOCK
;
372 if (strcmp(which
, "if") == 0) {
373 path
= check_arg_pathname("if");
374 handle
= dd_open_path(path
, check_arg_numeric("ibs"),
376 } else if (strcmp(which
, "of") == 0) {
378 path
= check_arg_pathname("of");
379 handle
= dd_open_path(path
, check_arg_numeric("obs"),
387 fprintf(stderr
, "%s: failed to open %s\n", PROGNAME
, path
);
393 static void set_max_xmit(uint64_t iomax
)
397 snprintf(buf
, sizeof(buf
), "%llu", (unsigned long long)iomax
);
398 lp_set_cmdline("max xmit", buf
);
401 static int copy_files(void)
403 uint8_t * iobuf
; /* IO buffer. */
404 uint64_t iomax
; /* Size of the IO buffer. */
405 uint64_t data_size
; /* Amount of data in the IO buffer. */
411 struct dd_iohandle
* ifile
;
412 struct dd_iohandle
* ofile
;
414 ibs
= check_arg_numeric("ibs");
415 obs
= check_arg_numeric("obs");
416 count
= check_arg_numeric("count");
418 /* Allocate IO buffer. We need more than the max IO size because we
419 * could accumulate a remainder if ibs and obs don't match.
421 iomax
= 2 * MAX(ibs
, obs
);
422 if ((iobuf
= malloc(iomax
)) == NULL
) {
424 "%s: failed to allocate IO buffer of %llu bytes\n",
425 PROGNAME
, (unsigned long long)iomax
);
426 return(EOM_EXIT_CODE
);
429 set_max_xmit(MAX(ibs
, obs
));
431 DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
432 (unsigned long long)iomax
, lp_max_xmit()));
434 if (!(ifile
= open_file("if"))) {
435 return(FILESYS_EXIT_CODE
);
438 if (!(ofile
= open_file("of"))) {
439 return(FILESYS_EXIT_CODE
);
442 /* Seek the files to their respective starting points. */
443 ifile
->io_seek(ifile
, check_arg_numeric("skip") * ibs
);
444 ofile
->io_seek(ofile
, check_arg_numeric("seek") * obs
);
446 DEBUG(4, ("max xmit was negotiated to be %d\n", lp_max_xmit()));
448 for (data_size
= 0;;) {
450 /* Handle signals. We are somewhat compatible with GNU dd.
451 * SIGINT makes us stop, but still print transfer statistics.
452 * SIGUSR1 makes us print transfer statistics but we continue
460 print_transfer_stats();
464 if (ifile
->io_flags
& DD_END_OF_FILE
) {
465 DEBUG(4, ("flushing %llu bytes at EOF\n",
466 (unsigned long long)data_size
));
467 while (data_size
> 0) {
468 if (!dd_flush_block(ofile
, iobuf
,
470 return(IOERROR_EXIT_CODE
);
476 /* Try and read enough blocks of ibs bytes to be able write
477 * out one of obs bytes.
479 if (!dd_fill_block(ifile
, iobuf
, &data_size
, obs
, ibs
)) {
480 return(IOERROR_EXIT_CODE
);
483 if (data_size
== 0) {
485 SMB_ASSERT(ifile
->io_flags
& DD_END_OF_FILE
);
488 /* Stop reading when we hit the block count. */
489 if (dd_stats
.in
.bytes
>= (ibs
* count
)) {
490 ifile
->io_flags
|= DD_END_OF_FILE
;
493 /* If we wanted to be a legitimate dd, we would do character
494 * conversions and other shenanigans here.
497 /* Flush what we read in units of obs bytes. We want to have
498 * at least obs bytes in the IO buffer but might not if the
502 !dd_flush_block(ofile
, iobuf
, &data_size
, obs
)) {
503 return(IOERROR_EXIT_CODE
);
508 print_transfer_stats();
512 /* ------------------------------------------------------------------------- */
514 /* ------------------------------------------------------------------------- */
516 struct poptOption cifsddHelpOptions
[] = {
517 { NULL
, '\0', POPT_ARG_CALLBACK
, (void *)&cifsdd_help_message
, '\0', NULL
, NULL
},
518 { "help", '?', 0, NULL
, '?', "Show this help message", NULL
},
519 { "usage", '\0', 0, NULL
, 'u', "Display brief usage message", NULL
},
523 int main(int argc
, const char ** argv
)
526 const char ** dd_args
;
529 struct poptOption poptions
[] = {
531 { NULL
, '\0', POPT_ARG_INCLUDE_TABLE
, cifsddHelpOptions
,
532 0, "Help options:", NULL
},
534 POPT_COMMON_CONNECTION
535 POPT_COMMON_CREDENTIALS
541 set_arg_val("bs", (uint64_t)4096);
542 set_arg_val("ibs", (uint64_t)4096);
543 set_arg_val("obs", (uint64_t)4096);
545 set_arg_val("count", (uint64_t)-1);
546 set_arg_val("seek", (uint64_t)0);
547 set_arg_val("seek", (uint64_t)0);
549 set_arg_val("if", NULL
);
550 set_arg_val("of", NULL
);
552 set_arg_val("direct", False
);
553 set_arg_val("sync", False
);
554 set_arg_val("oplock", False
);
556 pctx
= poptGetContext(PROGNAME
, argc
, argv
, poptions
, 0);
557 while ((i
= poptGetNextOpt(pctx
)) != -1) {
561 for (dd_args
= poptGetArgs(pctx
); dd_args
&& *dd_args
; ++dd_args
) {
563 if (!set_arg_argv(*dd_args
)) {
564 fprintf(stderr
, "%s: invalid option: %s\n",
566 exit(SYNTAX_EXIT_CODE
);
569 /* "bs" has the side-effect of setting "ibs" and "obs". */
570 if (strncmp(*dd_args
, "bs=", 3) == 0) {
571 uint64_t bs
= check_arg_numeric("bs");
572 set_arg_val("ibs", bs
);
573 set_arg_val("obs", bs
);
580 if (check_arg_numeric("ibs") == 0 || check_arg_numeric("ibs") == 0) {
581 fprintf(stderr
, "%s: block sizes must be greater that zero\n",
583 exit(SYNTAX_EXIT_CODE
);
586 if (check_arg_pathname("if") == NULL
) {
587 fprintf(stderr
, "%s: missing input filename\n", PROGNAME
);
588 exit(SYNTAX_EXIT_CODE
);
591 if (check_arg_pathname("of") == NULL
) {
592 fprintf(stderr
, "%s: missing output filename\n", PROGNAME
);
593 exit(SYNTAX_EXIT_CODE
);
596 CatchSignal(SIGINT
, dd_handle_signal
);
597 CatchSignal(SIGUSR1
, dd_handle_signal
);
598 return(copy_files());
601 /* vim: set sw=8 sts=8 ts=8 tw=79 : */