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