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