source: trunk/server/source4/client/cifsdd.c@ 745

Last change on this file since 745 was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 15.7 KB
Line 
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
32const 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
39struct dd_stats_record dd_stats;
40
41static int dd_sigint;
42static int dd_sigusr1;
43
44static 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
63static 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
73static 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
86static 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
108static 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
121int 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(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
178fail:
179 free(name);
180 return(0);
181}
182
183void 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
217fail:
218 fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
219 PROGNAME, name);
220 va_end(ap);
221 return;
222}
223
224bool 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
238uint64_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
252const 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
266static 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
294static 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
336static 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
356static 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
407static 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 return(FILESYS_EXIT_CODE);
452 }
453
454 if (!(ofile = open_file(lpcfg_resolve_context(lp_ctx), ev, "of",
455 lpcfg_smb_ports(lp_ctx), &options,
456 lpcfg_socket_options(lp_ctx),
457 &session_options,
458 lpcfg_gensec_settings(lp_ctx, lp_ctx)))) {
459 return(FILESYS_EXIT_CODE);
460 }
461
462 /* Seek the files to their respective starting points. */
463 ifile->io_seek(ifile, check_arg_numeric("skip") * ibs);
464 ofile->io_seek(ofile, check_arg_numeric("seek") * obs);
465
466 DEBUG(4, ("max xmit was negotiated to be %d\n", options.max_xmit));
467
468 for (data_size = 0;;) {
469
470 /* Handle signals. We are somewhat compatible with GNU dd.
471 * SIGINT makes us stop, but still print transfer statistics.
472 * SIGUSR1 makes us print transfer statistics but we continue
473 * copying.
474 */
475 if (dd_sigint) {
476 break;
477 }
478
479 if (dd_sigusr1) {
480 print_transfer_stats();
481 dd_sigusr1 = 0;
482 }
483
484 if (ifile->io_flags & DD_END_OF_FILE) {
485 DEBUG(4, ("flushing %llu bytes at EOF\n",
486 (unsigned long long)data_size));
487 while (data_size > 0) {
488 if (!dd_flush_block(ofile, iobuf,
489 &data_size, obs)) {
490 return(IOERROR_EXIT_CODE);
491 }
492 }
493 goto done;
494 }
495
496 /* Try and read enough blocks of ibs bytes to be able write
497 * out one of obs bytes.
498 */
499 if (!dd_fill_block(ifile, iobuf, &data_size, obs, ibs)) {
500 return(IOERROR_EXIT_CODE);
501 }
502
503 if (data_size == 0) {
504 /* Done. */
505 SMB_ASSERT(ifile->io_flags & DD_END_OF_FILE);
506 }
507
508 /* Stop reading when we hit the block count. */
509 if (dd_stats.in.bytes >= (ibs * count)) {
510 ifile->io_flags |= DD_END_OF_FILE;
511 }
512
513 /* If we wanted to be a legitimate dd, we would do character
514 * conversions and other shenanigans here.
515 */
516
517 /* Flush what we read in units of obs bytes. We want to have
518 * at least obs bytes in the IO buffer but might not if the
519 * file is too small.
520 */
521 if (data_size &&
522 !dd_flush_block(ofile, iobuf, &data_size, obs)) {
523 return(IOERROR_EXIT_CODE);
524 }
525 }
526
527done:
528 print_transfer_stats();
529 return(0);
530}
531
532/* ------------------------------------------------------------------------- */
533/* Main. */
534/* ------------------------------------------------------------------------- */
535
536struct poptOption cifsddHelpOptions[] = {
537 { NULL, '\0', POPT_ARG_CALLBACK, (void *)&cifsdd_help_message, '\0', NULL, NULL },
538 { "help", '?', 0, NULL, '?', "Show this help message", NULL },
539 { "usage", '\0', 0, NULL, 'u', "Display brief usage message", NULL },
540 { NULL }
541} ;
542
543int main(int argc, const char ** argv)
544{
545 int i;
546 const char ** dd_args;
547 struct tevent_context *ev;
548
549 poptContext pctx;
550 struct poptOption poptions[] = {
551 /* POPT_AUTOHELP */
552 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cifsddHelpOptions,
553 0, "Help options:", NULL },
554 POPT_COMMON_SAMBA
555 POPT_COMMON_CONNECTION
556 POPT_COMMON_CREDENTIALS
557 POPT_COMMON_VERSION
558 { NULL }
559 };
560
561 /* Block sizes. */
562 set_arg_val("bs", (uint64_t)4096);
563 set_arg_val("ibs", (uint64_t)4096);
564 set_arg_val("obs", (uint64_t)4096);
565 /* Block counts. */
566 set_arg_val("count", (uint64_t)-1);
567 set_arg_val("seek", (uint64_t)0);
568 set_arg_val("seek", (uint64_t)0);
569 /* Files. */
570 set_arg_val("if", NULL);
571 set_arg_val("of", NULL);
572 /* Options. */
573 set_arg_val("direct", false);
574 set_arg_val("sync", false);
575 set_arg_val("oplock", false);
576
577 pctx = poptGetContext(PROGNAME, argc, argv, poptions, 0);
578 while ((i = poptGetNextOpt(pctx)) != -1) {
579 ;
580 }
581
582 for (dd_args = poptGetArgs(pctx); dd_args && *dd_args; ++dd_args) {
583
584 if (!set_arg_argv(*dd_args)) {
585 fprintf(stderr, "%s: invalid option: %s\n",
586 PROGNAME, *dd_args);
587 exit(SYNTAX_EXIT_CODE);
588 }
589
590 /* "bs" has the side-effect of setting "ibs" and "obs". */
591 if (strncmp(*dd_args, "bs=", 3) == 0) {
592 uint64_t bs = check_arg_numeric("bs");
593 set_arg_val("ibs", bs);
594 set_arg_val("obs", bs);
595 }
596 }
597
598 ev = s4_event_context_init(talloc_autofree_context());
599
600 gensec_init(cmdline_lp_ctx);
601 dump_args();
602
603 if (check_arg_numeric("ibs") == 0 || check_arg_numeric("ibs") == 0) {
604 fprintf(stderr, "%s: block sizes must be greater that zero\n",
605 PROGNAME);
606 exit(SYNTAX_EXIT_CODE);
607 }
608
609 if (check_arg_pathname("if") == NULL) {
610 fprintf(stderr, "%s: missing input filename\n", PROGNAME);
611 exit(SYNTAX_EXIT_CODE);
612 }
613
614 if (check_arg_pathname("of") == NULL) {
615 fprintf(stderr, "%s: missing output filename\n", PROGNAME);
616 exit(SYNTAX_EXIT_CODE);
617 }
618
619 CatchSignal(SIGINT, dd_handle_signal);
620 CatchSignal(SIGUSR1, dd_handle_signal);
621 return(copy_files(ev, cmdline_lp_ctx));
622}
623
624/* vim: set sw=8 sts=8 ts=8 tw=79 : */
Note: See TracBrowser for help on using the repository browser.