[745] | 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(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 | 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 |
|
---|
| 527 | done:
|
---|
| 528 | print_transfer_stats();
|
---|
| 529 | return(0);
|
---|
| 530 | }
|
---|
| 531 |
|
---|
| 532 | /* ------------------------------------------------------------------------- */
|
---|
| 533 | /* Main. */
|
---|
| 534 | /* ------------------------------------------------------------------------- */
|
---|
| 535 |
|
---|
| 536 | struct 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 |
|
---|
| 543 | int 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 : */
|
---|