r21746: We don't link in this file any more.
[Samba/ekacnet.git] / source4 / client / cifsdd.c
blob0ebaa9e378f5338521d8c497a2934b96d022df3c
1 /*
2 CIFSDD - dd for SMB.
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.
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "auth/gensec/gensec.h"
25 #include "lib/cmdline/popt_common.h"
27 #include "cifsdd.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;
38 static int dd_sigint;
39 static int dd_sigusr1;
41 static void dd_handle_signal(int sig)
43 switch (sig)
45 case SIGINT:
46 ++dd_sigint;
47 break;
48 case SIGUSR1:
49 ++dd_sigusr1;
50 break;
51 default:
52 break;
56 /* ------------------------------------------------------------------------- */
57 /* Argument handling. */
58 /* ------------------------------------------------------------------------- */
60 static const char * argtype_str(enum argtype arg_type)
62 static const struct {
63 enum argtype arg_type;
64 const char * arg_name;
65 } names [] =
67 { ARG_NUMERIC, "COUNT" },
68 { ARG_SIZE, "SIZE" },
69 { ARG_PATHNAME, "FILE" },
70 { ARG_BOOL, "BOOLEAN" },
73 int i;
75 for (i = 0; i < ARRAY_SIZE(names); ++i) {
76 if (arg_type == names[i].arg_type) {
77 return(names[i].arg_name);
81 return("<unknown>");
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)
108 int i;
110 for (i = 0; i < ARRAY_SIZE(args); ++i) {
111 if (strwicmp(arg, args[i].arg_name) == 0) {
112 return(&args[i]);
116 return(NULL);
119 int set_arg_argv(const char * argv)
121 struct argdef * arg;
123 char * name;
124 char * val;
126 if ((name = strdup(argv)) == NULL) {
127 return(0);
130 if ((val = strchr(name, '=')) == NULL) {
131 fprintf(stderr, "%s: malformed argument \"%s\"\n",
132 PROGNAME, argv);
133 goto fail;
136 *val = '\0';
137 val++;
139 if ((arg = find_named_arg(name)) == NULL) {
140 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
141 PROGNAME, name);
142 goto fail;
145 /* Found a matching name; convert the variable argument. */
146 switch (arg->arg_type) {
147 case ARG_NUMERIC:
148 if (!conv_str_u64(val, &arg->arg_val.nval)) {
149 goto fail;
151 break;
152 case ARG_SIZE:
153 if (!conv_str_size(val, &arg->arg_val.nval)) {
154 goto fail;
156 break;
157 case ARG_BOOL:
158 if (!conv_str_bool(val, &arg->arg_val.bval)) {
159 goto fail;
161 break;
162 case ARG_PATHNAME:
163 if (!(arg->arg_val.pval = strdup(val))) {
164 goto fail;
166 break;
167 default:
168 fprintf(stderr, "%s: argument \"%s\" is of "
169 "unknown type\n", PROGNAME, name);
170 goto fail;
173 free(name);
174 return(1);
176 fail:
177 free(name);
178 return(0);
181 void set_arg_val(const char * name, ...)
183 va_list ap;
184 struct argdef * arg;
186 va_start(ap, name);
187 if ((arg = find_named_arg(name)) == NULL) {
188 goto fail;
191 /* Found a matching name; convert the variable argument. */
192 switch (arg->arg_type) {
193 case ARG_NUMERIC:
194 case ARG_SIZE:
195 arg->arg_val.nval = va_arg(ap, uint64_t);
196 break;
197 case ARG_BOOL:
198 arg->arg_val.bval = va_arg(ap, int);
199 break;
200 case ARG_PATHNAME:
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);
205 break;
206 default:
207 fprintf(stderr, "%s: argument \"%s\" is of "
208 "unknown type\n", PROGNAME, name);
209 goto fail;
212 va_end(ap);
213 return;
215 fail:
216 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
217 PROGNAME, name);
218 va_end(ap);
219 return;
222 BOOL check_arg_bool(const char * name)
224 struct argdef * arg;
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));
232 SMB_ASSERT(0);
233 return(False);
236 uint64_t check_arg_numeric(const char * name)
238 struct argdef * arg;
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));
246 SMB_ASSERT(0);
247 return(-1);
250 const char * check_arg_pathname(const char * name)
252 struct argdef * arg;
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));
260 SMB_ASSERT(0);
261 return(NULL);
264 static void dump_args(void)
266 int i;
268 DEBUG(10, ("dumping argument values:\n"));
269 for (i = 0; i < ARRAY_SIZE(args); ++i) {
270 switch (args[i].arg_type) {
271 case ARG_NUMERIC:
272 case ARG_SIZE:
273 DEBUG(10, ("\t%s=%llu\n", args[i].arg_name,
274 (unsigned long long)args[i].arg_val.nval));
275 break;
276 case ARG_BOOL:
277 DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
278 args[i].arg_val.bval ? "yes" : "no"));
279 break;
280 case ARG_PATHNAME:
281 DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
282 args[i].arg_val.pval ?
283 args[i].arg_val.pval :
284 "(NULL)"));
285 break;
286 default:
287 SMB_ASSERT(0);
292 static void cifsdd_help_message(poptContext pctx,
293 enum poptCallbackReason preason,
294 struct poptOption * poption,
295 const char * parg,
296 void * pdata)
298 static const char notes[] =
299 "FILE can be a local filename or a UNC path of the form //server/share/path.\n";
301 char prefix[24];
302 int i;
304 if (poption->shortName != '?') {
305 poptPrintUsage(pctx, stdout, 0);
306 fprintf(stdout, " [dd options]\n");
307 exit(0);
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) {
315 break;
318 snprintf(prefix, sizeof(prefix), "%s=%-*s",
319 args[i].arg_name,
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);
327 exit(0);
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);
345 } else {
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)
356 int options = 0;
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"),
375 options);
376 } else if (strcmp(which, "of") == 0) {
377 options |= DD_WRITE;
378 path = check_arg_pathname("of");
379 handle = dd_open_path(path, check_arg_numeric("obs"),
380 options);
381 } else {
382 SMB_ASSERT(0);
383 return(NULL);
386 if (!handle) {
387 fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
390 return(handle);
393 static void set_max_xmit(uint64_t iomax)
395 char buf[64];
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. */
407 uint64_t ibs;
408 uint64_t obs;
409 uint64_t count;
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) {
423 fprintf(stderr,
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
453 * copying.
455 if (dd_sigint) {
456 break;
459 if (dd_sigusr1) {
460 print_transfer_stats();
461 dd_sigusr1 = 0;
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,
469 &data_size, obs)) {
470 return(IOERROR_EXIT_CODE);
473 goto done;
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) {
484 /* Done. */
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
499 * file is too small.
501 if (data_size &&
502 !dd_flush_block(ofile, iobuf, &data_size, obs)) {
503 return(IOERROR_EXIT_CODE);
507 done:
508 print_transfer_stats();
509 return(0);
512 /* ------------------------------------------------------------------------- */
513 /* Main. */
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 },
520 { NULL }
523 int main(int argc, const char ** argv)
525 int i;
526 const char ** dd_args;
528 poptContext pctx;
529 struct poptOption poptions[] = {
530 /* POPT_AUTOHELP */
531 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cifsddHelpOptions,
532 0, "Help options:", NULL },
533 POPT_COMMON_SAMBA
534 POPT_COMMON_CONNECTION
535 POPT_COMMON_CREDENTIALS
536 POPT_COMMON_VERSION
537 { NULL }
540 /* Block sizes. */
541 set_arg_val("bs", (uint64_t)4096);
542 set_arg_val("ibs", (uint64_t)4096);
543 set_arg_val("obs", (uint64_t)4096);
544 /* Block counts. */
545 set_arg_val("count", (uint64_t)-1);
546 set_arg_val("seek", (uint64_t)0);
547 set_arg_val("seek", (uint64_t)0);
548 /* Files. */
549 set_arg_val("if", NULL);
550 set_arg_val("of", NULL);
551 /* Options. */
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",
565 PROGNAME, *dd_args);
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);
577 gensec_init();
578 dump_args();
580 if (check_arg_numeric("ibs") == 0 || check_arg_numeric("ibs") == 0) {
581 fprintf(stderr, "%s: block sizes must be greater that zero\n",
582 PROGNAME);
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 : */