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 "popt_common.h"
|
---|
22 | #include "libsmbclient.h"
|
---|
23 |
|
---|
24 | static int columns = 0;
|
---|
25 |
|
---|
26 | static time_t total_start_time = 0;
|
---|
27 | static off_t total_bytes = 0;
|
---|
28 |
|
---|
29 | #define SMB_MAXPATHLEN MAXPATHLEN
|
---|
30 |
|
---|
31 | /*
|
---|
32 | * Number of bytes to read when checking whether local and remote file
|
---|
33 | * are really the same file
|
---|
34 | */
|
---|
35 | #define RESUME_CHECK_SIZE 512
|
---|
36 | #define RESUME_DOWNLOAD_OFFSET 1024
|
---|
37 | #define RESUME_CHECK_OFFSET (RESUME_DOWNLOAD_OFFSET+RESUME_CHECK_SIZE)
|
---|
38 | /* Number of bytes to read at once */
|
---|
39 | #define SMB_DEFAULT_BLOCKSIZE 64000
|
---|
40 |
|
---|
41 | struct opt {
|
---|
42 | char *workgroup;
|
---|
43 | bool username_specified;
|
---|
44 | char *username;
|
---|
45 | bool password_specified;
|
---|
46 | char *password;
|
---|
47 |
|
---|
48 | char *outputfile;
|
---|
49 | size_t blocksize;
|
---|
50 |
|
---|
51 | bool nonprompt;
|
---|
52 | bool quiet;
|
---|
53 | bool dots;
|
---|
54 | bool verbose;
|
---|
55 | bool send_stdout;
|
---|
56 | bool update;
|
---|
57 | int debuglevel;
|
---|
58 | };
|
---|
59 | static struct opt opt = { .blocksize = SMB_DEFAULT_BLOCKSIZE };
|
---|
60 |
|
---|
61 | static bool smb_download_file(const char *base, const char *name,
|
---|
62 | bool recursive, bool resume, bool toplevel,
|
---|
63 | char *outfile);
|
---|
64 |
|
---|
65 | static int get_num_cols(void)
|
---|
66 | {
|
---|
67 | #ifdef TIOCGWINSZ
|
---|
68 | struct winsize ws;
|
---|
69 | if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
|
---|
70 | return 0;
|
---|
71 | }
|
---|
72 | return ws.ws_col;
|
---|
73 | #else
|
---|
74 | #warning No support for TIOCGWINSZ
|
---|
75 | char *cols = getenv("COLUMNS");
|
---|
76 | if (!cols) {
|
---|
77 | return 0;
|
---|
78 | }
|
---|
79 | return atoi(cols);
|
---|
80 | #endif
|
---|
81 | }
|
---|
82 |
|
---|
83 | static void change_columns(int sig)
|
---|
84 | {
|
---|
85 | columns = get_num_cols();
|
---|
86 | }
|
---|
87 |
|
---|
88 | static void human_readable(off_t s, char *buffer, int l)
|
---|
89 | {
|
---|
90 | if (s > 1024 * 1024 * 1024) {
|
---|
91 | snprintf(buffer, l, "%.2fGB", 1.0 * s / (1024 * 1024 * 1024));
|
---|
92 | } else if (s > 1024 * 1024) {
|
---|
93 | snprintf(buffer, l, "%.2fMB", 1.0 * s / (1024 * 1024));
|
---|
94 | } else if (s > 1024) {
|
---|
95 | snprintf(buffer, l, "%.2fkB", 1.0 * s / 1024);
|
---|
96 | } else {
|
---|
97 | snprintf(buffer, l, "%jdb", (intmax_t)s);
|
---|
98 | }
|
---|
99 | }
|
---|
100 |
|
---|
101 | static void get_auth_data(const char *srv, const char *shr, char *wg, int wglen,
|
---|
102 | char *un, int unlen, char *pw, int pwlen)
|
---|
103 | {
|
---|
104 | static bool hasasked = false;
|
---|
105 | static char *savedwg;
|
---|
106 | static char *savedun;
|
---|
107 | static char *savedpw;
|
---|
108 |
|
---|
109 | if (hasasked) {
|
---|
110 | strncpy(wg, savedwg, wglen - 1);
|
---|
111 | strncpy(un, savedun, unlen - 1);
|
---|
112 | strncpy(pw, savedpw, pwlen - 1);
|
---|
113 | return;
|
---|
114 | }
|
---|
115 | hasasked = true;
|
---|
116 |
|
---|
117 | /*
|
---|
118 | * If no user has been specified un is initialized with the current
|
---|
119 | * username of the user who started smbget.
|
---|
120 | */
|
---|
121 | if (opt.username_specified) {
|
---|
122 | strncpy(un, opt.username, unlen - 1);
|
---|
123 | }
|
---|
124 |
|
---|
125 | if (!opt.nonprompt && !opt.password_specified && pw[0] == '\0') {
|
---|
126 | char *prompt;
|
---|
127 | int rc;
|
---|
128 |
|
---|
129 | rc = asprintf(&prompt,
|
---|
130 | "Password for [%s] connecting to //%s/%s: ",
|
---|
131 | un, shr, srv);
|
---|
132 | if (rc == -1) {
|
---|
133 | return;
|
---|
134 | }
|
---|
135 | (void)samba_getpass(prompt, pw, pwlen, false, false);
|
---|
136 | free(prompt);
|
---|
137 | } else if (opt.password != NULL) {
|
---|
138 | strncpy(pw, opt.password, pwlen-1);
|
---|
139 | }
|
---|
140 |
|
---|
141 | if (opt.workgroup != NULL) {
|
---|
142 | strncpy(wg, opt.workgroup, wglen-1);
|
---|
143 | }
|
---|
144 |
|
---|
145 | /* save the values found for later */
|
---|
146 | savedwg = SMB_STRDUP(wg);
|
---|
147 | savedun = SMB_STRDUP(un);
|
---|
148 | savedpw = SMB_STRDUP(pw);
|
---|
149 |
|
---|
150 | if (!opt.quiet) {
|
---|
151 | char *wgtmp, *usertmp;
|
---|
152 | wgtmp = SMB_STRNDUP(wg, wglen);
|
---|
153 | usertmp = SMB_STRNDUP(un, unlen);
|
---|
154 | printf("Using workgroup %s, %s%s\n",
|
---|
155 | wgtmp,
|
---|
156 | *usertmp ? "user " : "guest user",
|
---|
157 | usertmp);
|
---|
158 | free(wgtmp);
|
---|
159 | free(usertmp);
|
---|
160 | }
|
---|
161 | }
|
---|
162 |
|
---|
163 | static bool smb_download_dir(const char *base, const char *name, int resume)
|
---|
164 | {
|
---|
165 | char path[SMB_MAXPATHLEN];
|
---|
166 | int dirhandle;
|
---|
167 | struct smbc_dirent *dirent;
|
---|
168 | const char *relname = name;
|
---|
169 | char *tmpname;
|
---|
170 | bool ok = false;
|
---|
171 |
|
---|
172 | snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base,
|
---|
173 | (base[0] && name[0] && name[0] != '/' &&
|
---|
174 | base[strlen(base)-1] != '/') ? "/" : "",
|
---|
175 | name);
|
---|
176 |
|
---|
177 | /* List files in directory and call smb_download_file on them */
|
---|
178 | dirhandle = smbc_opendir(path);
|
---|
179 | if (dirhandle < 1) {
|
---|
180 | if (errno == ENOTDIR) {
|
---|
181 | return smb_download_file(base, name, true, resume,
|
---|
182 | false, NULL);
|
---|
183 | }
|
---|
184 | fprintf(stderr, "Can't open directory %s: %s\n", path,
|
---|
185 | strerror(errno));
|
---|
186 | return false;
|
---|
187 | }
|
---|
188 |
|
---|
189 | while (*relname == '/') {
|
---|
190 | relname++;
|
---|
191 | }
|
---|
192 | mkdir(relname, 0755);
|
---|
193 |
|
---|
194 | tmpname = SMB_STRDUP(name);
|
---|
195 |
|
---|
196 | while ((dirent = smbc_readdir(dirhandle))) {
|
---|
197 | char *newname;
|
---|
198 | if (!strcmp(dirent->name, ".") || !strcmp(dirent->name, "..")) {
|
---|
199 | continue;
|
---|
200 | }
|
---|
201 | if (asprintf(&newname, "%s/%s", tmpname, dirent->name) == -1) {
|
---|
202 | free(tmpname);
|
---|
203 | return false;
|
---|
204 | }
|
---|
205 | switch (dirent->smbc_type) {
|
---|
206 | case SMBC_DIR:
|
---|
207 | ok = smb_download_dir(base, newname, resume);
|
---|
208 | break;
|
---|
209 |
|
---|
210 | case SMBC_WORKGROUP:
|
---|
211 | ok = smb_download_dir("smb://", dirent->name, resume);
|
---|
212 | break;
|
---|
213 |
|
---|
214 | case SMBC_SERVER:
|
---|
215 | ok = smb_download_dir("smb://", dirent->name, resume);
|
---|
216 | break;
|
---|
217 |
|
---|
218 | case SMBC_FILE:
|
---|
219 | ok = smb_download_file(base, newname, true, resume,
|
---|
220 | false, NULL);
|
---|
221 | break;
|
---|
222 |
|
---|
223 | case SMBC_FILE_SHARE:
|
---|
224 | ok = smb_download_dir(base, newname, resume);
|
---|
225 | break;
|
---|
226 |
|
---|
227 | case SMBC_PRINTER_SHARE:
|
---|
228 | if (!opt.quiet) {
|
---|
229 | printf("Ignoring printer share %s\n",
|
---|
230 | dirent->name);
|
---|
231 | }
|
---|
232 | break;
|
---|
233 |
|
---|
234 | case SMBC_COMMS_SHARE:
|
---|
235 | if (!opt.quiet) {
|
---|
236 | printf("Ignoring comms share %s\n",
|
---|
237 | dirent->name);
|
---|
238 | }
|
---|
239 | break;
|
---|
240 |
|
---|
241 | case SMBC_IPC_SHARE:
|
---|
242 | if (!opt.quiet) {
|
---|
243 | printf("Ignoring ipc$ share %s\n",
|
---|
244 | dirent->name);
|
---|
245 | }
|
---|
246 | break;
|
---|
247 |
|
---|
248 | default:
|
---|
249 | fprintf(stderr, "Ignoring file '%s' of type '%d'\n",
|
---|
250 | newname, dirent->smbc_type);
|
---|
251 | break;
|
---|
252 | }
|
---|
253 |
|
---|
254 | if (!ok) {
|
---|
255 | fprintf(stderr, "Failed to download %s: %s\n",
|
---|
256 | newname, strerror(errno));
|
---|
257 | return false;
|
---|
258 | }
|
---|
259 | free(newname);
|
---|
260 | }
|
---|
261 | free(tmpname);
|
---|
262 |
|
---|
263 | smbc_closedir(dirhandle);
|
---|
264 | return ok;
|
---|
265 | }
|
---|
266 |
|
---|
267 | static char *print_time(long t)
|
---|
268 | {
|
---|
269 | static char buffer[100];
|
---|
270 | int secs, mins, hours;
|
---|
271 | if (t < -1) {
|
---|
272 | strncpy(buffer, "Unknown", sizeof(buffer));
|
---|
273 | return buffer;
|
---|
274 | }
|
---|
275 |
|
---|
276 | secs = (int)t % 60;
|
---|
277 | mins = (int)t / 60 % 60;
|
---|
278 | hours = (int)t / (60 * 60);
|
---|
279 | snprintf(buffer, sizeof(buffer) - 1, "%02d:%02d:%02d", hours, mins,
|
---|
280 | secs);
|
---|
281 | return buffer;
|
---|
282 | }
|
---|
283 |
|
---|
284 | static void print_progress(const char *name, time_t start, time_t now,
|
---|
285 | off_t start_pos, off_t pos, off_t total)
|
---|
286 | {
|
---|
287 | double avg = 0.0;
|
---|
288 | long eta = -1;
|
---|
289 | double prcnt = 0.0;
|
---|
290 | char hpos[20], htotal[20], havg[20];
|
---|
291 | char *status, *filename;
|
---|
292 | int len;
|
---|
293 | if (now - start) {
|
---|
294 | avg = 1.0 * (pos - start_pos) / (now - start);
|
---|
295 | }
|
---|
296 | eta = (total - pos) / avg;
|
---|
297 | if (total) {
|
---|
298 | prcnt = 100.0 * pos / total;
|
---|
299 | }
|
---|
300 |
|
---|
301 | human_readable(pos, hpos, sizeof(hpos));
|
---|
302 | human_readable(total, htotal, sizeof(htotal));
|
---|
303 | human_readable(avg, havg, sizeof(havg));
|
---|
304 |
|
---|
305 | len = asprintf(&status, "%s of %s (%.2f%%) at %s/s ETA: %s", hpos,
|
---|
306 | htotal, prcnt, havg, print_time(eta));
|
---|
307 | if (len == -1) {
|
---|
308 | return;
|
---|
309 | }
|
---|
310 |
|
---|
311 | if (columns) {
|
---|
312 | int required = strlen(name),
|
---|
313 | available = columns - len - strlen("[] ");
|
---|
314 | if (required > available) {
|
---|
315 | if (asprintf(&filename, "...%s",
|
---|
316 | name + required - available + 3) == -1) {
|
---|
317 | return;
|
---|
318 | }
|
---|
319 | } else {
|
---|
320 | filename = SMB_STRNDUP(name, available);
|
---|
321 | }
|
---|
322 | } else {
|
---|
323 | filename = SMB_STRDUP(name);
|
---|
324 | }
|
---|
325 |
|
---|
326 | fprintf(stderr, "\r[%s] %s", filename, status);
|
---|
327 |
|
---|
328 | free(filename);
|
---|
329 | free(status);
|
---|
330 | }
|
---|
331 |
|
---|
332 | /* Return false on error, true on success. */
|
---|
333 |
|
---|
334 | static bool smb_download_file(const char *base, const char *name,
|
---|
335 | bool recursive, bool resume, bool toplevel,
|
---|
336 | char *outfile)
|
---|
337 | {
|
---|
338 | int remotehandle, localhandle;
|
---|
339 | time_t start_time = time_mono(NULL);
|
---|
340 | const char *newpath;
|
---|
341 | char path[SMB_MAXPATHLEN];
|
---|
342 | char checkbuf[2][RESUME_CHECK_SIZE];
|
---|
343 | char *readbuf = NULL;
|
---|
344 | off_t offset_download = 0, offset_check = 0, curpos = 0,
|
---|
345 | start_offset = 0;
|
---|
346 | struct stat localstat, remotestat;
|
---|
347 |
|
---|
348 | snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base,
|
---|
349 | (*base && *name && name[0] != '/' &&
|
---|
350 | base[strlen(base)-1] != '/') ? "/" : "",
|
---|
351 | name);
|
---|
352 |
|
---|
353 | remotehandle = smbc_open(path, O_RDONLY, 0755);
|
---|
354 |
|
---|
355 | if (remotehandle < 0) {
|
---|
356 | switch (errno) {
|
---|
357 | case EISDIR:
|
---|
358 | if (!recursive) {
|
---|
359 | fprintf(stderr,
|
---|
360 | "%s is a directory. Specify -R "
|
---|
361 | "to download recursively\n",
|
---|
362 | path);
|
---|
363 | return false;
|
---|
364 | }
|
---|
365 | return smb_download_dir(base, name, resume);
|
---|
366 |
|
---|
367 | case ENOENT:
|
---|
368 | fprintf(stderr,
|
---|
369 | "%s can't be found on the remote server\n",
|
---|
370 | path);
|
---|
371 | return false;
|
---|
372 |
|
---|
373 | case ENOMEM:
|
---|
374 | fprintf(stderr, "Not enough memory\n");
|
---|
375 | return false;
|
---|
376 |
|
---|
377 | case ENODEV:
|
---|
378 | fprintf(stderr,
|
---|
379 | "The share name used in %s does not exist\n",
|
---|
380 | path);
|
---|
381 | return false;
|
---|
382 |
|
---|
383 | case EACCES:
|
---|
384 | fprintf(stderr, "You don't have enough permissions "
|
---|
385 | "to access %s\n",
|
---|
386 | path);
|
---|
387 | return false;
|
---|
388 |
|
---|
389 | default:
|
---|
390 | perror("smbc_open");
|
---|
391 | return false;
|
---|
392 | }
|
---|
393 | }
|
---|
394 |
|
---|
395 | if (smbc_fstat(remotehandle, &remotestat) < 0) {
|
---|
396 | fprintf(stderr, "Can't stat %s: %s\n", path, strerror(errno));
|
---|
397 | return false;
|
---|
398 | }
|
---|
399 |
|
---|
400 | if (outfile) {
|
---|
401 | newpath = outfile;
|
---|
402 | } else if (!name[0]) {
|
---|
403 | newpath = strrchr(base, '/');
|
---|
404 | if (newpath) {
|
---|
405 | newpath++;
|
---|
406 | } else {
|
---|
407 | newpath = base;
|
---|
408 | }
|
---|
409 | } else {
|
---|
410 | newpath = name;
|
---|
411 | }
|
---|
412 |
|
---|
413 | if (!toplevel && (newpath[0] == '/')) {
|
---|
414 | newpath++;
|
---|
415 | }
|
---|
416 |
|
---|
417 | /* Open local file according to the mode */
|
---|
418 | if (opt.update) {
|
---|
419 | /* if it is up-to-date, skip */
|
---|
420 | if (stat(newpath, &localstat) == 0 &&
|
---|
421 | localstat.st_mtime >= remotestat.st_mtime) {
|
---|
422 | if (opt.verbose) {
|
---|
423 | printf("%s is up-to-date, skipping\n", newpath);
|
---|
424 | }
|
---|
425 | smbc_close(remotehandle);
|
---|
426 | return true;
|
---|
427 | }
|
---|
428 | /* else open it for writing and truncate if it exists */
|
---|
429 | localhandle = open(
|
---|
430 | newpath, O_CREAT | O_NONBLOCK | O_RDWR | O_TRUNC, 0775);
|
---|
431 | if (localhandle < 0) {
|
---|
432 | fprintf(stderr, "Can't open %s : %s\n", newpath,
|
---|
433 | strerror(errno));
|
---|
434 | smbc_close(remotehandle);
|
---|
435 | return false;
|
---|
436 | }
|
---|
437 | /* no offset */
|
---|
438 | } else if (!opt.send_stdout) {
|
---|
439 | localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR |
|
---|
440 | (!resume ? O_EXCL : 0),
|
---|
441 | 0755);
|
---|
442 | if (localhandle < 0) {
|
---|
443 | fprintf(stderr, "Can't open %s: %s\n", newpath,
|
---|
444 | strerror(errno));
|
---|
445 | smbc_close(remotehandle);
|
---|
446 | return false;
|
---|
447 | }
|
---|
448 |
|
---|
449 | if (fstat(localhandle, &localstat) != 0) {
|
---|
450 | fprintf(stderr, "Can't fstat %s: %s\n", newpath,
|
---|
451 | strerror(errno));
|
---|
452 | smbc_close(remotehandle);
|
---|
453 | close(localhandle);
|
---|
454 | return false;
|
---|
455 | }
|
---|
456 |
|
---|
457 | start_offset = localstat.st_size;
|
---|
458 |
|
---|
459 | if (localstat.st_size &&
|
---|
460 | localstat.st_size == remotestat.st_size) {
|
---|
461 | if (opt.verbose) {
|
---|
462 | fprintf(stderr, "%s is already downloaded "
|
---|
463 | "completely.\n",
|
---|
464 | path);
|
---|
465 | } else if (!opt.quiet) {
|
---|
466 | fprintf(stderr, "%s\n", path);
|
---|
467 | }
|
---|
468 | smbc_close(remotehandle);
|
---|
469 | close(localhandle);
|
---|
470 | return true;
|
---|
471 | }
|
---|
472 |
|
---|
473 | if (localstat.st_size > RESUME_CHECK_OFFSET &&
|
---|
474 | remotestat.st_size > RESUME_CHECK_OFFSET) {
|
---|
475 | offset_download =
|
---|
476 | localstat.st_size - RESUME_DOWNLOAD_OFFSET;
|
---|
477 | offset_check = localstat.st_size - RESUME_CHECK_OFFSET;
|
---|
478 | if (opt.verbose) {
|
---|
479 | printf("Trying to start resume of %s at %jd\n"
|
---|
480 | "At the moment %jd of %jd bytes have "
|
---|
481 | "been retrieved\n",
|
---|
482 | newpath, (intmax_t)offset_check,
|
---|
483 | (intmax_t)localstat.st_size,
|
---|
484 | (intmax_t)remotestat.st_size);
|
---|
485 | }
|
---|
486 | }
|
---|
487 |
|
---|
488 | if (offset_check) {
|
---|
489 | off_t off1, off2;
|
---|
490 | /* First, check all bytes from offset_check to
|
---|
491 | * offset_download */
|
---|
492 | off1 = lseek(localhandle, offset_check, SEEK_SET);
|
---|
493 | if (off1 < 0) {
|
---|
494 | fprintf(stderr,
|
---|
495 | "Can't seek to %jd in local file %s\n",
|
---|
496 | (intmax_t)offset_check, newpath);
|
---|
497 | smbc_close(remotehandle);
|
---|
498 | close(localhandle);
|
---|
499 | return false;
|
---|
500 | }
|
---|
501 |
|
---|
502 | off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET);
|
---|
503 | if (off2 < 0) {
|
---|
504 | fprintf(stderr,
|
---|
505 | "Can't seek to %jd in remote file %s\n",
|
---|
506 | (intmax_t)offset_check, newpath);
|
---|
507 | smbc_close(remotehandle);
|
---|
508 | close(localhandle);
|
---|
509 | return false;
|
---|
510 | }
|
---|
511 |
|
---|
512 | if (off1 != off2) {
|
---|
513 | fprintf(stderr, "Offset in local and remote "
|
---|
514 | "files are different "
|
---|
515 | "(local: %jd, remote: %jd)\n",
|
---|
516 | (intmax_t)off1, (intmax_t)off2);
|
---|
517 | smbc_close(remotehandle);
|
---|
518 | close(localhandle);
|
---|
519 | return false;
|
---|
520 | }
|
---|
521 |
|
---|
522 | if (smbc_read(remotehandle, checkbuf[0],
|
---|
523 | RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
|
---|
524 | fprintf(stderr, "Can't read %d bytes from "
|
---|
525 | "remote file %s\n",
|
---|
526 | RESUME_CHECK_SIZE, path);
|
---|
527 | smbc_close(remotehandle);
|
---|
528 | close(localhandle);
|
---|
529 | return false;
|
---|
530 | }
|
---|
531 |
|
---|
532 | if (read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) !=
|
---|
533 | RESUME_CHECK_SIZE) {
|
---|
534 | fprintf(stderr, "Can't read %d bytes from "
|
---|
535 | "local file %s\n",
|
---|
536 | RESUME_CHECK_SIZE, name);
|
---|
537 | smbc_close(remotehandle);
|
---|
538 | close(localhandle);
|
---|
539 | return false;
|
---|
540 | }
|
---|
541 |
|
---|
542 | if (memcmp(checkbuf[0], checkbuf[1],
|
---|
543 | RESUME_CHECK_SIZE) == 0) {
|
---|
544 | if (opt.verbose) {
|
---|
545 | printf("Current local and remote file "
|
---|
546 | "appear to be the same. "
|
---|
547 | "Starting download from "
|
---|
548 | "offset %jd\n",
|
---|
549 | (intmax_t)offset_download);
|
---|
550 | }
|
---|
551 | } else {
|
---|
552 | fprintf(stderr, "Local and remote file appear "
|
---|
553 | "to be different, not "
|
---|
554 | "doing resume for %s\n",
|
---|
555 | path);
|
---|
556 | smbc_close(remotehandle);
|
---|
557 | close(localhandle);
|
---|
558 | return false;
|
---|
559 | }
|
---|
560 | }
|
---|
561 | } else {
|
---|
562 | localhandle = STDOUT_FILENO;
|
---|
563 | start_offset = 0;
|
---|
564 | offset_download = 0;
|
---|
565 | offset_check = 0;
|
---|
566 | }
|
---|
567 |
|
---|
568 | readbuf = (char *)SMB_MALLOC(opt.blocksize);
|
---|
569 | if (!readbuf) {
|
---|
570 | fprintf(stderr, "Failed to allocate %zu bytes for read "
|
---|
571 | "buffer (%s)", opt.blocksize, strerror(errno));
|
---|
572 | if (localhandle != STDOUT_FILENO) {
|
---|
573 | close(localhandle);
|
---|
574 | }
|
---|
575 | return false;
|
---|
576 | }
|
---|
577 |
|
---|
578 | /* Now, download all bytes from offset_download to the end */
|
---|
579 | for (curpos = offset_download; curpos < remotestat.st_size;
|
---|
580 | curpos += opt.blocksize) {
|
---|
581 | ssize_t bytesread;
|
---|
582 | ssize_t byteswritten;
|
---|
583 |
|
---|
584 | bytesread = smbc_read(remotehandle, readbuf, opt.blocksize);
|
---|
585 | if(bytesread < 0) {
|
---|
586 | fprintf(stderr,
|
---|
587 | "Can't read %zu bytes at offset %jd, file %s\n",
|
---|
588 | opt.blocksize, (intmax_t)curpos, path);
|
---|
589 | smbc_close(remotehandle);
|
---|
590 | if (localhandle != STDOUT_FILENO) {
|
---|
591 | close(localhandle);
|
---|
592 | }
|
---|
593 | free(readbuf);
|
---|
594 | return false;
|
---|
595 | }
|
---|
596 |
|
---|
597 | total_bytes += bytesread;
|
---|
598 |
|
---|
599 | byteswritten = write(localhandle, readbuf, bytesread);
|
---|
600 | if (byteswritten != bytesread) {
|
---|
601 | fprintf(stderr,
|
---|
602 | "Can't write %zd bytes to local file %s at "
|
---|
603 | "offset %jd\n", bytesread, path,
|
---|
604 | (intmax_t)curpos);
|
---|
605 | free(readbuf);
|
---|
606 | smbc_close(remotehandle);
|
---|
607 | if (localhandle != STDOUT_FILENO) {
|
---|
608 | close(localhandle);
|
---|
609 | }
|
---|
610 | return false;
|
---|
611 | }
|
---|
612 |
|
---|
613 | if (opt.dots) {
|
---|
614 | fputc('.', stderr);
|
---|
615 | } else if (!opt.quiet) {
|
---|
616 | print_progress(newpath, start_time, time_mono(NULL),
|
---|
617 | start_offset, curpos,
|
---|
618 | remotestat.st_size);
|
---|
619 | }
|
---|
620 | }
|
---|
621 |
|
---|
622 | free(readbuf);
|
---|
623 |
|
---|
624 | if (opt.dots) {
|
---|
625 | fputc('\n', stderr);
|
---|
626 | printf("%s downloaded\n", path);
|
---|
627 | } else if (!opt.quiet) {
|
---|
628 | int i;
|
---|
629 | fprintf(stderr, "\r%s", path);
|
---|
630 | if (columns) {
|
---|
631 | for (i = strlen(path); i < columns; i++) {
|
---|
632 | fputc(' ', stderr);
|
---|
633 | }
|
---|
634 | }
|
---|
635 | fputc('\n', stderr);
|
---|
636 | }
|
---|
637 |
|
---|
638 | smbc_close(remotehandle);
|
---|
639 | if (localhandle != STDOUT_FILENO) {
|
---|
640 | close(localhandle);
|
---|
641 | }
|
---|
642 | return true;
|
---|
643 | }
|
---|
644 |
|
---|
645 | static void clean_exit(void)
|
---|
646 | {
|
---|
647 | char bs[100];
|
---|
648 | human_readable(total_bytes, bs, sizeof(bs));
|
---|
649 | if (!opt.quiet) {
|
---|
650 | fprintf(stderr, "Downloaded %s in %lu seconds\n", bs,
|
---|
651 | (unsigned long)(time_mono(NULL) - total_start_time));
|
---|
652 | }
|
---|
653 | exit(0);
|
---|
654 | }
|
---|
655 |
|
---|
656 | static void signal_quit(int v)
|
---|
657 | {
|
---|
658 | clean_exit();
|
---|
659 | }
|
---|
660 |
|
---|
661 | static int readrcfile(const char *name, const struct poptOption long_options[])
|
---|
662 | {
|
---|
663 | FILE *fd = fopen(name, "r");
|
---|
664 | int lineno = 0, i;
|
---|
665 | char var[101], val[101];
|
---|
666 | bool found;
|
---|
667 | int *intdata;
|
---|
668 | char **stringdata;
|
---|
669 | if (!fd) {
|
---|
670 | fprintf(stderr, "Can't open RC file %s\n", name);
|
---|
671 | return 1;
|
---|
672 | }
|
---|
673 |
|
---|
674 | while (!feof(fd)) {
|
---|
675 | lineno++;
|
---|
676 | if (fscanf(fd, "%100s %100s\n", var, val) < 2) {
|
---|
677 | fprintf(stderr,
|
---|
678 | "Can't parse line %d of %s, ignoring.\n",
|
---|
679 | lineno, name);
|
---|
680 | continue;
|
---|
681 | }
|
---|
682 |
|
---|
683 | found = false;
|
---|
684 |
|
---|
685 | for (i = 0; long_options[i].argInfo; i++) {
|
---|
686 | if (!long_options[i].longName) {
|
---|
687 | continue;
|
---|
688 | }
|
---|
689 | if (strcmp(long_options[i].longName, var)) {
|
---|
690 | continue;
|
---|
691 | }
|
---|
692 | if (!long_options[i].arg) {
|
---|
693 | continue;
|
---|
694 | }
|
---|
695 |
|
---|
696 | switch (long_options[i].argInfo) {
|
---|
697 | case POPT_ARG_NONE:
|
---|
698 | intdata = (int *)long_options[i].arg;
|
---|
699 | if (!strcmp(val, "on")) {
|
---|
700 | *intdata = 1;
|
---|
701 | } else if (!strcmp(val, "off")) {
|
---|
702 | *intdata = 0;
|
---|
703 | } else {
|
---|
704 | fprintf(stderr, "Illegal value %s for "
|
---|
705 | "%s at line %d in %s\n",
|
---|
706 | val, var, lineno, name);
|
---|
707 | }
|
---|
708 | break;
|
---|
709 | case POPT_ARG_INT:
|
---|
710 | intdata = (int *)long_options[i].arg;
|
---|
711 | *intdata = atoi(val);
|
---|
712 | break;
|
---|
713 | case POPT_ARG_STRING:
|
---|
714 | stringdata = (char **)long_options[i].arg;
|
---|
715 | *stringdata = SMB_STRDUP(val);
|
---|
716 | if (long_options[i].shortName == 'U') {
|
---|
717 | char *p;
|
---|
718 | opt.username_specified = true;
|
---|
719 | p = strchr(*stringdata, '%');
|
---|
720 | if (p != NULL) {
|
---|
721 | *p = '\0';
|
---|
722 | opt.password = p + 1;
|
---|
723 | opt.password_specified = true;
|
---|
724 | }
|
---|
725 | }
|
---|
726 | break;
|
---|
727 | default:
|
---|
728 | fprintf(stderr, "Invalid variable %s at "
|
---|
729 | "line %d in %s\n",
|
---|
730 | var, lineno, name);
|
---|
731 | break;
|
---|
732 | }
|
---|
733 |
|
---|
734 | found = true;
|
---|
735 | }
|
---|
736 | if (!found) {
|
---|
737 | fprintf(stderr,
|
---|
738 | "Invalid variable %s at line %d in %s\n", var,
|
---|
739 | lineno, name);
|
---|
740 | }
|
---|
741 | }
|
---|
742 |
|
---|
743 | fclose(fd);
|
---|
744 | return 0;
|
---|
745 | }
|
---|
746 |
|
---|
747 | int main(int argc, char **argv)
|
---|
748 | {
|
---|
749 | int c = 0;
|
---|
750 | const char *file = NULL;
|
---|
751 | char *rcfile = NULL;
|
---|
752 | bool smb_encrypt = false;
|
---|
753 | int resume = 0, recursive = 0;
|
---|
754 | TALLOC_CTX *frame = talloc_stackframe();
|
---|
755 | bool ret = true;
|
---|
756 | char *p;
|
---|
757 | const char **argv_const = discard_const_p(const char *, argv);
|
---|
758 | struct poptOption long_options[] = {
|
---|
759 | POPT_AUTOHELP
|
---|
760 |
|
---|
761 | {"workgroup", 'w', POPT_ARG_STRING, &opt.workgroup, 'w', "Workgroup to use (optional)" },
|
---|
762 | {"user", 'U', POPT_ARG_STRING, &opt.username, 'U', "Username to use" },
|
---|
763 | {"guest", 'a', POPT_ARG_NONE, NULL, 'a', "Work as user guest" },
|
---|
764 |
|
---|
765 | {"nonprompt", 'n', POPT_ARG_NONE, NULL, 'n', "Don't ask anything (non-interactive)" },
|
---|
766 | {"debuglevel", 'd', POPT_ARG_INT, &opt.debuglevel, 'd', "Debuglevel to use" },
|
---|
767 |
|
---|
768 | {"encrypt", 'e', POPT_ARG_NONE, NULL, 'e', "Encrypt SMB transport" },
|
---|
769 | {"resume", 'r', POPT_ARG_NONE, NULL, 'r', "Automatically resume aborted files" },
|
---|
770 | {"update", 'u', POPT_ARG_NONE, NULL, 'u', "Download only when remote file is newer than local file or local file is missing"},
|
---|
771 | {"recursive", 'R', POPT_ARG_NONE, NULL, 'R', "Recursively download files" },
|
---|
772 | {"blocksize", 'b', POPT_ARG_INT, &opt.blocksize, 'b', "Change number of bytes in a block"},
|
---|
773 |
|
---|
774 | {"outputfile", 'o', POPT_ARG_STRING, &opt.outputfile, 'o', "Write downloaded data to specified file" },
|
---|
775 | {"stdout", 'O', POPT_ARG_NONE, NULL, 'O', "Write data to stdout" },
|
---|
776 | {"dots", 'D', POPT_ARG_NONE, NULL, 'D', "Show dots as progress indication" },
|
---|
777 | {"quiet", 'q', POPT_ARG_NONE, NULL, 'q', "Be quiet" },
|
---|
778 | {"verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Be verbose" },
|
---|
779 | {"rcfile", 'f', POPT_ARG_STRING, NULL, 'f', "Use specified rc file"},
|
---|
780 |
|
---|
781 | POPT_TABLEEND
|
---|
782 | };
|
---|
783 | poptContext pc;
|
---|
784 |
|
---|
785 | smb_init_locale();
|
---|
786 |
|
---|
787 | /* only read rcfile if it exists */
|
---|
788 | if (asprintf(&rcfile, "%s/.smbgetrc", getenv("HOME")) == -1) {
|
---|
789 | return 1;
|
---|
790 | }
|
---|
791 | if (access(rcfile, F_OK) == 0) {
|
---|
792 | readrcfile(rcfile, long_options);
|
---|
793 | }
|
---|
794 | free(rcfile);
|
---|
795 |
|
---|
796 | #ifdef SIGWINCH
|
---|
797 | signal(SIGWINCH, change_columns);
|
---|
798 | #endif
|
---|
799 | signal(SIGINT, signal_quit);
|
---|
800 | signal(SIGTERM, signal_quit);
|
---|
801 |
|
---|
802 | pc = poptGetContext(argv[0], argc, argv_const, long_options, 0);
|
---|
803 |
|
---|
804 | while ((c = poptGetNextOpt(pc)) > 0) {
|
---|
805 | switch (c) {
|
---|
806 | case 'f':
|
---|
807 | readrcfile(poptGetOptArg(pc), long_options);
|
---|
808 | break;
|
---|
809 | case 'a':
|
---|
810 | opt.username_specified = true;
|
---|
811 | opt.username = talloc_strdup(frame, "");
|
---|
812 | opt.password_specified = true;
|
---|
813 | opt.password = talloc_strdup(frame, "");
|
---|
814 | break;
|
---|
815 | case 'e':
|
---|
816 | smb_encrypt = true;
|
---|
817 | break;
|
---|
818 | case 'U':
|
---|
819 | opt.username_specified = true;
|
---|
820 | opt.username = talloc_strdup(frame, opt.username);
|
---|
821 | p = strchr(opt.username,'%');
|
---|
822 | if (p != NULL) {
|
---|
823 | *p = '\0';
|
---|
824 | opt.password = p + 1;
|
---|
825 | opt.password_specified = true;
|
---|
826 | }
|
---|
827 | break;
|
---|
828 | case 'n':
|
---|
829 | opt.nonprompt = true;
|
---|
830 | break;
|
---|
831 | case 'r':
|
---|
832 | resume = true;
|
---|
833 | break;
|
---|
834 | case 'u':
|
---|
835 | opt.update = true;
|
---|
836 | break;
|
---|
837 | case 'R':
|
---|
838 | recursive = true;
|
---|
839 | break;
|
---|
840 | case 'O':
|
---|
841 | opt.send_stdout = true;
|
---|
842 | break;
|
---|
843 | case 'D':
|
---|
844 | opt.dots = true;
|
---|
845 | break;
|
---|
846 | case 'q':
|
---|
847 | opt.quiet = true;
|
---|
848 | break;
|
---|
849 | case 'v':
|
---|
850 | opt.verbose = true;
|
---|
851 | break;
|
---|
852 | }
|
---|
853 | }
|
---|
854 |
|
---|
855 | if (c < -1) {
|
---|
856 | fprintf(stderr, "%s: %s\n",
|
---|
857 | poptBadOption(pc, POPT_BADOPTION_NOALIAS),
|
---|
858 | poptStrerror(c));
|
---|
859 | return 1;
|
---|
860 | }
|
---|
861 |
|
---|
862 | if ((opt.send_stdout || resume || opt.outputfile) && opt.update) {
|
---|
863 | fprintf(stderr, "The -o, -R or -O and -U options can not be "
|
---|
864 | "used together.\n");
|
---|
865 | return 1;
|
---|
866 | }
|
---|
867 | if ((opt.send_stdout || opt.outputfile) && recursive) {
|
---|
868 | fprintf(stderr, "The -o or -O and -R options can not be "
|
---|
869 | "used together.\n");
|
---|
870 | return 1;
|
---|
871 | }
|
---|
872 |
|
---|
873 | if (opt.outputfile && opt.send_stdout) {
|
---|
874 | fprintf(stderr, "The -o and -O options can not be "
|
---|
875 | "used together.\n");
|
---|
876 | return 1;
|
---|
877 | }
|
---|
878 |
|
---|
879 | popt_burn_cmdline_password(argc, argv);
|
---|
880 |
|
---|
881 | if (smbc_init(get_auth_data, opt.debuglevel) < 0) {
|
---|
882 | fprintf(stderr, "Unable to initialize libsmbclient\n");
|
---|
883 | return 1;
|
---|
884 | }
|
---|
885 |
|
---|
886 | if (smb_encrypt) {
|
---|
887 | SMBCCTX *smb_ctx = smbc_set_context(NULL);
|
---|
888 | smbc_option_set(smb_ctx,
|
---|
889 | discard_const_p(char, "smb_encrypt_level"),
|
---|
890 | "require");
|
---|
891 | }
|
---|
892 |
|
---|
893 | columns = get_num_cols();
|
---|
894 |
|
---|
895 | total_start_time = time_mono(NULL);
|
---|
896 |
|
---|
897 | while ((file = poptGetArg(pc))) {
|
---|
898 | if (!recursive) {
|
---|
899 | ret = smb_download_file(file, "", recursive, resume,
|
---|
900 | true, opt.outputfile);
|
---|
901 | } else {
|
---|
902 | ret = smb_download_dir(file, "", resume);
|
---|
903 | }
|
---|
904 | }
|
---|
905 |
|
---|
906 | TALLOC_FREE(frame);
|
---|
907 | if (ret) {
|
---|
908 | clean_exit();
|
---|
909 | }
|
---|
910 | return ret?0:1;
|
---|
911 | }
|
---|