file.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 
00023 #include "curl_setup.h"
00024 
00025 #ifndef CURL_DISABLE_FILE
00026 
00027 #ifdef HAVE_NETINET_IN_H
00028 #include <netinet/in.h>
00029 #endif
00030 #ifdef HAVE_NETDB_H
00031 #include <netdb.h>
00032 #endif
00033 #ifdef HAVE_ARPA_INET_H
00034 #include <arpa/inet.h>
00035 #endif
00036 #ifdef HAVE_NET_IF_H
00037 #include <net/if.h>
00038 #endif
00039 #ifdef HAVE_SYS_IOCTL_H
00040 #include <sys/ioctl.h>
00041 #endif
00042 
00043 #ifdef HAVE_SYS_PARAM_H
00044 #include <sys/param.h>
00045 #endif
00046 
00047 #ifdef HAVE_FCNTL_H
00048 #include <fcntl.h>
00049 #endif
00050 
00051 #include "strtoofft.h"
00052 #include "urldata.h"
00053 #include <curl/curl.h>
00054 #include "progress.h"
00055 #include "sendf.h"
00056 #include "escape.h"
00057 #include "file.h"
00058 #include "speedcheck.h"
00059 #include "getinfo.h"
00060 #include "transfer.h"
00061 #include "url.h"
00062 #include "parsedate.h" /* for the week day and month names */
00063 #include "warnless.h"
00064 /* The last 3 #include files should be in this order */
00065 #include "curl_printf.h"
00066 #include "curl_memory.h"
00067 #include "memdebug.h"
00068 
00069 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
00070   defined(__SYMBIAN32__)
00071 #define DOS_FILESYSTEM 1
00072 #endif
00073 
00074 #ifdef OPEN_NEEDS_ARG3
00075 #  define open_readonly(p,f) open((p),(f),(0))
00076 #else
00077 #  define open_readonly(p,f) open((p),(f))
00078 #endif
00079 
00080 /*
00081  * Forward declarations.
00082  */
00083 
00084 static CURLcode file_do(struct connectdata *, bool *done);
00085 static CURLcode file_done(struct connectdata *conn,
00086                           CURLcode status, bool premature);
00087 static CURLcode file_connect(struct connectdata *conn, bool *done);
00088 static CURLcode file_disconnect(struct connectdata *conn,
00089                                 bool dead_connection);
00090 static CURLcode file_setup_connection(struct connectdata *conn);
00091 
00092 /*
00093  * FILE scheme handler.
00094  */
00095 
00096 const struct Curl_handler Curl_handler_file = {
00097   "FILE",                               /* scheme */
00098   file_setup_connection,                /* setup_connection */
00099   file_do,                              /* do_it */
00100   file_done,                            /* done */
00101   ZERO_NULL,                            /* do_more */
00102   file_connect,                         /* connect_it */
00103   ZERO_NULL,                            /* connecting */
00104   ZERO_NULL,                            /* doing */
00105   ZERO_NULL,                            /* proto_getsock */
00106   ZERO_NULL,                            /* doing_getsock */
00107   ZERO_NULL,                            /* domore_getsock */
00108   ZERO_NULL,                            /* perform_getsock */
00109   file_disconnect,                      /* disconnect */
00110   ZERO_NULL,                            /* readwrite */
00111   0,                                    /* defport */
00112   CURLPROTO_FILE,                       /* protocol */
00113   PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
00114 };
00115 
00116 
00117 static CURLcode file_setup_connection(struct connectdata *conn)
00118 {
00119   /* allocate the FILE specific struct */
00120   conn->data->req.protop = calloc(1, sizeof(struct FILEPROTO));
00121   if(!conn->data->req.protop)
00122     return CURLE_OUT_OF_MEMORY;
00123 
00124   return CURLE_OK;
00125 }
00126 
00127  /*
00128   Check if this is a range download, and if so, set the internal variables
00129   properly. This code is copied from the FTP implementation and might as
00130   well be factored out.
00131  */
00132 static CURLcode file_range(struct connectdata *conn)
00133 {
00134   curl_off_t from, to;
00135   curl_off_t totalsize=-1;
00136   char *ptr;
00137   char *ptr2;
00138   struct Curl_easy *data = conn->data;
00139 
00140   if(data->state.use_range && data->state.range) {
00141     from=curlx_strtoofft(data->state.range, &ptr, 0);
00142     while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
00143       ptr++;
00144     to=curlx_strtoofft(ptr, &ptr2, 0);
00145     if(ptr == ptr2) {
00146       /* we didn't get any digit */
00147       to=-1;
00148     }
00149     if((-1 == to) && (from>=0)) {
00150       /* X - */
00151       data->state.resume_from = from;
00152       DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n",
00153                    from));
00154     }
00155     else if(from < 0) {
00156       /* -Y */
00157       data->req.maxdownload = -from;
00158       data->state.resume_from = from;
00159       DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n",
00160                    -from));
00161     }
00162     else {
00163       /* X-Y */
00164       totalsize = to-from;
00165       data->req.maxdownload = totalsize+1; /* include last byte */
00166       data->state.resume_from = from;
00167       DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T
00168                    " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n",
00169                    from, data->req.maxdownload));
00170     }
00171     DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T
00172                  " to %" CURL_FORMAT_CURL_OFF_T ", totally %"
00173                  CURL_FORMAT_CURL_OFF_T " bytes\n",
00174                  from, to, data->req.maxdownload));
00175   }
00176   else
00177     data->req.maxdownload = -1;
00178   return CURLE_OK;
00179 }
00180 
00181 /*
00182  * file_connect() gets called from Curl_protocol_connect() to allow us to
00183  * do protocol-specific actions at connect-time.  We emulate a
00184  * connect-then-transfer protocol and "connect" to the file here
00185  */
00186 static CURLcode file_connect(struct connectdata *conn, bool *done)
00187 {
00188   struct Curl_easy *data = conn->data;
00189   char *real_path;
00190   struct FILEPROTO *file = data->req.protop;
00191   int fd;
00192 #ifdef DOS_FILESYSTEM
00193   size_t i;
00194   char *actual_path;
00195 #endif
00196   size_t real_path_len;
00197 
00198   CURLcode result = Curl_urldecode(data, data->state.path, 0, &real_path,
00199                                    &real_path_len, FALSE);
00200   if(result)
00201     return result;
00202 
00203 #ifdef DOS_FILESYSTEM
00204   /* If the first character is a slash, and there's
00205      something that looks like a drive at the beginning of
00206      the path, skip the slash.  If we remove the initial
00207      slash in all cases, paths without drive letters end up
00208      relative to the current directory which isn't how
00209      browsers work.
00210 
00211      Some browsers accept | instead of : as the drive letter
00212      separator, so we do too.
00213 
00214      On other platforms, we need the slash to indicate an
00215      absolute pathname.  On Windows, absolute paths start
00216      with a drive letter.
00217   */
00218   actual_path = real_path;
00219   if((actual_path[0] == '/') &&
00220       actual_path[1] &&
00221      (actual_path[2] == ':' || actual_path[2] == '|')) {
00222     actual_path[2] = ':';
00223     actual_path++;
00224     real_path_len--;
00225   }
00226 
00227   /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
00228   for(i=0; i < real_path_len; ++i)
00229     if(actual_path[i] == '/')
00230       actual_path[i] = '\\';
00231     else if(!actual_path[i]) { /* binary zero */
00232       Curl_safefree(real_path);
00233       return CURLE_URL_MALFORMAT;
00234     }
00235 
00236   fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
00237   file->path = actual_path;
00238 #else
00239   if(memchr(real_path, 0, real_path_len)) {
00240     /* binary zeroes indicate foul play */
00241     Curl_safefree(real_path);
00242     return CURLE_URL_MALFORMAT;
00243   }
00244 
00245   fd = open_readonly(real_path, O_RDONLY);
00246   file->path = real_path;
00247 #endif
00248   file->freepath = real_path; /* free this when done */
00249 
00250   file->fd = fd;
00251   if(!data->set.upload && (fd == -1)) {
00252     failf(data, "Couldn't open file %s", data->state.path);
00253     file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
00254     return CURLE_FILE_COULDNT_READ_FILE;
00255   }
00256   *done = TRUE;
00257 
00258   return CURLE_OK;
00259 }
00260 
00261 static CURLcode file_done(struct connectdata *conn,
00262                                CURLcode status, bool premature)
00263 {
00264   struct FILEPROTO *file = conn->data->req.protop;
00265   (void)status; /* not used */
00266   (void)premature; /* not used */
00267 
00268   if(file) {
00269     Curl_safefree(file->freepath);
00270     file->path = NULL;
00271     if(file->fd != -1)
00272       close(file->fd);
00273     file->fd = -1;
00274   }
00275 
00276   return CURLE_OK;
00277 }
00278 
00279 static CURLcode file_disconnect(struct connectdata *conn,
00280                                 bool dead_connection)
00281 {
00282   struct FILEPROTO *file = conn->data->req.protop;
00283   (void)dead_connection; /* not used */
00284 
00285   if(file) {
00286     Curl_safefree(file->freepath);
00287     file->path = NULL;
00288     if(file->fd != -1)
00289       close(file->fd);
00290     file->fd = -1;
00291   }
00292 
00293   return CURLE_OK;
00294 }
00295 
00296 #ifdef DOS_FILESYSTEM
00297 #define DIRSEP '\\'
00298 #else
00299 #define DIRSEP '/'
00300 #endif
00301 
00302 static CURLcode file_upload(struct connectdata *conn)
00303 {
00304   struct FILEPROTO *file = conn->data->req.protop;
00305   const char *dir = strchr(file->path, DIRSEP);
00306   int fd;
00307   int mode;
00308   CURLcode result = CURLE_OK;
00309   struct Curl_easy *data = conn->data;
00310   char *buf = data->state.buffer;
00311   size_t nread;
00312   size_t nwrite;
00313   curl_off_t bytecount = 0;
00314   struct timeval now = Curl_tvnow();
00315   struct_stat file_stat;
00316   const char *buf2;
00317 
00318   /*
00319    * Since FILE: doesn't do the full init, we need to provide some extra
00320    * assignments here.
00321    */
00322   conn->data->req.upload_fromhere = buf;
00323 
00324   if(!dir)
00325     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
00326 
00327   if(!dir[1])
00328     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
00329 
00330 #ifdef O_BINARY
00331 #define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
00332 #else
00333 #define MODE_DEFAULT O_WRONLY|O_CREAT
00334 #endif
00335 
00336   if(data->state.resume_from)
00337     mode = MODE_DEFAULT|O_APPEND;
00338   else
00339     mode = MODE_DEFAULT|O_TRUNC;
00340 
00341   fd = open(file->path, mode, conn->data->set.new_file_perms);
00342   if(fd < 0) {
00343     failf(data, "Can't open %s for writing", file->path);
00344     return CURLE_WRITE_ERROR;
00345   }
00346 
00347   if(-1 != data->state.infilesize)
00348     /* known size of data to "upload" */
00349     Curl_pgrsSetUploadSize(data, data->state.infilesize);
00350 
00351   /* treat the negative resume offset value as the case of "-" */
00352   if(data->state.resume_from < 0) {
00353     if(fstat(fd, &file_stat)) {
00354       close(fd);
00355       failf(data, "Can't get the size of %s", file->path);
00356       return CURLE_WRITE_ERROR;
00357     }
00358     else
00359       data->state.resume_from = (curl_off_t)file_stat.st_size;
00360   }
00361 
00362   while(!result) {
00363     int readcount;
00364     result = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
00365     if(result)
00366       break;
00367 
00368     if(readcount <= 0)  /* fix questionable compare error. curlvms */
00369       break;
00370 
00371     nread = (size_t)readcount;
00372 
00373     /*skip bytes before resume point*/
00374     if(data->state.resume_from) {
00375       if((curl_off_t)nread <= data->state.resume_from) {
00376         data->state.resume_from -= nread;
00377         nread = 0;
00378         buf2 = buf;
00379       }
00380       else {
00381         buf2 = buf + data->state.resume_from;
00382         nread -= (size_t)data->state.resume_from;
00383         data->state.resume_from = 0;
00384       }
00385     }
00386     else
00387       buf2 = buf;
00388 
00389     /* write the data to the target */
00390     nwrite = write(fd, buf2, nread);
00391     if(nwrite != nread) {
00392       result = CURLE_SEND_ERROR;
00393       break;
00394     }
00395 
00396     bytecount += nread;
00397 
00398     Curl_pgrsSetUploadCounter(data, bytecount);
00399 
00400     if(Curl_pgrsUpdate(conn))
00401       result = CURLE_ABORTED_BY_CALLBACK;
00402     else
00403       result = Curl_speedcheck(data, now);
00404   }
00405   if(!result && Curl_pgrsUpdate(conn))
00406     result = CURLE_ABORTED_BY_CALLBACK;
00407 
00408   close(fd);
00409 
00410   return result;
00411 }
00412 
00413 /*
00414  * file_do() is the protocol-specific function for the do-phase, separated
00415  * from the connect-phase above. Other protocols merely setup the transfer in
00416  * the do-phase, to have it done in the main transfer loop but since some
00417  * platforms we support don't allow select()ing etc on file handles (as
00418  * opposed to sockets) we instead perform the whole do-operation in this
00419  * function.
00420  */
00421 static CURLcode file_do(struct connectdata *conn, bool *done)
00422 {
00423   /* This implementation ignores the host name in conformance with
00424      RFC 1738. Only local files (reachable via the standard file system)
00425      are supported. This means that files on remotely mounted directories
00426      (via NFS, Samba, NT sharing) can be accessed through a file:// URL
00427   */
00428   CURLcode result = CURLE_OK;
00429   struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
00430                           Windows version to have a different struct without
00431                           having to redefine the simple word 'stat' */
00432   curl_off_t expected_size=0;
00433   bool size_known;
00434   bool fstated=FALSE;
00435   ssize_t nread;
00436   struct Curl_easy *data = conn->data;
00437   char *buf = data->state.buffer;
00438   curl_off_t bytecount = 0;
00439   int fd;
00440   struct timeval now = Curl_tvnow();
00441   struct FILEPROTO *file;
00442 
00443   *done = TRUE; /* unconditionally */
00444 
00445   Curl_initinfo(data);
00446   Curl_pgrsStartNow(data);
00447 
00448   if(data->set.upload)
00449     return file_upload(conn);
00450 
00451   file = conn->data->req.protop;
00452 
00453   /* get the fd from the connection phase */
00454   fd = file->fd;
00455 
00456   /* VMS: This only works reliable for STREAMLF files */
00457   if(-1 != fstat(fd, &statbuf)) {
00458     /* we could stat it, then read out the size */
00459     expected_size = statbuf.st_size;
00460     /* and store the modification time */
00461     data->info.filetime = (long)statbuf.st_mtime;
00462     fstated = TRUE;
00463   }
00464 
00465   if(fstated && !data->state.range && data->set.timecondition) {
00466     if(!Curl_meets_timecondition(data, (time_t)data->info.filetime)) {
00467       *done = TRUE;
00468       return CURLE_OK;
00469     }
00470   }
00471 
00472   /* If we have selected NOBODY and HEADER, it means that we only want file
00473      information. Which for FILE can't be much more than the file size and
00474      date. */
00475   if(data->set.opt_no_body && data->set.include_header && fstated) {
00476     time_t filetime;
00477     struct tm buffer;
00478     const struct tm *tm = &buffer;
00479     snprintf(buf, sizeof(data->state.buffer),
00480              "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
00481     result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
00482     if(result)
00483       return result;
00484 
00485     result = Curl_client_write(conn, CLIENTWRITE_BOTH,
00486                                (char *)"Accept-ranges: bytes\r\n", 0);
00487     if(result)
00488       return result;
00489 
00490     filetime = (time_t)statbuf.st_mtime;
00491     result = Curl_gmtime(filetime, &buffer);
00492     if(result)
00493       return result;
00494 
00495     /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
00496     snprintf(buf, BUFSIZE-1,
00497              "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
00498              Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
00499              tm->tm_mday,
00500              Curl_month[tm->tm_mon],
00501              tm->tm_year + 1900,
00502              tm->tm_hour,
00503              tm->tm_min,
00504              tm->tm_sec);
00505     result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
00506     if(!result)
00507       /* set the file size to make it available post transfer */
00508       Curl_pgrsSetDownloadSize(data, expected_size);
00509     return result;
00510   }
00511 
00512   /* Check whether file range has been specified */
00513   file_range(conn);
00514 
00515   /* Adjust the start offset in case we want to get the N last bytes
00516    * of the stream iff the filesize could be determined */
00517   if(data->state.resume_from < 0) {
00518     if(!fstated) {
00519       failf(data, "Can't get the size of file.");
00520       return CURLE_READ_ERROR;
00521     }
00522     else
00523       data->state.resume_from += (curl_off_t)statbuf.st_size;
00524   }
00525 
00526   if(data->state.resume_from <= expected_size)
00527     expected_size -= data->state.resume_from;
00528   else {
00529     failf(data, "failed to resume file:// transfer");
00530     return CURLE_BAD_DOWNLOAD_RESUME;
00531   }
00532 
00533   /* A high water mark has been specified so we obey... */
00534   if(data->req.maxdownload > 0)
00535     expected_size = data->req.maxdownload;
00536 
00537   if(!fstated || (expected_size == 0))
00538     size_known = FALSE;
00539   else
00540     size_known = TRUE;
00541 
00542   /* The following is a shortcut implementation of file reading
00543      this is both more efficient than the former call to download() and
00544      it avoids problems with select() and recv() on file descriptors
00545      in Winsock */
00546   if(fstated)
00547     Curl_pgrsSetDownloadSize(data, expected_size);
00548 
00549   if(data->state.resume_from) {
00550     if(data->state.resume_from !=
00551        lseek(fd, data->state.resume_from, SEEK_SET))
00552       return CURLE_BAD_DOWNLOAD_RESUME;
00553   }
00554 
00555   Curl_pgrsTime(data, TIMER_STARTTRANSFER);
00556 
00557   while(!result) {
00558     /* Don't fill a whole buffer if we want less than all data */
00559     size_t bytestoread;
00560 
00561     if(size_known) {
00562       bytestoread =
00563         (expected_size < CURL_OFF_T_C(BUFSIZE) - CURL_OFF_T_C(1)) ?
00564         curlx_sotouz(expected_size) : BUFSIZE - 1;
00565     }
00566     else
00567       bytestoread = BUFSIZE-1;
00568 
00569     nread = read(fd, buf, bytestoread);
00570 
00571     if(nread > 0)
00572       buf[nread] = 0;
00573 
00574     if(nread <= 0 || (size_known && (expected_size == 0)))
00575       break;
00576 
00577     bytecount += nread;
00578     if(size_known)
00579       expected_size -= nread;
00580 
00581     result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
00582     if(result)
00583       return result;
00584 
00585     Curl_pgrsSetDownloadCounter(data, bytecount);
00586 
00587     if(Curl_pgrsUpdate(conn))
00588       result = CURLE_ABORTED_BY_CALLBACK;
00589     else
00590       result = Curl_speedcheck(data, now);
00591   }
00592   if(Curl_pgrsUpdate(conn))
00593     result = CURLE_ABORTED_BY_CALLBACK;
00594 
00595   return result;
00596 }
00597 
00598 #endif


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02