LCOV - code coverage report
Current view: top level - source3/utils - smbget.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 278 493 56.4 %
Date: 2021-09-23 10:06:22 Functions: 10 12 83.3 %

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

Generated by: LCOV version 1.13