dadff84bda53df3af74676ab50a5cb7ba72b5be2
[bbaumbach/samba-autobuild/.git] / source4 / client / cifsdd.c
1 /*
2    CIFSDD - dd for SMB.
3    Main program, argument handling and block copying.
4
5    Copyright (C) James Peach 2005-2006
6
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.
11
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.
16
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/>.
19 */
20
21 #include "includes.h"
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"
27 #include "lib/events/events.h"
28
29 #include "cifsdd.h"
30 #include "param/param.h"
31
32 const char * const PROGNAME = "cifsdd";
33
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. */
38
39 struct  dd_stats_record dd_stats;
40
41 static int dd_sigint;
42 static int dd_sigusr1;
43
44 static void dd_handle_signal(int sig)
45 {
46         switch (sig)
47         {
48                 case SIGINT:
49                         ++dd_sigint;
50                         break;
51                 case SIGUSR1:
52                         ++dd_sigusr1;
53                         break;
54                 default:
55                         break;
56         }
57 }
58
59 /* ------------------------------------------------------------------------- */
60 /* Argument handling.                                                        */
61 /* ------------------------------------------------------------------------- */
62
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" },
71 };
72
73 static const char * argtype_str(enum argtype arg_type)
74 {
75         int i;
76
77         for (i = 0; i < ARRAY_SIZE(names); ++i) {
78                 if (arg_type == names[i].arg_type) {
79                         return(names[i].arg_name);
80                 }
81         }
82
83         return("<unknown>");
84 }
85
86 static struct argdef args[] =
87 {
88         {
89                 .arg_name = "bs",
90                 .arg_type = ARG_SIZE,
91                 .arg_help = "force ibs and obs to SIZE bytes",
92         },
93         {
94                 .arg_name = "ibs",
95                 .arg_type = ARG_SIZE,
96                 .arg_help = "read SIZE bytes at a time",
97         },
98         {
99                 .arg_name = "obs",
100                 .arg_type = ARG_SIZE,
101                 .arg_help = "write SIZE bytes at a time",
102         },
103
104         {
105                 .arg_name = "count",
106                 .arg_type = ARG_NUMERIC,
107                 .arg_help = "copy COUNT input blocks",
108         },
109         {
110                 .arg_name = "seek",
111                 .arg_type = ARG_NUMERIC,
112                 .arg_help = "skip COUNT blocks at start of output",
113         },
114         {
115                 .arg_name = "skip",
116                 .arg_type = ARG_NUMERIC,
117                 .arg_help = "skip COUNT blocks at start of input",
118         },
119
120         {
121                 .arg_name = "if",
122                 .arg_type = ARG_PATHNAME,
123                 .arg_help = "read input from FILE",
124         },
125         {
126                 .arg_name = "of",
127                 .arg_type = ARG_PATHNAME,
128                 .arg_help = "write output to FILE",
129         },
130
131         {
132                 .arg_name = "direct",
133                 .arg_type = ARG_BOOL,
134                 .arg_help = "use direct I/O if non-zero",
135         },
136         {
137                 .arg_name = "sync",
138                 .arg_type = ARG_BOOL,
139                 .arg_help = "use synchronous writes if non-zero",
140         },
141         {
142                 .arg_name = "oplock",
143                 .arg_type = ARG_BOOL,
144                 .arg_help = "take oplocks on the input and output files",
145         },
146
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.
149  */
150 };
151
152 static struct argdef * find_named_arg(const char * arg)
153 {
154         int i;
155
156         for (i = 0; i < ARRAY_SIZE(args); ++i) {
157                 if (strwicmp(arg, args[i].arg_name) == 0) {
158                         return(&args[i]);
159                 }
160         }
161
162         return(NULL);
163 }
164
165 int set_arg_argv(const char * argv)
166 {
167         struct argdef * arg;
168
169         char *  name;
170         char *  val;
171
172         if ((name = strdup(argv)) == NULL) {
173                 return(0);
174         }
175
176         if ((val = strchr(name, '=')) == NULL) {
177                 fprintf(stderr, "%s: malformed argument \"%s\"\n",
178                                 PROGNAME, argv);
179                 goto fail;
180         }
181
182         *val = '\0';
183         val++;
184
185         if ((arg = find_named_arg(name)) == NULL) {
186                 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
187                                 PROGNAME, name);
188                 goto fail;
189         }
190
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;
196                         }
197                         break;
198                 case ARG_SIZE:
199                         if (!conv_str_size_error(val, &arg->arg_val.nval)) {
200                                 goto fail;
201                         }
202                         break;
203                 case ARG_BOOL:
204                         if (!conv_str_bool(val, &arg->arg_val.bval)) {
205                                 goto fail;
206                         }
207                         break;
208                 case ARG_PATHNAME:
209                         if (!(arg->arg_val.pval = strdup(val))) {
210                                 goto fail;
211                         }
212                         break;
213                 default:
214                         fprintf(stderr, "%s: argument \"%s\" is of "
215                                 "unknown type\n", PROGNAME, name);
216                         goto fail;
217         }
218
219         free(name);
220         return(1);
221
222 fail:
223         free(name);
224         return(0);
225 }
226
227 void set_arg_val(const char * name, ...)
228 {
229         va_list         ap;
230         struct argdef * arg;
231
232         va_start(ap, name);
233         if ((arg = find_named_arg(name)) == NULL) {
234                 goto fail;
235         }
236
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);
250                         }
251                         break;
252                 default:
253                         fprintf(stderr, "%s: argument \"%s\" is of "
254                                 "unknown type\n", PROGNAME, name);
255                         goto fail;
256         }
257
258         va_end(ap);
259         return;
260
261 fail:
262         fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
263                         PROGNAME, name);
264         va_end(ap);
265         return;
266 }
267
268 bool check_arg_bool(const char * name)
269 {
270         struct argdef * arg;
271
272         if ((arg = find_named_arg(name)) &&
273             (arg->arg_type == ARG_BOOL)) {
274                 return(arg->arg_val.bval);
275         }
276
277         DEBUG(0, ("invalid argument name: %s", name));
278         SMB_ASSERT(0);
279         return(false);
280 }
281
282 uint64_t check_arg_numeric(const char * name)
283 {
284         struct argdef * arg;
285
286         if ((arg = find_named_arg(name)) &&
287             (arg->arg_type == ARG_NUMERIC || arg->arg_type == ARG_SIZE)) {
288                 return(arg->arg_val.nval);
289         }
290
291         DEBUG(0, ("invalid argument name: %s", name));
292         SMB_ASSERT(0);
293         return(-1);
294 }
295
296 const char * check_arg_pathname(const char * name)
297 {
298         struct argdef * arg;
299
300         if ((arg = find_named_arg(name)) &&
301             (arg->arg_type == ARG_PATHNAME)) {
302                 return(arg->arg_val.pval);
303         }
304
305         DEBUG(0, ("invalid argument name: %s", name));
306         SMB_ASSERT(0);
307         return(NULL);
308 }
309
310 static void dump_args(void)
311 {
312         int i;
313
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);
334                 }
335         }
336 }
337
338 static void cifsdd_help_message(poptContext pctx,
339                 enum poptCallbackReason preason,
340                 struct poptOption * poption, 
341                 const char * parg,
342                 void * pdata)
343 {
344         static const char notes[] = 
345 "FILE can be a local filename or a UNC path of the form //server/share/path.\n";
346
347         char prefix[24];
348         int i;
349
350         if (poption->shortName != '?') {
351                 poptPrintUsage(pctx, stdout, 0);
352                 fprintf(stdout, "        [dd options]\n");
353                 exit(0);
354         }
355
356         poptPrintHelp(pctx, stdout, 0);
357         fprintf(stdout, "\nCIFS dd options:\n");
358
359         for (i = 0; i < ARRAY_SIZE(args); ++i) {
360                 if (args[i].arg_name == NULL) {
361                         break;
362                 }
363
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);
370         }
371
372         fprintf(stdout, "\n%s\n", notes);
373         exit(0);
374 }
375
376 /* ------------------------------------------------------------------------- */
377 /* Main block copying routine.                                               */
378 /* ------------------------------------------------------------------------- */
379
380 static void print_transfer_stats(void)
381 {
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);
397         }
398 }
399
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)
407 {
408         int                     options = 0;
409         const char *            path = NULL;
410         struct dd_iohandle *    handle = NULL;
411
412         if (check_arg_bool("direct")) {
413                 options |= DD_DIRECT_IO;
414         }
415
416         if (check_arg_bool("sync")) {
417                 options |= DD_SYNC_IO;
418         }
419
420         if (check_arg_bool("oplock")) {
421                 options |= DD_OPLOCK;
422         }
423
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);
442         }
443
444         if (!handle) {
445                 fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
446         }
447
448         return(handle);
449 }
450
451 static int copy_files(struct tevent_context *ev, struct loadparm_context *lp_ctx)
452 {
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. */
456
457         uint64_t        ibs;
458         uint64_t        obs;
459         uint64_t        count;
460
461         struct dd_iohandle *    ifile;
462         struct dd_iohandle *    ofile;
463
464         struct smbcli_options options;
465         struct smbcli_session_options session_options;
466
467         ibs = check_arg_numeric("ibs");
468         obs = check_arg_numeric("obs");
469         count = check_arg_numeric("count");
470
471         lpcfg_smbcli_options(lp_ctx, &options);
472         lpcfg_smbcli_session_options(lp_ctx, &session_options);
473
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.
476          */
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);
483         }
484
485         options.max_xmit = MAX(ibs, obs);
486
487         DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
488                         (unsigned long long)iomax, options.max_xmit));
489
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);
497         }
498
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);
506         }
507
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);
511
512         DEBUG(4, ("max xmit was negotiated to be %d\n", options.max_xmit));
513
514         for (data_size = 0;;) {
515
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.
520                  */
521                 if (dd_sigint) {
522                         break;
523                 }
524
525                 if (dd_sigusr1) {
526                         print_transfer_stats();
527                         dd_sigusr1 = 0;
528                 }
529
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);
538                                 }
539                         }
540                         goto done;
541                 }
542
543                 /* Try and read enough blocks of ibs bytes to be able write
544                  * out one of obs bytes.
545                  */
546                 if (!dd_fill_block(ifile, iobuf, &data_size, obs, ibs)) {
547                         SAFE_FREE(iobuf);
548                         return(IOERROR_EXIT_CODE);
549                 }
550
551                 if (data_size == 0) {
552                         /* Done. */
553                         SMB_ASSERT(ifile->io_flags & DD_END_OF_FILE);
554                 }
555
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;
559                 }
560
561                 /* If we wanted to be a legitimate dd, we would do character
562                  * conversions and other shenanigans here.
563                  */
564
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.
568                  */
569                 if (data_size && 
570                     !dd_flush_block(ofile, iobuf, &data_size, obs)) {
571                         SAFE_FREE(iobuf);
572                         return(IOERROR_EXIT_CODE);
573                 }
574         }
575
576 done:
577         SAFE_FREE(iobuf);
578         print_transfer_stats();
579         return(0);
580 }
581
582 /* ------------------------------------------------------------------------- */
583 /* Main.                                                                     */
584 /* ------------------------------------------------------------------------- */
585
586 struct poptOption cifsddHelpOptions[] = {
587         {
588                 .longName   = NULL,
589                 .shortName  = '\0',
590                 .argInfo    = POPT_ARG_CALLBACK,
591                 .arg        = (void *)&cifsdd_help_message,
592                 .val        = '\0',
593         },
594         {
595                 .longName   = "help",
596                 .shortName  = '?',
597                 .val        = '?',
598                 .descrip    = "Show this help message",
599         },
600         {
601                 .longName   = "usage",
602                 .shortName  = '\0',
603                 .val        = 'u',
604                 .descrip    = "Display brief usage message",
605         },
606         POPT_TABLEEND
607 };
608
609 int main(int argc, const char ** argv)
610 {
611         int i;
612         const char ** dd_args;
613         struct tevent_context *ev;
614         int rc;
615
616         poptContext pctx;
617         struct poptOption poptions[] = {
618                 /* POPT_AUTOHELP */
619                 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cifsddHelpOptions,
620                         0, "Help options:", NULL },
621                 POPT_COMMON_SAMBA
622                 POPT_COMMON_CONNECTION
623                 POPT_COMMON_CREDENTIALS
624                 POPT_COMMON_VERSION
625                 { NULL }
626         };
627
628         /* Block sizes. */
629         set_arg_val("bs", (uint64_t)4096);
630         set_arg_val("ibs", (uint64_t)4096);
631         set_arg_val("obs", (uint64_t)4096);
632         /* Block counts. */
633         set_arg_val("count", (uint64_t)-1);
634         set_arg_val("seek", (uint64_t)0);
635         set_arg_val("skip", (uint64_t)0);
636         /* Files. */
637         set_arg_val("if", NULL);
638         set_arg_val("of", NULL);
639         /* Options. */
640         set_arg_val("direct", false);
641         set_arg_val("sync", false);
642         set_arg_val("oplock", false);
643
644         pctx = poptGetContext(PROGNAME, argc, argv, poptions, 0);
645         while ((i = poptGetNextOpt(pctx)) != -1) {
646                 ;
647         }
648
649         for (dd_args = poptGetArgs(pctx); dd_args && *dd_args; ++dd_args) {
650
651                 if (!set_arg_argv(*dd_args)) {
652                         fprintf(stderr, "%s: invalid option: %s\n",
653                                         PROGNAME, *dd_args);
654                         exit(SYNTAX_EXIT_CODE);
655                 }
656
657                 /* "bs" has the side-effect of setting "ibs" and "obs". */
658                 if (strncmp(*dd_args, "bs=", 3) == 0) {
659                         uint64_t bs = check_arg_numeric("bs");
660                         set_arg_val("ibs", bs);
661                         set_arg_val("obs", bs);
662                 }
663         }
664
665         ev = s4_event_context_init(NULL);
666
667         gensec_init();
668         dump_args();
669
670         if (check_arg_numeric("ibs") == 0 || check_arg_numeric("obs") == 0) {
671                 fprintf(stderr, "%s: block sizes must be greater that zero\n",
672                                 PROGNAME);
673                 talloc_free(ev);
674                 exit(SYNTAX_EXIT_CODE);
675         }
676
677         if (check_arg_pathname("if") == NULL) {
678                 fprintf(stderr, "%s: missing input filename\n", PROGNAME);
679                 talloc_free(ev);
680                 exit(SYNTAX_EXIT_CODE);
681         }
682
683         if (check_arg_pathname("of") == NULL) {
684                 fprintf(stderr, "%s: missing output filename\n", PROGNAME);
685                 talloc_free(ev);
686                 exit(SYNTAX_EXIT_CODE);
687         }
688
689         CatchSignal(SIGINT, dd_handle_signal);
690         CatchSignal(SIGUSR1, dd_handle_signal);
691         rc = copy_files(ev, cmdline_lp_ctx);
692
693         poptFreeContext(pctx);
694         talloc_free(ev);
695         return rc;
696 }
697
698 /* vim: set sw=8 sts=8 ts=8 tw=79 : */