Line data Source code
1 : /*
2 : smbget: a wget-like utility with support for recursive downloading of
3 : smb:// urls
4 : Copyright (C) 2003-2004 Jelmer Vernooij <jelmer@samba.org>
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : #include "includes.h"
20 : #include "system/filesys.h"
21 : #include "lib/cmdline/cmdline.h"
22 : #include "lib/param/param.h"
23 : #include "libsmbclient.h"
24 : #include "cmdline_contexts.h"
25 : #include "auth/credentials/credentials.h"
26 : #include "auth/gensec/gensec.h"
27 :
28 : static int columns = 0;
29 :
30 : static time_t total_start_time = 0;
31 : static off_t total_bytes = 0;
32 :
33 : #define SMB_MAXPATHLEN MAXPATHLEN
34 :
35 : /*
36 : * Number of bytes to read when checking whether local and remote file
37 : * are really the same file
38 : */
39 : #define RESUME_CHECK_SIZE 512
40 : #define RESUME_DOWNLOAD_OFFSET 1024
41 : #define RESUME_CHECK_OFFSET (RESUME_DOWNLOAD_OFFSET+RESUME_CHECK_SIZE)
42 : /* Number of bytes to read at once */
43 : #define SMB_DEFAULT_BLOCKSIZE 64000
44 :
45 : struct opt {
46 : char *outputfile;
47 : size_t blocksize;
48 :
49 : int quiet;
50 : int dots;
51 : int verbose;
52 : int send_stdout;
53 : int update;
54 : unsigned limit_rate;
55 : };
56 : static struct opt opt = { .blocksize = SMB_DEFAULT_BLOCKSIZE };
57 :
58 : static bool smb_download_file(const char *base, const char *name,
59 : bool recursive, bool resume, bool toplevel,
60 : char *outfile);
61 :
62 48 : static int get_num_cols(void)
63 : {
64 : #ifdef TIOCGWINSZ
65 : struct winsize ws;
66 48 : if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
67 46 : return 0;
68 : }
69 2 : return ws.ws_col;
70 : #else
71 : #warning No support for TIOCGWINSZ
72 : char *cols = getenv("COLUMNS");
73 : if (!cols) {
74 : return 0;
75 : }
76 : return atoi(cols);
77 : #endif
78 : }
79 :
80 0 : static void change_columns(int sig)
81 : {
82 0 : columns = get_num_cols();
83 0 : }
84 :
85 1246 : static void human_readable(off_t s, char *buffer, int l)
86 : {
87 1246 : if (s > 1024 * 1024 * 1024) {
88 0 : snprintf(buffer, l, "%.2fGB", 1.0 * s / (1024 * 1024 * 1024));
89 1246 : } else if (s > 1024 * 1024) {
90 0 : snprintf(buffer, l, "%.2fMB", 1.0 * s / (1024 * 1024));
91 1246 : } else if (s > 1024) {
92 925 : snprintf(buffer, l, "%.2fkB", 1.0 * s / 1024);
93 : } else {
94 321 : snprintf(buffer, l, "%jdb", (intmax_t)s);
95 : }
96 1246 : }
97 :
98 : /*
99 : * Authentication callback for libsmbclient.
100 : *
101 : * The command line parser will take care asking for a password interactively!
102 : */
103 86 : static void get_auth_data_with_context_fn(SMBCCTX *ctx,
104 : const char *srv,
105 : const char *shr,
106 : char *dom,
107 : int dom_len,
108 : char *usr,
109 : int usr_len,
110 : char *pwd,
111 : int pwd_len)
112 : {
113 86 : struct cli_credentials *creds = samba_cmdline_get_creds();
114 86 : const char *username = NULL;
115 86 : const char *password = NULL;
116 86 : const char *domain = NULL;
117 86 : enum credentials_obtained obtained = CRED_UNINITIALISED;
118 :
119 86 : domain = cli_credentials_get_domain_and_obtained(creds, &obtained);
120 86 : if (domain != NULL) {
121 86 : bool overwrite = false;
122 86 : if (dom[0] == '\0') {
123 0 : overwrite = true;
124 : }
125 86 : if (obtained >= CRED_CALLBACK_RESULT) {
126 82 : overwrite = true;
127 : }
128 86 : if (overwrite) {
129 82 : strncpy(dom, domain, dom_len - 1);
130 : }
131 : }
132 86 : cli_credentials_set_domain(creds, dom, obtained);
133 :
134 86 : username = cli_credentials_get_username_and_obtained(creds, &obtained);
135 86 : if (username != NULL) {
136 86 : bool overwrite = false;
137 86 : if (usr[0] == '\0') {
138 0 : overwrite = true;
139 : }
140 86 : if (obtained >= CRED_CALLBACK_RESULT) {
141 82 : overwrite = true;
142 : }
143 86 : if (overwrite) {
144 82 : strncpy(usr, username, usr_len - 1);
145 : }
146 : }
147 86 : cli_credentials_set_username(creds, usr, obtained);
148 :
149 86 : password = cli_credentials_get_password_and_obtained(creds, &obtained);
150 86 : if (password != NULL) {
151 80 : bool overwrite = false;
152 80 : if (pwd[0] == '\0') {
153 80 : overwrite = true;
154 : }
155 80 : if (obtained >= CRED_CALLBACK_RESULT) {
156 80 : overwrite = true;
157 : }
158 80 : if (overwrite) {
159 80 : strncpy(pwd, password, pwd_len - 1);
160 : }
161 : }
162 86 : cli_credentials_set_password(creds, pwd, obtained);
163 :
164 86 : smbc_set_credentials_with_fallback(ctx, dom, usr, pwd);
165 :
166 86 : if (!opt.quiet) {
167 86 : if (usr[0] == '\0') {
168 4 : printf("Using guest user\n");
169 82 : } else if (dom[0] == '\0') {
170 6 : printf("Using user: %s\n", usr);
171 : } else {
172 76 : printf("Using domain: %s, user: %s\n", dom, usr);
173 : }
174 : }
175 86 : }
176 :
177 30 : static bool smb_download_dir(const char *base, const char *name, int resume)
178 : {
179 : char path[SMB_MAXPATHLEN];
180 : int dirhandle;
181 : struct smbc_dirent *dirent;
182 30 : const char *relname = name;
183 : char *tmpname;
184 30 : bool ok = false;
185 :
186 30 : snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base,
187 30 : (base[0] && name[0] && name[0] != '/' &&
188 0 : base[strlen(base)-1] != '/') ? "/" : "",
189 : name);
190 :
191 : /* List files in directory and call smb_download_file on them */
192 30 : dirhandle = smbc_opendir(path);
193 30 : if (dirhandle < 1) {
194 0 : if (errno == ENOTDIR) {
195 0 : return smb_download_file(base, name, true, resume,
196 : false, NULL);
197 : }
198 0 : fprintf(stderr, "Can't open directory %s: %s\n", path,
199 0 : strerror(errno));
200 0 : return false;
201 : }
202 :
203 54 : while (*relname == '/') {
204 24 : relname++;
205 : }
206 :
207 30 : if (strlen(relname) > 0) {
208 24 : int rc = mkdir(relname, 0755);
209 24 : if (rc == -1 && errno != EEXIST) {
210 0 : fprintf(stderr, "Can't create directory %s: %s\n",
211 0 : relname, strerror(errno));
212 0 : return false;
213 : }
214 : }
215 :
216 30 : tmpname = SMB_STRDUP(name);
217 :
218 132 : while ((dirent = smbc_readdir(dirhandle))) {
219 : char *newname;
220 102 : if (!strcmp(dirent->name, ".") || !strcmp(dirent->name, "..")) {
221 60 : ok = true;
222 60 : continue;
223 : }
224 42 : if (asprintf(&newname, "%s/%s", tmpname, dirent->name) == -1) {
225 0 : free(tmpname);
226 0 : return false;
227 : }
228 42 : switch (dirent->smbc_type) {
229 24 : case SMBC_DIR:
230 24 : ok = smb_download_dir(base, newname, resume);
231 24 : break;
232 :
233 0 : case SMBC_WORKGROUP:
234 0 : ok = smb_download_dir("smb://", dirent->name, resume);
235 0 : break;
236 :
237 0 : case SMBC_SERVER:
238 0 : ok = smb_download_dir("smb://", dirent->name, resume);
239 0 : break;
240 :
241 18 : case SMBC_FILE:
242 18 : ok = smb_download_file(base, newname, true, resume,
243 : false, NULL);
244 18 : break;
245 :
246 0 : case SMBC_FILE_SHARE:
247 0 : ok = smb_download_dir(base, newname, resume);
248 0 : break;
249 :
250 0 : case SMBC_PRINTER_SHARE:
251 0 : if (!opt.quiet) {
252 0 : printf("Ignoring printer share %s\n",
253 0 : dirent->name);
254 : }
255 0 : break;
256 :
257 0 : case SMBC_COMMS_SHARE:
258 0 : if (!opt.quiet) {
259 0 : printf("Ignoring comms share %s\n",
260 0 : dirent->name);
261 : }
262 0 : break;
263 :
264 0 : case SMBC_IPC_SHARE:
265 0 : if (!opt.quiet) {
266 0 : printf("Ignoring ipc$ share %s\n",
267 0 : dirent->name);
268 : }
269 0 : break;
270 :
271 0 : default:
272 0 : fprintf(stderr, "Ignoring file '%s' of type '%d'\n",
273 : newname, dirent->smbc_type);
274 0 : break;
275 : }
276 :
277 42 : if (!ok) {
278 0 : fprintf(stderr, "Failed to download %s: %s\n",
279 0 : newname, strerror(errno));
280 0 : free(newname);
281 0 : free(tmpname);
282 0 : return false;
283 : }
284 42 : free(newname);
285 : }
286 30 : free(tmpname);
287 :
288 30 : smbc_closedir(dirhandle);
289 30 : return ok;
290 : }
291 :
292 400 : static char *print_time(long t)
293 : {
294 : static char buffer[100];
295 : int secs, mins, hours;
296 400 : if (t < -1) {
297 261 : strncpy(buffer, "Unknown", sizeof(buffer));
298 261 : return buffer;
299 : }
300 :
301 139 : secs = (int)t % 60;
302 139 : mins = (int)t / 60 % 60;
303 139 : hours = (int)t / (60 * 60);
304 139 : snprintf(buffer, sizeof(buffer) - 1, "%02d:%02d:%02d", hours, mins,
305 : secs);
306 139 : return buffer;
307 : }
308 :
309 400 : static void print_progress(const char *name, time_t start, time_t now,
310 : off_t start_pos, off_t pos, off_t total)
311 : {
312 400 : double avg = 0.0;
313 400 : long eta = -1;
314 400 : double prcnt = 0.0;
315 : char hpos[22], htotal[22], havg[22];
316 : char *status, *filename;
317 : int len;
318 400 : if (now - start) {
319 141 : avg = 1.0 * (pos - start_pos) / (now - start);
320 : }
321 400 : eta = (total - pos) / avg;
322 400 : if (total) {
323 400 : prcnt = 100.0 * pos / total;
324 : }
325 :
326 400 : human_readable(pos, hpos, sizeof(hpos));
327 400 : human_readable(total, htotal, sizeof(htotal));
328 400 : human_readable(avg, havg, sizeof(havg));
329 :
330 400 : len = asprintf(&status, "%s of %s (%.2f%%) at %s/s ETA: %s", hpos,
331 : htotal, prcnt, havg, print_time(eta));
332 400 : if (len == -1) {
333 0 : return;
334 : }
335 :
336 400 : if (columns) {
337 0 : int required = strlen(name),
338 0 : available = columns - len - strlen("[] ");
339 0 : if (required > available) {
340 0 : if (asprintf(&filename, "...%s",
341 0 : name + required - available + 3) == -1) {
342 0 : return;
343 : }
344 : } else {
345 0 : filename = SMB_STRNDUP(name, available);
346 : }
347 : } else {
348 400 : filename = SMB_STRDUP(name);
349 : }
350 :
351 400 : fprintf(stderr, "\r[%s] %s", filename, status);
352 :
353 400 : free(filename);
354 400 : free(status);
355 : }
356 :
357 : /* Return false on error, true on success. */
358 :
359 60 : static bool smb_download_file(const char *base, const char *name,
360 : bool recursive, bool resume, bool toplevel,
361 : char *outfile)
362 : {
363 : int remotehandle, localhandle;
364 60 : time_t start_time = time_mono(NULL);
365 : const char *newpath;
366 : char path[SMB_MAXPATHLEN];
367 : char checkbuf[2][RESUME_CHECK_SIZE];
368 60 : char *readbuf = NULL;
369 60 : off_t offset_download = 0, offset_check = 0, curpos = 0,
370 60 : start_offset = 0;
371 : struct stat localstat, remotestat;
372 60 : clock_t start_of_bucket_ticks = 0;
373 60 : size_t bytes_in_bucket = 0;
374 60 : size_t bucket_size = 0;
375 60 : clock_t ticks_to_fill_bucket = 0;
376 :
377 60 : snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base,
378 60 : (*base && *name && name[0] != '/' &&
379 0 : base[strlen(base)-1] != '/') ? "/" : "",
380 : name);
381 :
382 60 : remotehandle = smbc_open(path, O_RDONLY, 0755);
383 :
384 60 : if (remotehandle < 0) {
385 0 : switch (errno) {
386 0 : case EISDIR:
387 0 : if (!recursive) {
388 0 : fprintf(stderr,
389 : "%s is a directory. Specify -R "
390 : "to download recursively\n",
391 : path);
392 0 : return false;
393 : }
394 0 : return smb_download_dir(base, name, resume);
395 :
396 0 : case ENOENT:
397 0 : fprintf(stderr,
398 : "%s can't be found on the remote server\n",
399 : path);
400 0 : return false;
401 :
402 0 : case ENOMEM:
403 0 : fprintf(stderr, "Not enough memory\n");
404 0 : return false;
405 :
406 0 : case ENODEV:
407 0 : fprintf(stderr,
408 : "The share name used in %s does not exist\n",
409 : path);
410 0 : return false;
411 :
412 0 : case EACCES:
413 0 : fprintf(stderr, "You don't have enough permissions "
414 : "to access %s\n",
415 : path);
416 0 : return false;
417 :
418 0 : default:
419 0 : perror("smbc_open");
420 0 : return false;
421 : }
422 : }
423 :
424 60 : if (smbc_fstat(remotehandle, &remotestat) < 0) {
425 0 : fprintf(stderr, "Can't stat %s: %s\n", path, strerror(errno));
426 0 : return false;
427 : }
428 :
429 60 : if (outfile) {
430 0 : newpath = outfile;
431 60 : } else if (!name[0]) {
432 42 : newpath = strrchr(base, '/');
433 42 : if (newpath) {
434 42 : newpath++;
435 : } else {
436 0 : newpath = base;
437 : }
438 : } else {
439 18 : newpath = name;
440 : }
441 :
442 60 : if (!toplevel && (newpath[0] == '/')) {
443 18 : newpath++;
444 : }
445 :
446 : /* Open local file according to the mode */
447 60 : if (opt.update) {
448 : /* if it is up-to-date, skip */
449 4 : if (stat(newpath, &localstat) == 0 &&
450 4 : localstat.st_mtime >= remotestat.st_mtime) {
451 2 : if (opt.verbose) {
452 2 : printf("%s is up-to-date, skipping\n", newpath);
453 : }
454 2 : smbc_close(remotehandle);
455 2 : return true;
456 : }
457 : /* else open it for writing and truncate if it exists */
458 2 : localhandle = open(
459 : newpath, O_CREAT | O_NONBLOCK | O_RDWR | O_TRUNC, 0775);
460 2 : if (localhandle < 0) {
461 0 : fprintf(stderr, "Can't open %s : %s\n", newpath,
462 0 : strerror(errno));
463 0 : smbc_close(remotehandle);
464 0 : return false;
465 : }
466 : /* no offset */
467 56 : } else if (!opt.send_stdout) {
468 56 : localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR |
469 56 : (!resume ? O_EXCL : 0),
470 : 0755);
471 56 : if (localhandle < 0) {
472 0 : fprintf(stderr, "Can't open %s: %s\n", newpath,
473 0 : strerror(errno));
474 0 : smbc_close(remotehandle);
475 0 : return false;
476 : }
477 :
478 56 : if (fstat(localhandle, &localstat) != 0) {
479 0 : fprintf(stderr, "Can't fstat %s: %s\n", newpath,
480 0 : strerror(errno));
481 0 : smbc_close(remotehandle);
482 0 : close(localhandle);
483 0 : return false;
484 : }
485 :
486 56 : start_offset = localstat.st_size;
487 :
488 56 : if (localstat.st_size &&
489 4 : localstat.st_size == remotestat.st_size) {
490 0 : if (opt.verbose) {
491 0 : fprintf(stderr, "%s is already downloaded "
492 : "completely.\n",
493 : path);
494 0 : } else if (!opt.quiet) {
495 0 : fprintf(stderr, "%s\n", path);
496 : }
497 0 : smbc_close(remotehandle);
498 0 : close(localhandle);
499 0 : return true;
500 : }
501 :
502 56 : if (localstat.st_size > RESUME_CHECK_OFFSET &&
503 2 : remotestat.st_size > RESUME_CHECK_OFFSET) {
504 2 : offset_download =
505 2 : localstat.st_size - RESUME_DOWNLOAD_OFFSET;
506 2 : offset_check = localstat.st_size - RESUME_CHECK_OFFSET;
507 2 : if (opt.verbose) {
508 2 : printf("Trying to start resume of %s at %jd\n"
509 : "At the moment %jd of %jd bytes have "
510 : "been retrieved\n",
511 : newpath, (intmax_t)offset_check,
512 2 : (intmax_t)localstat.st_size,
513 2 : (intmax_t)remotestat.st_size);
514 : }
515 : }
516 :
517 56 : if (offset_check) {
518 : off_t off1, off2;
519 : /* First, check all bytes from offset_check to
520 : * offset_download */
521 2 : off1 = lseek(localhandle, offset_check, SEEK_SET);
522 2 : if (off1 < 0) {
523 0 : fprintf(stderr,
524 : "Can't seek to %jd in local file %s\n",
525 : (intmax_t)offset_check, newpath);
526 0 : smbc_close(remotehandle);
527 0 : close(localhandle);
528 0 : return false;
529 : }
530 :
531 2 : off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET);
532 2 : if (off2 < 0) {
533 0 : fprintf(stderr,
534 : "Can't seek to %jd in remote file %s\n",
535 : (intmax_t)offset_check, newpath);
536 0 : smbc_close(remotehandle);
537 0 : close(localhandle);
538 0 : return false;
539 : }
540 :
541 2 : if (off1 != off2) {
542 0 : fprintf(stderr, "Offset in local and remote "
543 : "files are different "
544 : "(local: %jd, remote: %jd)\n",
545 : (intmax_t)off1, (intmax_t)off2);
546 0 : smbc_close(remotehandle);
547 0 : close(localhandle);
548 0 : return false;
549 : }
550 :
551 2 : if (smbc_read(remotehandle, checkbuf[0],
552 : RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
553 0 : fprintf(stderr, "Can't read %d bytes from "
554 : "remote file %s\n",
555 : RESUME_CHECK_SIZE, path);
556 0 : smbc_close(remotehandle);
557 0 : close(localhandle);
558 0 : return false;
559 : }
560 :
561 2 : if (read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) !=
562 : RESUME_CHECK_SIZE) {
563 0 : fprintf(stderr, "Can't read %d bytes from "
564 : "local file %s\n",
565 : RESUME_CHECK_SIZE, name);
566 0 : smbc_close(remotehandle);
567 0 : close(localhandle);
568 0 : return false;
569 : }
570 :
571 2 : if (memcmp(checkbuf[0], checkbuf[1],
572 : RESUME_CHECK_SIZE) == 0) {
573 0 : if (opt.verbose) {
574 0 : printf("Current local and remote file "
575 : "appear to be the same. "
576 : "Starting download from "
577 : "offset %jd\n",
578 : (intmax_t)offset_download);
579 : }
580 : } else {
581 2 : fprintf(stderr, "Local and remote file appear "
582 : "to be different, not "
583 : "doing resume for %s\n",
584 : path);
585 2 : smbc_close(remotehandle);
586 2 : close(localhandle);
587 2 : return false;
588 : }
589 : }
590 : } else {
591 0 : localhandle = STDOUT_FILENO;
592 0 : start_offset = 0;
593 0 : offset_download = 0;
594 0 : offset_check = 0;
595 : }
596 :
597 : /* We implement rate limiting by filling up a bucket with bytes and
598 : * checking, once the bucket is filled, if it was filled too fast.
599 : * If so, we sleep for some time to get an average transfer rate that
600 : * equals to the one set by the user.
601 : *
602 : * The bucket size directly affects the traffic characteristics.
603 : * The smaller the bucket the more frequent the pause/resume cycle.
604 : * A large bucket can result in burst of high speed traffic and large
605 : * pauses. A cycle of 100ms looks like a good value. This value (in
606 : * ticks) is held in `ticks_to_fill_bucket`. The `bucket_size` is
607 : * calculated as:
608 : * `limit_rate * 1024 * / (CLOCKS_PER_SEC / ticks_to_fill_bucket)`
609 : *
610 : * After selecting the bucket size we also need to check the blocksize
611 : * of the transfer, since this is the minimum unit of traffic that we
612 : * can observe. Achieving a ~10% precision requires a blocksize with a
613 : * maximum size of `bucket_size / 10`.
614 : */
615 56 : if (opt.limit_rate > 0) {
616 : unsigned max_block_size;
617 : /* This is the time that the bucket should take to fill. */
618 2 : ticks_to_fill_bucket = 100 /*ms*/ * CLOCKS_PER_SEC / 1000;
619 : /* This is the size of the bucket in bytes.
620 : * If we fill the bucket too quickly we should pause */
621 2 : bucket_size = opt.limit_rate * 1024 / (CLOCKS_PER_SEC / ticks_to_fill_bucket);
622 2 : max_block_size = bucket_size / 10;
623 2 : max_block_size = max_block_size > 0 ? max_block_size : 1;
624 2 : if (opt.blocksize > max_block_size) {
625 2 : if (opt.blocksize != SMB_DEFAULT_BLOCKSIZE) {
626 0 : fprintf(stderr,
627 : "Warning: Overriding block size to %d "
628 : "due to limit-rate", max_block_size);
629 : }
630 2 : opt.blocksize = max_block_size;
631 : }
632 2 : start_of_bucket_ticks = clock();
633 : }
634 :
635 56 : readbuf = (char *)SMB_MALLOC(opt.blocksize);
636 56 : if (!readbuf) {
637 0 : fprintf(stderr, "Failed to allocate %zu bytes for read "
638 0 : "buffer (%s)", opt.blocksize, strerror(errno));
639 0 : if (localhandle != STDOUT_FILENO) {
640 0 : close(localhandle);
641 : }
642 0 : return false;
643 : }
644 :
645 : /* Now, download all bytes from offset_download to the end */
646 456 : for (curpos = offset_download; curpos < remotestat.st_size;
647 400 : curpos += opt.blocksize) {
648 : ssize_t bytesread;
649 : ssize_t byteswritten;
650 :
651 : /* Rate limiting. This pauses the transfer to limit traffic. */
652 400 : if (opt.limit_rate > 0) {
653 256 : if (bytes_in_bucket > bucket_size) {
654 22 : clock_t now_ticks = clock();
655 22 : clock_t diff_ticks = now_ticks
656 : - start_of_bucket_ticks;
657 : /* Check if the bucket filled up too fast. */
658 22 : if (diff_ticks < ticks_to_fill_bucket) {
659 : /* Pause until `ticks_to_fill_bucket` */
660 22 : double sleep_us
661 22 : = (ticks_to_fill_bucket - diff_ticks)
662 22 : * 1000000.0 / CLOCKS_PER_SEC;
663 22 : usleep(sleep_us);
664 : }
665 : /* Reset the byte counter and the ticks. */
666 22 : bytes_in_bucket = 0;
667 22 : start_of_bucket_ticks = clock();
668 : }
669 : }
670 :
671 400 : bytesread = smbc_read(remotehandle, readbuf, opt.blocksize);
672 400 : if (opt.limit_rate > 0) {
673 256 : bytes_in_bucket += bytesread;
674 : }
675 400 : if(bytesread < 0) {
676 0 : fprintf(stderr,
677 : "Can't read %zu bytes at offset %jd, file %s\n",
678 : opt.blocksize, (intmax_t)curpos, path);
679 0 : smbc_close(remotehandle);
680 0 : if (localhandle != STDOUT_FILENO) {
681 0 : close(localhandle);
682 : }
683 0 : free(readbuf);
684 0 : return false;
685 : }
686 :
687 400 : total_bytes += bytesread;
688 :
689 400 : byteswritten = write(localhandle, readbuf, bytesread);
690 400 : if (byteswritten != bytesread) {
691 0 : fprintf(stderr,
692 : "Can't write %zd bytes to local file %s at "
693 : "offset %jd\n", bytesread, path,
694 : (intmax_t)curpos);
695 0 : free(readbuf);
696 0 : smbc_close(remotehandle);
697 0 : if (localhandle != STDOUT_FILENO) {
698 0 : close(localhandle);
699 : }
700 0 : return false;
701 : }
702 :
703 400 : if (opt.dots) {
704 0 : fputc('.', stderr);
705 400 : } else if (!opt.quiet) {
706 400 : print_progress(newpath, start_time, time_mono(NULL),
707 : start_offset, curpos,
708 : remotestat.st_size);
709 : }
710 : }
711 :
712 56 : free(readbuf);
713 :
714 56 : if (opt.dots) {
715 0 : fputc('\n', stderr);
716 0 : printf("%s downloaded\n", path);
717 56 : } else if (!opt.quiet) {
718 : int i;
719 56 : fprintf(stderr, "\r%s", path);
720 56 : if (columns) {
721 0 : for (i = strlen(path); i < columns; i++) {
722 0 : fputc(' ', stderr);
723 : }
724 : }
725 56 : fputc('\n', stderr);
726 : }
727 :
728 56 : smbc_close(remotehandle);
729 56 : if (localhandle != STDOUT_FILENO) {
730 56 : close(localhandle);
731 : }
732 56 : return true;
733 : }
734 :
735 46 : static void clean_exit(void)
736 : {
737 : char bs[100];
738 46 : human_readable(total_bytes, bs, sizeof(bs));
739 46 : if (!opt.quiet) {
740 46 : fprintf(stderr, "Downloaded %s in %lu seconds\n", bs,
741 46 : (unsigned long)(time_mono(NULL) - total_start_time));
742 : }
743 46 : exit(0);
744 : }
745 :
746 0 : static void signal_quit(int v)
747 : {
748 0 : clean_exit();
749 0 : }
750 :
751 48 : int main(int argc, char **argv)
752 : {
753 48 : int c = 0;
754 48 : const char *file = NULL;
755 48 : int smb_encrypt = false;
756 48 : int resume = 0, recursive = 0;
757 48 : TALLOC_CTX *frame = talloc_stackframe();
758 48 : bool ok = false;
759 48 : const char **argv_const = discard_const_p(const char *, argv);
760 288 : struct poptOption long_options[] = {
761 : POPT_AUTOHELP
762 :
763 : {
764 : .longName = "guest",
765 : .shortName = 'a',
766 : .argInfo = POPT_ARG_NONE,
767 : .arg = NULL,
768 : .val = 'a',
769 : .descrip = "Work as user guest"
770 : },
771 : {
772 : .longName = "encrypt",
773 : .shortName = 'e',
774 : .argInfo = POPT_ARG_NONE,
775 : .arg = &smb_encrypt,
776 : .val = 1,
777 : .descrip = "Encrypt SMB transport"
778 : },
779 : {
780 : .longName = "resume",
781 : .shortName = 'r',
782 : .argInfo = POPT_ARG_NONE,
783 : .arg = &resume,
784 : .val = 1,
785 : .descrip = "Automatically resume aborted files"
786 : },
787 : {
788 : .longName = "update",
789 : .shortName = 'u',
790 : .argInfo = POPT_ARG_NONE,
791 : .arg = &opt.update,
792 : .val = 1,
793 : .descrip = "Download only when remote file is "
794 : "newer than local file or local file "
795 : "is missing"
796 : },
797 : {
798 : .longName = "recursive",
799 : .shortName = 0,
800 : .argInfo = POPT_ARG_NONE,
801 : .arg = &recursive,
802 : .val = true,
803 : .descrip = "Recursively download files"
804 : },
805 : {
806 : .longName = "blocksize",
807 : .shortName = 'b',
808 : .argInfo = POPT_ARG_INT,
809 : .arg = &opt.blocksize,
810 : .val = 'b',
811 : .descrip = "Change number of bytes in a block"
812 : },
813 :
814 : {
815 : .longName = "outputfile",
816 : .shortName = 'o',
817 : .argInfo = POPT_ARG_STRING,
818 : .arg = &opt.outputfile,
819 : .val = 'o',
820 : .descrip = "Write downloaded data to specified file"
821 : },
822 : {
823 : .longName = "stdout",
824 : .shortName = 0,
825 : .argInfo = POPT_ARG_NONE,
826 : .arg = &opt.send_stdout,
827 : .val = true,
828 : .descrip = "Write data to stdout"
829 : },
830 : {
831 : .longName = "dots",
832 : .shortName = 'D',
833 : .argInfo = POPT_ARG_NONE,
834 : .arg = &opt.dots,
835 : .val = 1,
836 : .descrip = "Show dots as progress indication"
837 : },
838 : {
839 : .longName = "quiet",
840 : .shortName = 'q',
841 : .argInfo = POPT_ARG_NONE,
842 : .arg = &opt.quiet,
843 : .val = 1,
844 : .descrip = "Be quiet"
845 : },
846 : {
847 : .longName = "verbose",
848 : .shortName = 'v',
849 : .argInfo = POPT_ARG_NONE,
850 : .arg = &opt.verbose,
851 : .val = 1,
852 : .descrip = "Be verbose"
853 : },
854 : {
855 : .longName = "limit-rate",
856 : .shortName = 0,
857 : .argInfo = POPT_ARG_INT,
858 : .arg = &opt.limit_rate,
859 : .val = 'l',
860 : .descrip = "Limit download speed to this many KB/s"
861 : },
862 :
863 48 : POPT_COMMON_SAMBA
864 48 : POPT_COMMON_CONNECTION
865 48 : POPT_COMMON_CREDENTIALS
866 48 : POPT_LEGACY_S3
867 48 : POPT_COMMON_VERSION
868 : POPT_TABLEEND
869 : };
870 48 : poptContext pc = NULL;
871 48 : struct cli_credentials *creds = NULL;
872 48 : enum smb_encryption_setting encryption_state = SMB_ENCRYPTION_DEFAULT;
873 48 : enum credentials_use_kerberos use_kerberos = CRED_USE_KERBEROS_DESIRED;
874 48 : smbc_smb_encrypt_level encrypt_level = SMBC_ENCRYPTLEVEL_DEFAULT;
875 : #if 0
876 : enum smb_signing_setting signing_state = SMB_SIGNING_DEFAULT;
877 : const char *use_signing = "auto";
878 : #endif
879 48 : bool is_nt_hash = false;
880 : uint32_t gensec_features;
881 48 : bool use_wbccache = false;
882 48 : SMBCCTX *smb_ctx = NULL;
883 48 : int dbg_lvl = -1;
884 : int rc;
885 :
886 48 : smb_init_locale();
887 :
888 48 : ok = samba_cmdline_init(frame,
889 : SAMBA_CMDLINE_CONFIG_CLIENT,
890 : false);
891 48 : if (!ok) {
892 0 : goto done;
893 : }
894 :
895 : #ifdef SIGWINCH
896 48 : signal(SIGWINCH, change_columns);
897 : #endif
898 48 : signal(SIGINT, signal_quit);
899 48 : signal(SIGTERM, signal_quit);
900 :
901 48 : pc = samba_popt_get_context(getprogname(),
902 : argc,
903 : argv_const,
904 : long_options,
905 : 0);
906 48 : if (pc == NULL) {
907 0 : ok = false;
908 0 : goto done;
909 : }
910 :
911 48 : creds = samba_cmdline_get_creds();
912 :
913 112 : while ((c = poptGetNextOpt(pc)) != -1) {
914 64 : switch (c) {
915 4 : case 'a':
916 4 : cli_credentials_set_anonymous(creds);
917 4 : break;
918 0 : case POPT_ERROR_BADOPT:
919 0 : fprintf(stderr, "\nInvalid option %s: %s\n\n",
920 : poptBadOption(pc, 0), poptStrerror(c));
921 0 : poptPrintUsage(pc, stderr, 0);
922 0 : ok = false;
923 0 : goto done;
924 : }
925 :
926 64 : if (c < -1) {
927 0 : fprintf(stderr, "%s: %s\n",
928 : poptBadOption(pc, POPT_BADOPTION_NOALIAS),
929 : poptStrerror(c));
930 0 : ok = false;
931 0 : goto done;
932 : }
933 : }
934 :
935 48 : if ((opt.send_stdout || resume || opt.outputfile) && opt.update) {
936 0 : fprintf(stderr, "The -o, -R or -O and -U options can not be "
937 : "used together.\n");
938 0 : ok = true;
939 0 : goto done;
940 : }
941 48 : if ((opt.send_stdout || opt.outputfile) && recursive) {
942 0 : fprintf(stderr, "The -o or -O and -R options can not be "
943 : "used together.\n");
944 0 : ok = true;
945 0 : goto done;
946 : }
947 :
948 48 : if (opt.outputfile && opt.send_stdout) {
949 0 : fprintf(stderr, "The -o and -O options can not be "
950 : "used together.\n");
951 0 : ok = true;
952 0 : goto done;
953 : }
954 :
955 48 : samba_cmdline_burn(argc, argv);
956 :
957 : /* smbc_new_context() will set the log level to 0 */
958 48 : dbg_lvl = debuglevel_get();
959 :
960 48 : smb_ctx = smbc_new_context();
961 48 : if (smb_ctx == NULL) {
962 0 : fprintf(stderr, "Unable to initialize libsmbclient\n");
963 0 : ok = false;
964 0 : goto done;
965 : }
966 48 : smbc_setDebug(smb_ctx, dbg_lvl);
967 :
968 48 : rc = smbc_setConfiguration(smb_ctx, lp_default_path());
969 48 : if (rc < 0) {
970 0 : ok = false;
971 0 : goto done;
972 : }
973 :
974 48 : smbc_setFunctionAuthDataWithContext(smb_ctx,
975 : get_auth_data_with_context_fn);
976 :
977 48 : ok = smbc_init_context(smb_ctx);
978 48 : if (!ok) {
979 0 : goto done;
980 : }
981 48 : smbc_set_context(smb_ctx);
982 :
983 48 : encryption_state = cli_credentials_get_smb_encryption(creds);
984 48 : switch (encryption_state) {
985 2 : case SMB_ENCRYPTION_REQUIRED:
986 2 : encrypt_level = SMBC_ENCRYPTLEVEL_REQUIRE;
987 2 : break;
988 0 : case SMB_ENCRYPTION_DESIRED:
989 : case SMB_ENCRYPTION_IF_REQUIRED:
990 0 : encrypt_level = SMBC_ENCRYPTLEVEL_REQUEST;
991 0 : break;
992 0 : case SMB_ENCRYPTION_OFF:
993 0 : encrypt_level = SMBC_ENCRYPTLEVEL_NONE;
994 0 : break;
995 46 : case SMB_ENCRYPTION_DEFAULT:
996 46 : encrypt_level = SMBC_ENCRYPTLEVEL_DEFAULT;
997 46 : break;
998 : }
999 48 : if (smb_encrypt) {
1000 2 : encrypt_level = SMBC_ENCRYPTLEVEL_REQUIRE;
1001 : }
1002 48 : smbc_setOptionSmbEncryptionLevel(smb_ctx, encrypt_level);
1003 :
1004 : #if 0
1005 : signing_state = cli_credentials_get_smb_signing(creds);
1006 : if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
1007 : signing_state = SMB_SIGNING_REQUIRED;
1008 : }
1009 : switch (signing_state) {
1010 : case SMB_SIGNING_REQUIRED:
1011 : use_signing = "required";
1012 : break;
1013 : case SMB_SIGNING_DEFAULT:
1014 : case SMB_SIGNING_DESIRED:
1015 : case SMB_SIGNING_IF_REQUIRED:
1016 : use_signing = "yes";
1017 : break;
1018 : case SMB_SIGNING_OFF:
1019 : use_signing = "off";
1020 : break;
1021 : default:
1022 : use_signing = "auto";
1023 : break;
1024 : }
1025 : /* FIXME: There is no libsmbclient function to set signing state */
1026 : #endif
1027 :
1028 48 : use_kerberos = cli_credentials_get_kerberos_state(creds);
1029 48 : switch (use_kerberos) {
1030 4 : case CRED_USE_KERBEROS_REQUIRED:
1031 4 : smbc_setOptionUseKerberos(smb_ctx, true);
1032 4 : smbc_setOptionFallbackAfterKerberos(smb_ctx, false);
1033 4 : break;
1034 40 : case CRED_USE_KERBEROS_DESIRED:
1035 40 : smbc_setOptionUseKerberos(smb_ctx, true);
1036 40 : smbc_setOptionFallbackAfterKerberos(smb_ctx, true);
1037 40 : break;
1038 4 : case CRED_USE_KERBEROS_DISABLED:
1039 4 : smbc_setOptionUseKerberos(smb_ctx, false);
1040 4 : break;
1041 : }
1042 :
1043 : /* Check if the password supplied is an NT hash */
1044 48 : is_nt_hash = cli_credentials_is_password_nt_hash(creds);
1045 48 : smbc_setOptionUseNTHash(smb_ctx, is_nt_hash);
1046 :
1047 : /* Check if we should use the winbind ccache */
1048 48 : gensec_features = cli_credentials_get_gensec_features(creds);
1049 48 : use_wbccache = (gensec_features & GENSEC_FEATURE_NTLM_CCACHE);
1050 48 : smbc_setOptionUseCCache(smb_ctx, use_wbccache);
1051 :
1052 48 : columns = get_num_cols();
1053 :
1054 48 : total_start_time = time_mono(NULL);
1055 :
1056 96 : while ((file = poptGetArg(pc))) {
1057 48 : if (!recursive) {
1058 42 : ok = smb_download_file(file, "", recursive, resume,
1059 : true, opt.outputfile);
1060 : } else {
1061 6 : ok = smb_download_dir(file, "", resume);
1062 : }
1063 : }
1064 :
1065 48 : done:
1066 48 : gfree_all();
1067 48 : poptFreeContext(pc);
1068 48 : TALLOC_FREE(frame);
1069 48 : if (ok) {
1070 46 : clean_exit();
1071 : }
1072 2 : return ok ? 0 : 1;
1073 : }
|