06a5f79ed79e622471cbe1e48ab0889e023e5f0f
[ira/wip.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
26 #include "cifsdd.h"
27 #include "param/param.h"
28
29 const char * const PROGNAME = "cifsdd";
30
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. */
35
36 struct  dd_stats_record dd_stats;
37
38 static int dd_sigint;
39 static int dd_sigusr1;
40
41 static void dd_handle_signal(int sig)
42 {
43         switch (sig)
44         {
45                 case SIGINT:
46                         ++dd_sigint;
47                         break;
48                 case SIGUSR1:
49                         ++dd_sigusr1;
50                         break;
51                 default:
52                         break;
53         }
54 }
55
56 /* ------------------------------------------------------------------------- */
57 /* Argument handling.                                                        */
58 /* ------------------------------------------------------------------------- */
59
60 static const char * argtype_str(enum argtype arg_type)
61 {
62         static const struct {
63                 enum argtype arg_type;
64                 const char * arg_name;
65         } names [] = 
66         {
67                 { ARG_NUMERIC, "COUNT" },
68                 { ARG_SIZE, "SIZE" },
69                 { ARG_PATHNAME, "FILE" },
70                 { ARG_BOOL, "BOOLEAN" },
71         };
72
73         int i;
74
75         for (i = 0; i < ARRAY_SIZE(names); ++i) {
76                 if (arg_type == names[i].arg_type) {
77                         return(names[i].arg_name);
78                 }
79         }
80
81         return("<unknown>");
82 }
83
84 static struct argdef args[] =
85 {
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" },
89
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" },
93
94         { "if", ARG_PATHNAME,   "read input from FILE" },
95         { "of", ARG_PATHNAME,   "write output to FILE" },
96
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" },
100
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.
103  */
104 };
105
106 static struct argdef * find_named_arg(const char * arg)
107 {
108         int i;
109
110         for (i = 0; i < ARRAY_SIZE(args); ++i) {
111                 if (strwicmp(arg, args[i].arg_name) == 0) {
112                         return(&args[i]);
113                 }
114         }
115
116         return(NULL);
117 }
118
119 int set_arg_argv(const char * argv)
120 {
121         struct argdef * arg;
122
123         char *  name;
124         char *  val;
125
126         if ((name = strdup(argv)) == NULL) {
127                 return(0);
128         }
129
130         if ((val = strchr(name, '=')) == NULL) {
131                 fprintf(stderr, "%s: malformed argument \"%s\"\n",
132                                 PROGNAME, argv);
133                 goto fail;
134         }
135
136         *val = '\0';
137         val++;
138
139         if ((arg = find_named_arg(name)) == NULL) {
140                 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
141                                 PROGNAME, name);
142                 goto fail;
143         }
144
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;
150                         }
151                         break;
152                 case ARG_SIZE:
153                         if (!conv_str_size(val, &arg->arg_val.nval)) {
154                                 goto fail;
155                         }
156                         break;
157                 case ARG_BOOL:
158                         if (!conv_str_bool(val, &arg->arg_val.bval)) {
159                                 goto fail;
160                         }
161                         break;
162                 case ARG_PATHNAME:
163                         if (!(arg->arg_val.pval = strdup(val))) {
164                                 goto fail;
165                         }
166                         break;
167                 default:
168                         fprintf(stderr, "%s: argument \"%s\" is of "
169                                 "unknown type\n", PROGNAME, name);
170                         goto fail;
171         }
172
173         free(name);
174         return(1);
175
176 fail:
177         free(name);
178         return(0);
179 }
180
181 void set_arg_val(const char * name, ...)
182 {
183         va_list         ap;
184         struct argdef * arg;
185
186         va_start(ap, name);
187         if ((arg = find_named_arg(name)) == NULL) {
188                 goto fail;
189         }
190
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);
204                         }
205                         break;
206                 default:
207                         fprintf(stderr, "%s: argument \"%s\" is of "
208                                 "unknown type\n", PROGNAME, name);
209                         goto fail;
210         }
211
212         va_end(ap);
213         return;
214
215 fail:
216         fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
217                         PROGNAME, name);
218         va_end(ap);
219         return;
220 }
221
222 bool check_arg_bool(const char * name)
223 {
224         struct argdef * arg;
225
226         if ((arg = find_named_arg(name)) &&
227             (arg->arg_type == ARG_BOOL)) {
228                 return(arg->arg_val.bval);
229         }
230
231         DEBUG(0, ("invalid argument name: %s", name));
232         SMB_ASSERT(0);
233         return(false);
234 }
235
236 uint64_t check_arg_numeric(const char * name)
237 {
238         struct argdef * arg;
239
240         if ((arg = find_named_arg(name)) &&
241             (arg->arg_type == ARG_NUMERIC || arg->arg_type == ARG_SIZE)) {
242                 return(arg->arg_val.nval);
243         }
244
245         DEBUG(0, ("invalid argument name: %s", name));
246         SMB_ASSERT(0);
247         return(-1);
248 }
249
250 const char * check_arg_pathname(const char * name)
251 {
252         struct argdef * arg;
253
254         if ((arg = find_named_arg(name)) &&
255             (arg->arg_type == ARG_PATHNAME)) {
256                 return(arg->arg_val.pval);
257         }
258
259         DEBUG(0, ("invalid argument name: %s", name));
260         SMB_ASSERT(0);
261         return(NULL);
262 }
263
264 static void dump_args(void)
265 {
266         int i;
267
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);
288                 }
289         }
290 }
291
292 static void cifsdd_help_message(poptContext pctx,
293                 enum poptCallbackReason preason,
294                 struct poptOption * poption, 
295                 const char * parg,
296                 void * pdata)
297 {
298         static const char notes[] = 
299 "FILE can be a local filename or a UNC path of the form //server/share/path.\n";
300
301         char prefix[24];
302         int i;
303
304         if (poption->shortName != '?') {
305                 poptPrintUsage(pctx, stdout, 0);
306                 fprintf(stdout, "        [dd options]\n");
307                 exit(0);
308         }
309
310         poptPrintHelp(pctx, stdout, 0);
311         fprintf(stdout, "\nCIFS dd options:\n");
312
313         for (i = 0; i < ARRAY_SIZE(args); ++i) {
314                 if (args[i].arg_name == NULL) {
315                         break;
316                 }
317
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);
324         }
325
326         fprintf(stdout, "\n%s\n", notes);
327         exit(0);
328 }
329
330 /* ------------------------------------------------------------------------- */
331 /* Main block copying routine.                                               */
332 /* ------------------------------------------------------------------------- */
333
334 static void print_transfer_stats(void)
335 {
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);
351         }
352 }
353
354 static struct dd_iohandle * open_file(const char * which)
355 {
356         int                     options = 0;
357         const char *            path = NULL;
358         struct dd_iohandle *    handle = NULL;
359
360         if (check_arg_bool("direct")) {
361                 options |= DD_DIRECT_IO;
362         }
363
364         if (check_arg_bool("sync")) {
365                 options |= DD_SYNC_IO;
366         }
367
368         if (check_arg_bool("oplock")) {
369                 options |= DD_OPLOCK;
370         }
371
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);
384         }
385
386         if (!handle) {
387                 fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
388         }
389
390         return(handle);
391 }
392
393 static void set_max_xmit(struct loadparm_context *lp_ctx, uint64_t iomax)
394 {
395         char buf[64];
396
397         snprintf(buf, sizeof(buf), "%llu", (unsigned long long)iomax);
398         lp_set_cmdline(lp_ctx, "max xmit", buf);
399 }
400
401 static int copy_files(struct loadparm_context *lp_ctx)
402 {
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. */
406
407         uint64_t        ibs;
408         uint64_t        obs;
409         uint64_t        count;
410
411         struct dd_iohandle *    ifile;
412         struct dd_iohandle *    ofile;
413
414         ibs = check_arg_numeric("ibs");
415         obs = check_arg_numeric("obs");
416         count = check_arg_numeric("count");
417
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.
420          */
421         iomax = 2 * MAX(ibs, obs);
422         if ((iobuf = malloc_array_p(uint8_t, 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);
427         }
428
429         set_max_xmit(lp_ctx, MAX(ibs, obs));
430
431         DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
432                         (unsigned long long)iomax, lp_max_xmit(lp_ctx)));
433
434         if (!(ifile = open_file("if"))) {
435                 return(FILESYS_EXIT_CODE);
436         }
437
438         if (!(ofile = open_file("of"))) {
439                 return(FILESYS_EXIT_CODE);
440         }
441
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);
445
446         DEBUG(4, ("max xmit was negotiated to be %d\n", lp_max_xmit(lp_ctx)));
447
448         for (data_size = 0;;) {
449
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.
454                  */
455                 if (dd_sigint) {
456                         break;
457                 }
458
459                 if (dd_sigusr1) {
460                         print_transfer_stats();
461                         dd_sigusr1 = 0;
462                 }
463
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);
471                                 }
472                         }
473                         goto done;
474                 }
475
476                 /* Try and read enough blocks of ibs bytes to be able write
477                  * out one of obs bytes.
478                  */
479                 if (!dd_fill_block(ifile, iobuf, &data_size, obs, ibs)) {
480                         return(IOERROR_EXIT_CODE);
481                 }
482
483                 if (data_size == 0) {
484                         /* Done. */
485                         SMB_ASSERT(ifile->io_flags & DD_END_OF_FILE);
486                 }
487
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;
491                 }
492
493                 /* If we wanted to be a legitimate dd, we would do character
494                  * conversions and other shenanigans here.
495                  */
496
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.
500                  */
501                 if (data_size && 
502                     !dd_flush_block(ofile, iobuf, &data_size, obs)) {
503                         return(IOERROR_EXIT_CODE);
504                 }
505         }
506
507 done:
508         print_transfer_stats();
509         return(0);
510 }
511
512 /* ------------------------------------------------------------------------- */
513 /* Main.                                                                     */
514 /* ------------------------------------------------------------------------- */
515
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 }
521 } ;
522
523 int main(int argc, const char ** argv)
524 {
525         int i;
526         const char ** dd_args;
527
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 }
538         };
539
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);
555
556         pctx = poptGetContext(PROGNAME, argc, argv, poptions, 0);
557         while ((i = poptGetNextOpt(pctx)) != -1) {
558                 ;
559         }
560
561         for (dd_args = poptGetArgs(pctx); dd_args && *dd_args; ++dd_args) {
562
563                 if (!set_arg_argv(*dd_args)) {
564                         fprintf(stderr, "%s: invalid option: %s\n",
565                                         PROGNAME, *dd_args);
566                         exit(SYNTAX_EXIT_CODE);
567                 }
568
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);
574                 }
575         }
576
577         gensec_init(global_loadparm);
578         dump_args();
579
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);
584         }
585
586         if (check_arg_pathname("if") == NULL) {
587                 fprintf(stderr, "%s: missing input filename\n", PROGNAME);
588                 exit(SYNTAX_EXIT_CODE);
589         }
590
591         if (check_arg_pathname("of") == NULL) {
592                 fprintf(stderr, "%s: missing output filename\n", PROGNAME);
593                 exit(SYNTAX_EXIT_CODE);
594         }
595
596         CatchSignal(SIGINT, dd_handle_signal);
597         CatchSignal(SIGUSR1, dd_handle_signal);
598         return(copy_files(global_loadparm));
599 }
600
601 /* vim: set sw=8 sts=8 ts=8 tw=79 : */