source: branches/samba-3.5.x/source4/client/cifsdd.c@ 772

Last change on this file since 772 was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

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