s3: libsmbclient: Fix smbc_stat() to return ENOENT on a non-existent file.
[Samba.git] / source4 / client / cifsdd.c
blob812698e49db6d5c24280b49dc56e00651a8b42a0
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 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/>.
21 #include "includes.h"
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"
29 #include "cifsdd.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;
41 static int dd_sigint;
42 static int dd_sigusr1;
44 static void dd_handle_signal(int sig)
46 switch (sig)
48 case SIGINT:
49 ++dd_sigint;
50 break;
51 case SIGUSR1:
52 ++dd_sigusr1;
53 break;
54 default:
55 break;
59 /* ------------------------------------------------------------------------- */
60 /* Argument handling. */
61 /* ------------------------------------------------------------------------- */
63 static const struct {
64 enum argtype arg_type;
65 const char * arg_name;
66 } names [] = {
67 { ARG_NUMERIC, "COUNT" },
68 { ARG_SIZE, "SIZE" },
69 { ARG_PATHNAME, "FILE" },
70 { ARG_BOOL, "BOOLEAN" },
73 static const char * argtype_str(enum argtype arg_type)
75 int i;
77 for (i = 0; i < ARRAY_SIZE(names); ++i) {
78 if (arg_type == names[i].arg_type) {
79 return(names[i].arg_name);
83 return("<unknown>");
86 static struct argdef args[] =
89 .arg_name = "bs",
90 .arg_type = ARG_SIZE,
91 .arg_help = "force ibs and obs to SIZE bytes",
94 .arg_name = "ibs",
95 .arg_type = ARG_SIZE,
96 .arg_help = "read SIZE bytes at a time",
99 .arg_name = "obs",
100 .arg_type = ARG_SIZE,
101 .arg_help = "write SIZE bytes at a time",
105 .arg_name = "count",
106 .arg_type = ARG_NUMERIC,
107 .arg_help = "copy COUNT input blocks",
110 .arg_name = "seek",
111 .arg_type = ARG_NUMERIC,
112 .arg_help = "skip COUNT blocks at start of output",
115 .arg_name = "skip",
116 .arg_type = ARG_NUMERIC,
117 .arg_help = "skip COUNT blocks at start of input",
121 .arg_name = "if",
122 .arg_type = ARG_PATHNAME,
123 .arg_help = "read input from FILE",
126 .arg_name = "of",
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",
137 .arg_name = "sync",
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)
154 int i;
156 for (i = 0; i < ARRAY_SIZE(args); ++i) {
157 if (strwicmp(arg, args[i].arg_name) == 0) {
158 return(&args[i]);
162 return(NULL);
165 int set_arg_argv(const char * argv)
167 struct argdef * arg;
169 char * name;
170 char * val;
172 if ((name = strdup(argv)) == NULL) {
173 return(0);
176 if ((val = strchr(name, '=')) == NULL) {
177 fprintf(stderr, "%s: malformed argument \"%s\"\n",
178 PROGNAME, argv);
179 goto fail;
182 *val = '\0';
183 val++;
185 if ((arg = find_named_arg(name)) == NULL) {
186 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
187 PROGNAME, name);
188 goto fail;
191 /* Found a matching name; convert the variable argument. */
192 switch (arg->arg_type) {
193 case ARG_NUMERIC:
194 if (!conv_str_u64(val, &arg->arg_val.nval)) {
195 goto fail;
197 break;
198 case ARG_SIZE:
199 if (!conv_str_size_error(val, &arg->arg_val.nval)) {
200 goto fail;
202 break;
203 case ARG_BOOL:
204 if (!conv_str_bool(val, &arg->arg_val.bval)) {
205 goto fail;
207 break;
208 case ARG_PATHNAME:
209 if (!(arg->arg_val.pval = strdup(val))) {
210 goto fail;
212 break;
213 default:
214 fprintf(stderr, "%s: argument \"%s\" is of "
215 "unknown type\n", PROGNAME, name);
216 goto fail;
219 free(name);
220 return(1);
222 fail:
223 free(name);
224 return(0);
227 void set_arg_val(const char * name, ...)
229 va_list ap;
230 struct argdef * arg;
232 va_start(ap, name);
233 if ((arg = find_named_arg(name)) == NULL) {
234 goto fail;
237 /* Found a matching name; convert the variable argument. */
238 switch (arg->arg_type) {
239 case ARG_NUMERIC:
240 case ARG_SIZE:
241 arg->arg_val.nval = va_arg(ap, uint64_t);
242 break;
243 case ARG_BOOL:
244 arg->arg_val.bval = va_arg(ap, int);
245 break;
246 case ARG_PATHNAME:
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);
251 break;
252 default:
253 fprintf(stderr, "%s: argument \"%s\" is of "
254 "unknown type\n", PROGNAME, name);
255 goto fail;
258 va_end(ap);
259 return;
261 fail:
262 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
263 PROGNAME, name);
264 va_end(ap);
265 return;
268 bool check_arg_bool(const char * name)
270 struct argdef * arg;
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));
278 SMB_ASSERT(0);
279 return(false);
282 uint64_t check_arg_numeric(const char * name)
284 struct argdef * arg;
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));
292 SMB_ASSERT(0);
293 return(-1);
296 const char * check_arg_pathname(const char * name)
298 struct argdef * arg;
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));
306 SMB_ASSERT(0);
307 return(NULL);
310 static void dump_args(void)
312 int i;
314 DEBUG(10, ("dumping argument values:\n"));
315 for (i = 0; i < ARRAY_SIZE(args); ++i) {
316 switch (args[i].arg_type) {
317 case ARG_NUMERIC:
318 case ARG_SIZE:
319 DEBUG(10, ("\t%s=%llu\n", args[i].arg_name,
320 (unsigned long long)args[i].arg_val.nval));
321 break;
322 case ARG_BOOL:
323 DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
324 args[i].arg_val.bval ? "yes" : "no"));
325 break;
326 case ARG_PATHNAME:
327 DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
328 args[i].arg_val.pval ?
329 args[i].arg_val.pval :
330 "(NULL)"));
331 break;
332 default:
333 SMB_ASSERT(0);
338 static void cifsdd_help_message(poptContext pctx,
339 enum poptCallbackReason preason,
340 struct poptOption * poption,
341 const char * parg,
342 void * pdata)
344 static const char notes[] =
345 "FILE can be a local filename or a UNC path of the form //server/share/path.\n";
347 char prefix[24];
348 int i;
350 if (poption->shortName != '?') {
351 poptPrintUsage(pctx, stdout, 0);
352 fprintf(stdout, " [dd options]\n");
353 exit(0);
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) {
361 break;
364 snprintf(prefix, sizeof(prefix), "%s=%-*s",
365 args[i].arg_name,
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);
373 exit(0);
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);
391 } else {
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)
408 int options = 0;
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,
428 socket_options,
429 smb_options, smb_session_options,
430 gensec_settings);
431 } else if (strcmp(which, "of") == 0) {
432 options |= DD_WRITE;
433 path = check_arg_pathname("of");
434 handle = dd_open_path(resolve_ctx, ev, path, ports,
435 check_arg_numeric("obs"), options,
436 socket_options,
437 smb_options, smb_session_options,
438 gensec_settings);
439 } else {
440 SMB_ASSERT(0);
441 return(NULL);
444 if (!handle) {
445 fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
448 return(handle);
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. */
457 uint64_t ibs;
458 uint64_t obs;
459 uint64_t count;
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) {
479 fprintf(stderr,
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),
493 &session_options,
494 lpcfg_gensec_settings(lp_ctx, lp_ctx)))) {
495 SAFE_FREE(iobuf);
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),
502 &session_options,
503 lpcfg_gensec_settings(lp_ctx, lp_ctx)))) {
504 SAFE_FREE(iobuf);
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
519 * copying.
521 if (dd_sigint) {
522 break;
525 if (dd_sigusr1) {
526 print_transfer_stats();
527 dd_sigusr1 = 0;
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,
535 &data_size, obs)) {
536 SAFE_FREE(iobuf);
537 return(IOERROR_EXIT_CODE);
540 goto done;
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)) {
547 SAFE_FREE(iobuf);
548 return(IOERROR_EXIT_CODE);
551 if (data_size == 0) {
552 /* Done. */
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
567 * file is too small.
569 if (data_size &&
570 !dd_flush_block(ofile, iobuf, &data_size, obs)) {
571 SAFE_FREE(iobuf);
572 return(IOERROR_EXIT_CODE);
576 done:
577 SAFE_FREE(iobuf);
578 print_transfer_stats();
579 return(0);
582 /* ------------------------------------------------------------------------- */
583 /* Main. */
584 /* ------------------------------------------------------------------------- */
586 struct poptOption cifsddHelpOptions[] = {
588 .longName = NULL,
589 .shortName = '\0',
590 .argInfo = POPT_ARG_CALLBACK,
591 .arg = (void *)&cifsdd_help_message,
592 .val = '\0',
595 .longName = "help",
596 .shortName = '?',
597 .val = '?',
598 .descrip = "Show this help message",
601 .longName = "usage",
602 .shortName = '\0',
603 .val = 'u',
604 .descrip = "Display brief usage message",
606 POPT_TABLEEND
609 int main(int argc, char *argv[])
611 const char **const_argv = discard_const_p(const char *, argv);
612 int i;
613 const char ** dd_args;
614 TALLOC_CTX *mem_ctx = NULL;
615 struct loadparm_context *lp_ctx = NULL;
616 struct tevent_context *ev;
617 bool ok;
618 int rc;
620 poptContext pctx;
621 struct poptOption poptions[] = {
622 /* POPT_AUTOHELP */
623 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cifsddHelpOptions,
624 0, "Help options:", NULL },
625 POPT_COMMON_SAMBA
626 POPT_COMMON_CONNECTION
627 POPT_COMMON_CREDENTIALS
628 POPT_LEGACY_S4
629 POPT_COMMON_VERSION
630 POPT_TABLEEND
633 /* Block sizes. */
634 set_arg_val("bs", (uint64_t)4096);
635 set_arg_val("ibs", (uint64_t)4096);
636 set_arg_val("obs", (uint64_t)4096);
637 /* Block counts. */
638 set_arg_val("count", (uint64_t)-1);
639 set_arg_val("seek", (uint64_t)0);
640 set_arg_val("skip", (uint64_t)0);
641 /* Files. */
642 set_arg_val("if", NULL);
643 set_arg_val("of", NULL);
644 /* Options. */
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");
652 exit(1);
655 ok = samba_cmdline_init(mem_ctx,
656 SAMBA_CMDLINE_CONFIG_CLIENT,
657 false /* require_smbconf */);
658 if (!ok) {
659 DBG_ERR("Failed to init cmdline parser!\n");
660 TALLOC_FREE(mem_ctx);
661 exit(1);
664 pctx = samba_popt_get_context(getprogname(), argc, const_argv, poptions, 0);
665 if (pctx == NULL) {
666 DBG_ERR("Failed to setup popt context!\n");
667 TALLOC_FREE(mem_ctx);
668 exit(1);
671 while ((i = poptGetNextOpt(pctx)) != -1) {
672 switch (i) {
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);
677 exit(1);
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",
685 PROGNAME, *dd_args);
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();
703 gensec_init();
704 dump_args();
706 if (check_arg_numeric("ibs") == 0 || check_arg_numeric("obs") == 0) {
707 fprintf(stderr, "%s: block sizes must be greater that zero\n",
708 PROGNAME);
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);
730 return rc;
733 /* vim: set sw=8 sts=8 ts=8 tw=79 : */