Merge branch 'master' of ssh://git.samba.org/data/git/samba into selftest
[gd/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 char * argtype_str(enum argtype arg_type)
64 {
65         static const struct {
66                 enum argtype arg_type;
67                 const char * arg_name;
68         } names [] = 
69         {
70                 { ARG_NUMERIC, "COUNT" },
71                 { ARG_SIZE, "SIZE" },
72                 { ARG_PATHNAME, "FILE" },
73                 { ARG_BOOL, "BOOLEAN" },
74         };
75
76         int i;
77
78         for (i = 0; i < ARRAY_SIZE(names); ++i) {
79                 if (arg_type == names[i].arg_type) {
80                         return(names[i].arg_name);
81                 }
82         }
83
84         return("<unknown>");
85 }
86
87 static struct argdef args[] =
88 {
89         { "bs", ARG_SIZE,       "force ibs and obs to SIZE bytes" },
90         { "ibs", ARG_SIZE,      "read SIZE bytes at a time" },
91         { "obs", ARG_SIZE,      "write SIZE bytes at a time" },
92
93         { "count", ARG_NUMERIC, "copy COUNT input blocks" },
94         { "seek",ARG_NUMERIC,   "skip COUNT blocks at start of output" },
95         { "skip",ARG_NUMERIC,   "skip COUNT blocks at start of input" },
96
97         { "if", ARG_PATHNAME,   "read input from FILE" },
98         { "of", ARG_PATHNAME,   "write output to FILE" },
99
100         { "direct", ARG_BOOL,   "use direct I/O if non-zero" },
101         { "sync", ARG_BOOL,     "use synchronous writes if non-zero" },
102         { "oplock", ARG_BOOL,   "take oplocks on the input and output files" },
103
104 /* FIXME: We should support using iflags and oflags for setting oplock and I/O
105  * options. This would make us compatible with GNU dd.
106  */
107 };
108
109 static struct argdef * find_named_arg(const char * arg)
110 {
111         int i;
112
113         for (i = 0; i < ARRAY_SIZE(args); ++i) {
114                 if (strwicmp(arg, args[i].arg_name) == 0) {
115                         return(&args[i]);
116                 }
117         }
118
119         return(NULL);
120 }
121
122 int set_arg_argv(const char * argv)
123 {
124         struct argdef * arg;
125
126         char *  name;
127         char *  val;
128
129         if ((name = strdup(argv)) == NULL) {
130                 return(0);
131         }
132
133         if ((val = strchr(name, '=')) == NULL) {
134                 fprintf(stderr, "%s: malformed argument \"%s\"\n",
135                                 PROGNAME, argv);
136                 goto fail;
137         }
138
139         *val = '\0';
140         val++;
141
142         if ((arg = find_named_arg(name)) == NULL) {
143                 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
144                                 PROGNAME, name);
145                 goto fail;
146         }
147
148         /* Found a matching name; convert the variable argument. */
149         switch (arg->arg_type) {
150                 case ARG_NUMERIC:
151                         if (!conv_str_u64(val, &arg->arg_val.nval)) {
152                                 goto fail;
153                         }
154                         break;
155                 case ARG_SIZE:
156                         if (!conv_str_size(val, &arg->arg_val.nval)) {
157                                 goto fail;
158                         }
159                         break;
160                 case ARG_BOOL:
161                         if (!conv_str_bool(val, &arg->arg_val.bval)) {
162                                 goto fail;
163                         }
164                         break;
165                 case ARG_PATHNAME:
166                         if (!(arg->arg_val.pval = strdup(val))) {
167                                 goto fail;
168                         }
169                         break;
170                 default:
171                         fprintf(stderr, "%s: argument \"%s\" is of "
172                                 "unknown type\n", PROGNAME, name);
173                         goto fail;
174         }
175
176         free(name);
177         return(1);
178
179 fail:
180         free(name);
181         return(0);
182 }
183
184 void set_arg_val(const char * name, ...)
185 {
186         va_list         ap;
187         struct argdef * arg;
188
189         va_start(ap, name);
190         if ((arg = find_named_arg(name)) == NULL) {
191                 goto fail;
192         }
193
194         /* Found a matching name; convert the variable argument. */
195         switch (arg->arg_type) {
196                 case ARG_NUMERIC:
197                 case ARG_SIZE:
198                         arg->arg_val.nval = va_arg(ap, uint64_t);
199                         break;
200                 case ARG_BOOL:
201                         arg->arg_val.bval = va_arg(ap, int);
202                         break;
203                 case ARG_PATHNAME:
204                         arg->arg_val.pval = va_arg(ap, char *);
205                         if (arg->arg_val.pval) {
206                                 arg->arg_val.pval = strdup(arg->arg_val.pval);
207                         }
208                         break;
209                 default:
210                         fprintf(stderr, "%s: argument \"%s\" is of "
211                                 "unknown type\n", PROGNAME, name);
212                         goto fail;
213         }
214
215         va_end(ap);
216         return;
217
218 fail:
219         fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
220                         PROGNAME, name);
221         va_end(ap);
222         return;
223 }
224
225 bool check_arg_bool(const char * name)
226 {
227         struct argdef * arg;
228
229         if ((arg = find_named_arg(name)) &&
230             (arg->arg_type == ARG_BOOL)) {
231                 return(arg->arg_val.bval);
232         }
233
234         DEBUG(0, ("invalid argument name: %s", name));
235         SMB_ASSERT(0);
236         return(false);
237 }
238
239 uint64_t check_arg_numeric(const char * name)
240 {
241         struct argdef * arg;
242
243         if ((arg = find_named_arg(name)) &&
244             (arg->arg_type == ARG_NUMERIC || arg->arg_type == ARG_SIZE)) {
245                 return(arg->arg_val.nval);
246         }
247
248         DEBUG(0, ("invalid argument name: %s", name));
249         SMB_ASSERT(0);
250         return(-1);
251 }
252
253 const char * check_arg_pathname(const char * name)
254 {
255         struct argdef * arg;
256
257         if ((arg = find_named_arg(name)) &&
258             (arg->arg_type == ARG_PATHNAME)) {
259                 return(arg->arg_val.pval);
260         }
261
262         DEBUG(0, ("invalid argument name: %s", name));
263         SMB_ASSERT(0);
264         return(NULL);
265 }
266
267 static void dump_args(void)
268 {
269         int i;
270
271         DEBUG(10, ("dumping argument values:\n"));
272         for (i = 0; i < ARRAY_SIZE(args); ++i) {
273                 switch (args[i].arg_type) {
274                         case ARG_NUMERIC:
275                         case ARG_SIZE:
276                                 DEBUG(10, ("\t%s=%llu\n", args[i].arg_name,
277                                         (unsigned long long)args[i].arg_val.nval));
278                                 break;
279                         case ARG_BOOL:
280                                 DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
281                                         args[i].arg_val.bval ? "yes" : "no"));
282                                 break;
283                         case ARG_PATHNAME:
284                                 DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
285                                         args[i].arg_val.pval ?
286                                                 args[i].arg_val.pval :
287                                                 "(NULL)"));
288                                 break;
289                         default:
290                                 SMB_ASSERT(0);
291                 }
292         }
293 }
294
295 static void cifsdd_help_message(poptContext pctx,
296                 enum poptCallbackReason preason,
297                 struct poptOption * poption, 
298                 const char * parg,
299                 void * pdata)
300 {
301         static const char notes[] = 
302 "FILE can be a local filename or a UNC path of the form //server/share/path.\n";
303
304         char prefix[24];
305         int i;
306
307         if (poption->shortName != '?') {
308                 poptPrintUsage(pctx, stdout, 0);
309                 fprintf(stdout, "        [dd options]\n");
310                 exit(0);
311         }
312
313         poptPrintHelp(pctx, stdout, 0);
314         fprintf(stdout, "\nCIFS dd options:\n");
315
316         for (i = 0; i < ARRAY_SIZE(args); ++i) {
317                 if (args[i].arg_name == NULL) {
318                         break;
319                 }
320
321                 snprintf(prefix, sizeof(prefix), "%s=%-*s",
322                         args[i].arg_name,
323                         (int)(sizeof(prefix) - strlen(args[i].arg_name) - 2),
324                         argtype_str(args[i].arg_type));
325                 prefix[sizeof(prefix) - 1] = '\0';
326                 fprintf(stdout, "  %s%s\n", prefix, args[i].arg_help);
327         }
328
329         fprintf(stdout, "\n%s\n", notes);
330         exit(0);
331 }
332
333 /* ------------------------------------------------------------------------- */
334 /* Main block copying routine.                                               */
335 /* ------------------------------------------------------------------------- */
336
337 static void print_transfer_stats(void)
338 {
339         if (DEBUGLEVEL > 0) {
340                 printf("%llu+%llu records in (%llu bytes)\n"
341                         "%llu+%llu records out (%llu bytes)\n",
342                         (unsigned long long)dd_stats.in.fblocks,
343                         (unsigned long long)dd_stats.in.pblocks,
344                         (unsigned long long)dd_stats.in.bytes,
345                         (unsigned long long)dd_stats.out.fblocks,
346                         (unsigned long long)dd_stats.out.pblocks,
347                         (unsigned long long)dd_stats.out.bytes);
348         } else {
349                 printf("%llu+%llu records in\n%llu+%llu records out\n",
350                                 (unsigned long long)dd_stats.in.fblocks,
351                                 (unsigned long long)dd_stats.in.pblocks,
352                                 (unsigned long long)dd_stats.out.fblocks,
353                                 (unsigned long long)dd_stats.out.pblocks);
354         }
355 }
356
357 static struct dd_iohandle * open_file(struct resolve_context *resolve_ctx, 
358                                       struct event_context *ev,
359                                       const char * which, const char **ports,
360                                       struct smbcli_options *smb_options,
361                                       struct smbcli_session_options *smb_session_options)
362 {
363         int                     options = 0;
364         const char *            path = NULL;
365         struct dd_iohandle *    handle = NULL;
366
367         if (check_arg_bool("direct")) {
368                 options |= DD_DIRECT_IO;
369         }
370
371         if (check_arg_bool("sync")) {
372                 options |= DD_SYNC_IO;
373         }
374
375         if (check_arg_bool("oplock")) {
376                 options |= DD_OPLOCK;
377         }
378
379         if (strcmp(which, "if") == 0) {
380                 path = check_arg_pathname("if");
381                 handle = dd_open_path(resolve_ctx, ev, path, ports,
382                                       check_arg_numeric("ibs"), options,
383                                       smb_options, smb_session_options);
384         } else if (strcmp(which, "of") == 0) {
385                 options |= DD_WRITE;
386                 path = check_arg_pathname("of");
387                 handle = dd_open_path(resolve_ctx, ev, path, ports,
388                                       check_arg_numeric("obs"), options,
389                                       smb_options, smb_session_options);
390         } else {
391                 SMB_ASSERT(0);
392                 return(NULL);
393         }
394
395         if (!handle) {
396                 fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
397         }
398
399         return(handle);
400 }
401
402 static int copy_files(struct event_context *ev, struct loadparm_context *lp_ctx)
403 {
404         uint8_t *       iobuf;  /* IO buffer. */
405         uint64_t        iomax;  /* Size of the IO buffer. */
406         uint64_t        data_size; /* Amount of data in the IO buffer. */
407
408         uint64_t        ibs;
409         uint64_t        obs;
410         uint64_t        count;
411
412         struct dd_iohandle *    ifile;
413         struct dd_iohandle *    ofile;
414
415         struct smbcli_options options;
416         struct smbcli_session_options session_options;
417
418         ibs = check_arg_numeric("ibs");
419         obs = check_arg_numeric("obs");
420         count = check_arg_numeric("count");
421
422         lp_smbcli_options(lp_ctx, &options);
423         lp_smbcli_session_options(lp_ctx, &session_options);
424
425         /* Allocate IO buffer. We need more than the max IO size because we
426          * could accumulate a remainder if ibs and obs don't match.
427          */
428         iomax = 2 * MAX(ibs, obs);
429         if ((iobuf = malloc_array_p(uint8_t, iomax)) == NULL) {
430                 fprintf(stderr,
431                         "%s: failed to allocate IO buffer of %llu bytes\n",
432                         PROGNAME, (unsigned long long)iomax);
433                 return(EOM_EXIT_CODE);
434         }
435
436         options.max_xmit = MAX(ibs, obs);
437
438         DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
439                         (unsigned long long)iomax, options.max_xmit));
440
441         if (!(ifile = open_file(lp_resolve_context(lp_ctx), ev, "if",
442                                 lp_smb_ports(lp_ctx), &options,
443                                 &session_options))) {
444                 return(FILESYS_EXIT_CODE);
445         }
446
447         if (!(ofile = open_file(lp_resolve_context(lp_ctx), ev, "of",
448                                 lp_smb_ports(lp_ctx), &options,
449                                 &session_options))) {
450                 return(FILESYS_EXIT_CODE);
451         }
452
453         /* Seek the files to their respective starting points. */
454         ifile->io_seek(ifile, check_arg_numeric("skip") * ibs);
455         ofile->io_seek(ofile, check_arg_numeric("seek") * obs);
456
457         DEBUG(4, ("max xmit was negotiated to be %d\n", options.max_xmit));
458
459         for (data_size = 0;;) {
460
461                 /* Handle signals. We are somewhat compatible with GNU dd.
462                  * SIGINT makes us stop, but still print transfer statistics.
463                  * SIGUSR1 makes us print transfer statistics but we continue
464                  * copying.
465                  */
466                 if (dd_sigint) {
467                         break;
468                 }
469
470                 if (dd_sigusr1) {
471                         print_transfer_stats();
472                         dd_sigusr1 = 0;
473                 }
474
475                 if (ifile->io_flags & DD_END_OF_FILE) {
476                         DEBUG(4, ("flushing %llu bytes at EOF\n",
477                                         (unsigned long long)data_size));
478                         while (data_size > 0) {
479                                 if (!dd_flush_block(ofile, iobuf,
480                                                         &data_size, obs)) {
481                                         return(IOERROR_EXIT_CODE);
482                                 }
483                         }
484                         goto done;
485                 }
486
487                 /* Try and read enough blocks of ibs bytes to be able write
488                  * out one of obs bytes.
489                  */
490                 if (!dd_fill_block(ifile, iobuf, &data_size, obs, ibs)) {
491                         return(IOERROR_EXIT_CODE);
492                 }
493
494                 if (data_size == 0) {
495                         /* Done. */
496                         SMB_ASSERT(ifile->io_flags & DD_END_OF_FILE);
497                 }
498
499                 /* Stop reading when we hit the block count. */
500                 if (dd_stats.in.bytes >= (ibs * count)) {
501                         ifile->io_flags |= DD_END_OF_FILE;
502                 }
503
504                 /* If we wanted to be a legitimate dd, we would do character
505                  * conversions and other shenanigans here.
506                  */
507
508                 /* Flush what we read in units of obs bytes. We want to have
509                  * at least obs bytes in the IO buffer but might not if the
510                  * file is too small.
511                  */
512                 if (data_size && 
513                     !dd_flush_block(ofile, iobuf, &data_size, obs)) {
514                         return(IOERROR_EXIT_CODE);
515                 }
516         }
517
518 done:
519         print_transfer_stats();
520         return(0);
521 }
522
523 /* ------------------------------------------------------------------------- */
524 /* Main.                                                                     */
525 /* ------------------------------------------------------------------------- */
526
527 struct poptOption cifsddHelpOptions[] = {
528   { NULL, '\0', POPT_ARG_CALLBACK, (void *)&cifsdd_help_message, '\0', NULL, NULL },
529   { "help", '?', 0, NULL, '?', "Show this help message", NULL },
530   { "usage", '\0', 0, NULL, 'u', "Display brief usage message", NULL },
531   { NULL }
532 } ;
533
534 int main(int argc, const char ** argv)
535 {
536         int i;
537         const char ** dd_args;
538         struct event_context *ev;
539
540         poptContext pctx;
541         struct poptOption poptions[] = {
542                 /* POPT_AUTOHELP */
543                 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cifsddHelpOptions,
544                         0, "Help options:", NULL },
545                 POPT_COMMON_SAMBA
546                 POPT_COMMON_CONNECTION
547                 POPT_COMMON_CREDENTIALS
548                 POPT_COMMON_VERSION
549                 { NULL }
550         };
551
552         /* Block sizes. */
553         set_arg_val("bs", (uint64_t)4096);
554         set_arg_val("ibs", (uint64_t)4096);
555         set_arg_val("obs", (uint64_t)4096);
556         /* Block counts. */
557         set_arg_val("count", (uint64_t)-1);
558         set_arg_val("seek", (uint64_t)0);
559         set_arg_val("seek", (uint64_t)0);
560         /* Files. */
561         set_arg_val("if", NULL);
562         set_arg_val("of", NULL);
563         /* Options. */
564         set_arg_val("direct", false);
565         set_arg_val("sync", false);
566         set_arg_val("oplock", false);
567
568         pctx = poptGetContext(PROGNAME, argc, argv, poptions, 0);
569         while ((i = poptGetNextOpt(pctx)) != -1) {
570                 ;
571         }
572
573         for (dd_args = poptGetArgs(pctx); dd_args && *dd_args; ++dd_args) {
574
575                 if (!set_arg_argv(*dd_args)) {
576                         fprintf(stderr, "%s: invalid option: %s\n",
577                                         PROGNAME, *dd_args);
578                         exit(SYNTAX_EXIT_CODE);
579                 }
580
581                 /* "bs" has the side-effect of setting "ibs" and "obs". */
582                 if (strncmp(*dd_args, "bs=", 3) == 0) {
583                         uint64_t bs = check_arg_numeric("bs");
584                         set_arg_val("ibs", bs);
585                         set_arg_val("obs", bs);
586                 }
587         }
588
589         ev = s4_event_context_init(talloc_autofree_context());
590
591         gensec_init(cmdline_lp_ctx);
592         dump_args();
593
594         if (check_arg_numeric("ibs") == 0 || check_arg_numeric("ibs") == 0) {
595                 fprintf(stderr, "%s: block sizes must be greater that zero\n",
596                                 PROGNAME);
597                 exit(SYNTAX_EXIT_CODE);
598         }
599
600         if (check_arg_pathname("if") == NULL) {
601                 fprintf(stderr, "%s: missing input filename\n", PROGNAME);
602                 exit(SYNTAX_EXIT_CODE);
603         }
604
605         if (check_arg_pathname("of") == NULL) {
606                 fprintf(stderr, "%s: missing output filename\n", PROGNAME);
607                 exit(SYNTAX_EXIT_CODE);
608         }
609
610         CatchSignal(SIGINT, dd_handle_signal);
611         CatchSignal(SIGUSR1, dd_handle_signal);
612         return(copy_files(ev, cmdline_lp_ctx));
613 }
614
615 /* vim: set sw=8 sts=8 ts=8 tw=79 : */