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