LCOV - code coverage report
Current view: top level - source3/utils - smbget.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 316 504 62.7 %
Date: 2024-02-28 12:06:22 Functions: 9 11 81.8 %

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

Generated by: LCOV version 1.14