Annotation of embedaddon/curl/lib/file.c, revision 1.1.1.1

1.1       misho       1: /***************************************************************************
                      2:  *                                  _   _ ____  _
                      3:  *  Project                     ___| | | |  _ \| |
                      4:  *                             / __| | | | |_) | |
                      5:  *                            | (__| |_| |  _ <| |___
                      6:  *                             \___|\___/|_| \_\_____|
                      7:  *
                      8:  * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
                      9:  *
                     10:  * This software is licensed as described in the file COPYING, which
                     11:  * you should have received as part of this distribution. The terms
                     12:  * are also available at https://curl.haxx.se/docs/copyright.html.
                     13:  *
                     14:  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
                     15:  * copies of the Software, and permit persons to whom the Software is
                     16:  * furnished to do so, under the terms of the COPYING file.
                     17:  *
                     18:  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
                     19:  * KIND, either express or implied.
                     20:  *
                     21:  ***************************************************************************/
                     22: 
                     23: #include "curl_setup.h"
                     24: 
                     25: #ifndef CURL_DISABLE_FILE
                     26: 
                     27: #ifdef HAVE_NETINET_IN_H
                     28: #include <netinet/in.h>
                     29: #endif
                     30: #ifdef HAVE_NETDB_H
                     31: #include <netdb.h>
                     32: #endif
                     33: #ifdef HAVE_ARPA_INET_H
                     34: #include <arpa/inet.h>
                     35: #endif
                     36: #ifdef HAVE_NET_IF_H
                     37: #include <net/if.h>
                     38: #endif
                     39: #ifdef HAVE_SYS_IOCTL_H
                     40: #include <sys/ioctl.h>
                     41: #endif
                     42: 
                     43: #ifdef HAVE_SYS_PARAM_H
                     44: #include <sys/param.h>
                     45: #endif
                     46: 
                     47: #ifdef HAVE_FCNTL_H
                     48: #include <fcntl.h>
                     49: #endif
                     50: 
                     51: #include "strtoofft.h"
                     52: #include "urldata.h"
                     53: #include <curl/curl.h>
                     54: #include "progress.h"
                     55: #include "sendf.h"
                     56: #include "escape.h"
                     57: #include "file.h"
                     58: #include "speedcheck.h"
                     59: #include "getinfo.h"
                     60: #include "transfer.h"
                     61: #include "url.h"
                     62: #include "parsedate.h" /* for the week day and month names */
                     63: #include "warnless.h"
                     64: #include "curl_range.h"
                     65: /* The last 3 #include files should be in this order */
                     66: #include "curl_printf.h"
                     67: #include "curl_memory.h"
                     68: #include "memdebug.h"
                     69: 
                     70: #if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
                     71:   defined(__SYMBIAN32__)
                     72: #define DOS_FILESYSTEM 1
                     73: #endif
                     74: 
                     75: #ifdef OPEN_NEEDS_ARG3
                     76: #  define open_readonly(p,f) open((p),(f),(0))
                     77: #else
                     78: #  define open_readonly(p,f) open((p),(f))
                     79: #endif
                     80: 
                     81: /*
                     82:  * Forward declarations.
                     83:  */
                     84: 
                     85: static CURLcode file_do(struct connectdata *, bool *done);
                     86: static CURLcode file_done(struct connectdata *conn,
                     87:                           CURLcode status, bool premature);
                     88: static CURLcode file_connect(struct connectdata *conn, bool *done);
                     89: static CURLcode file_disconnect(struct connectdata *conn,
                     90:                                 bool dead_connection);
                     91: static CURLcode file_setup_connection(struct connectdata *conn);
                     92: 
                     93: /*
                     94:  * FILE scheme handler.
                     95:  */
                     96: 
                     97: const struct Curl_handler Curl_handler_file = {
                     98:   "FILE",                               /* scheme */
                     99:   file_setup_connection,                /* setup_connection */
                    100:   file_do,                              /* do_it */
                    101:   file_done,                            /* done */
                    102:   ZERO_NULL,                            /* do_more */
                    103:   file_connect,                         /* connect_it */
                    104:   ZERO_NULL,                            /* connecting */
                    105:   ZERO_NULL,                            /* doing */
                    106:   ZERO_NULL,                            /* proto_getsock */
                    107:   ZERO_NULL,                            /* doing_getsock */
                    108:   ZERO_NULL,                            /* domore_getsock */
                    109:   ZERO_NULL,                            /* perform_getsock */
                    110:   file_disconnect,                      /* disconnect */
                    111:   ZERO_NULL,                            /* readwrite */
                    112:   ZERO_NULL,                            /* connection_check */
                    113:   0,                                    /* defport */
                    114:   CURLPROTO_FILE,                       /* protocol */
                    115:   PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
                    116: };
                    117: 
                    118: 
                    119: static CURLcode file_setup_connection(struct connectdata *conn)
                    120: {
                    121:   /* allocate the FILE specific struct */
                    122:   conn->data->req.protop = calloc(1, sizeof(struct FILEPROTO));
                    123:   if(!conn->data->req.protop)
                    124:     return CURLE_OUT_OF_MEMORY;
                    125: 
                    126:   return CURLE_OK;
                    127: }
                    128: 
                    129: /*
                    130:  * file_connect() gets called from Curl_protocol_connect() to allow us to
                    131:  * do protocol-specific actions at connect-time.  We emulate a
                    132:  * connect-then-transfer protocol and "connect" to the file here
                    133:  */
                    134: static CURLcode file_connect(struct connectdata *conn, bool *done)
                    135: {
                    136:   struct Curl_easy *data = conn->data;
                    137:   char *real_path;
                    138:   struct FILEPROTO *file = data->req.protop;
                    139:   int fd;
                    140: #ifdef DOS_FILESYSTEM
                    141:   size_t i;
                    142:   char *actual_path;
                    143: #endif
                    144:   size_t real_path_len;
                    145: 
                    146:   CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &real_path,
                    147:                                    &real_path_len, FALSE);
                    148:   if(result)
                    149:     return result;
                    150: 
                    151: #ifdef DOS_FILESYSTEM
                    152:   /* If the first character is a slash, and there's
                    153:      something that looks like a drive at the beginning of
                    154:      the path, skip the slash.  If we remove the initial
                    155:      slash in all cases, paths without drive letters end up
                    156:      relative to the current directory which isn't how
                    157:      browsers work.
                    158: 
                    159:      Some browsers accept | instead of : as the drive letter
                    160:      separator, so we do too.
                    161: 
                    162:      On other platforms, we need the slash to indicate an
                    163:      absolute pathname.  On Windows, absolute paths start
                    164:      with a drive letter.
                    165:   */
                    166:   actual_path = real_path;
                    167:   if((actual_path[0] == '/') &&
                    168:       actual_path[1] &&
                    169:      (actual_path[2] == ':' || actual_path[2] == '|')) {
                    170:     actual_path[2] = ':';
                    171:     actual_path++;
                    172:     real_path_len--;
                    173:   }
                    174: 
                    175:   /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
                    176:   for(i = 0; i < real_path_len; ++i)
                    177:     if(actual_path[i] == '/')
                    178:       actual_path[i] = '\\';
                    179:     else if(!actual_path[i]) { /* binary zero */
                    180:       Curl_safefree(real_path);
                    181:       return CURLE_URL_MALFORMAT;
                    182:     }
                    183: 
                    184:   fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
                    185:   file->path = actual_path;
                    186: #else
                    187:   if(memchr(real_path, 0, real_path_len)) {
                    188:     /* binary zeroes indicate foul play */
                    189:     Curl_safefree(real_path);
                    190:     return CURLE_URL_MALFORMAT;
                    191:   }
                    192: 
                    193:   fd = open_readonly(real_path, O_RDONLY);
                    194:   file->path = real_path;
                    195: #endif
                    196:   file->freepath = real_path; /* free this when done */
                    197: 
                    198:   file->fd = fd;
                    199:   if(!data->set.upload && (fd == -1)) {
                    200:     failf(data, "Couldn't open file %s", data->state.up.path);
                    201:     file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
                    202:     return CURLE_FILE_COULDNT_READ_FILE;
                    203:   }
                    204:   *done = TRUE;
                    205: 
                    206:   return CURLE_OK;
                    207: }
                    208: 
                    209: static CURLcode file_done(struct connectdata *conn,
                    210:                                CURLcode status, bool premature)
                    211: {
                    212:   struct FILEPROTO *file = conn->data->req.protop;
                    213:   (void)status; /* not used */
                    214:   (void)premature; /* not used */
                    215: 
                    216:   if(file) {
                    217:     Curl_safefree(file->freepath);
                    218:     file->path = NULL;
                    219:     if(file->fd != -1)
                    220:       close(file->fd);
                    221:     file->fd = -1;
                    222:   }
                    223: 
                    224:   return CURLE_OK;
                    225: }
                    226: 
                    227: static CURLcode file_disconnect(struct connectdata *conn,
                    228:                                 bool dead_connection)
                    229: {
                    230:   struct FILEPROTO *file = conn->data->req.protop;
                    231:   (void)dead_connection; /* not used */
                    232: 
                    233:   if(file) {
                    234:     Curl_safefree(file->freepath);
                    235:     file->path = NULL;
                    236:     if(file->fd != -1)
                    237:       close(file->fd);
                    238:     file->fd = -1;
                    239:   }
                    240: 
                    241:   return CURLE_OK;
                    242: }
                    243: 
                    244: #ifdef DOS_FILESYSTEM
                    245: #define DIRSEP '\\'
                    246: #else
                    247: #define DIRSEP '/'
                    248: #endif
                    249: 
                    250: static CURLcode file_upload(struct connectdata *conn)
                    251: {
                    252:   struct FILEPROTO *file = conn->data->req.protop;
                    253:   const char *dir = strchr(file->path, DIRSEP);
                    254:   int fd;
                    255:   int mode;
                    256:   CURLcode result = CURLE_OK;
                    257:   struct Curl_easy *data = conn->data;
                    258:   char *buf = data->state.buffer;
                    259:   curl_off_t bytecount = 0;
                    260:   struct_stat file_stat;
                    261:   const char *buf2;
                    262: 
                    263:   /*
                    264:    * Since FILE: doesn't do the full init, we need to provide some extra
                    265:    * assignments here.
                    266:    */
                    267:   conn->data->req.upload_fromhere = buf;
                    268: 
                    269:   if(!dir)
                    270:     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
                    271: 
                    272:   if(!dir[1])
                    273:     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
                    274: 
                    275: #ifdef O_BINARY
                    276: #define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
                    277: #else
                    278: #define MODE_DEFAULT O_WRONLY|O_CREAT
                    279: #endif
                    280: 
                    281:   if(data->state.resume_from)
                    282:     mode = MODE_DEFAULT|O_APPEND;
                    283:   else
                    284:     mode = MODE_DEFAULT|O_TRUNC;
                    285: 
                    286:   fd = open(file->path, mode, conn->data->set.new_file_perms);
                    287:   if(fd < 0) {
                    288:     failf(data, "Can't open %s for writing", file->path);
                    289:     return CURLE_WRITE_ERROR;
                    290:   }
                    291: 
                    292:   if(-1 != data->state.infilesize)
                    293:     /* known size of data to "upload" */
                    294:     Curl_pgrsSetUploadSize(data, data->state.infilesize);
                    295: 
                    296:   /* treat the negative resume offset value as the case of "-" */
                    297:   if(data->state.resume_from < 0) {
                    298:     if(fstat(fd, &file_stat)) {
                    299:       close(fd);
                    300:       failf(data, "Can't get the size of %s", file->path);
                    301:       return CURLE_WRITE_ERROR;
                    302:     }
                    303:     data->state.resume_from = (curl_off_t)file_stat.st_size;
                    304:   }
                    305: 
                    306:   while(!result) {
                    307:     size_t nread;
                    308:     size_t nwrite;
                    309:     size_t readcount;
                    310:     result = Curl_fillreadbuffer(conn, data->set.buffer_size, &readcount);
                    311:     if(result)
                    312:       break;
                    313: 
                    314:     if(!readcount)
                    315:       break;
                    316: 
                    317:     nread = readcount;
                    318: 
                    319:     /*skip bytes before resume point*/
                    320:     if(data->state.resume_from) {
                    321:       if((curl_off_t)nread <= data->state.resume_from) {
                    322:         data->state.resume_from -= nread;
                    323:         nread = 0;
                    324:         buf2 = buf;
                    325:       }
                    326:       else {
                    327:         buf2 = buf + data->state.resume_from;
                    328:         nread -= (size_t)data->state.resume_from;
                    329:         data->state.resume_from = 0;
                    330:       }
                    331:     }
                    332:     else
                    333:       buf2 = buf;
                    334: 
                    335:     /* write the data to the target */
                    336:     nwrite = write(fd, buf2, nread);
                    337:     if(nwrite != nread) {
                    338:       result = CURLE_SEND_ERROR;
                    339:       break;
                    340:     }
                    341: 
                    342:     bytecount += nread;
                    343: 
                    344:     Curl_pgrsSetUploadCounter(data, bytecount);
                    345: 
                    346:     if(Curl_pgrsUpdate(conn))
                    347:       result = CURLE_ABORTED_BY_CALLBACK;
                    348:     else
                    349:       result = Curl_speedcheck(data, Curl_now());
                    350:   }
                    351:   if(!result && Curl_pgrsUpdate(conn))
                    352:     result = CURLE_ABORTED_BY_CALLBACK;
                    353: 
                    354:   close(fd);
                    355: 
                    356:   return result;
                    357: }
                    358: 
                    359: /*
                    360:  * file_do() is the protocol-specific function for the do-phase, separated
                    361:  * from the connect-phase above. Other protocols merely setup the transfer in
                    362:  * the do-phase, to have it done in the main transfer loop but since some
                    363:  * platforms we support don't allow select()ing etc on file handles (as
                    364:  * opposed to sockets) we instead perform the whole do-operation in this
                    365:  * function.
                    366:  */
                    367: static CURLcode file_do(struct connectdata *conn, bool *done)
                    368: {
                    369:   /* This implementation ignores the host name in conformance with
                    370:      RFC 1738. Only local files (reachable via the standard file system)
                    371:      are supported. This means that files on remotely mounted directories
                    372:      (via NFS, Samba, NT sharing) can be accessed through a file:// URL
                    373:   */
                    374:   CURLcode result = CURLE_OK;
                    375:   struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
                    376:                           Windows version to have a different struct without
                    377:                           having to redefine the simple word 'stat' */
                    378:   curl_off_t expected_size = 0;
                    379:   bool size_known;
                    380:   bool fstated = FALSE;
                    381:   struct Curl_easy *data = conn->data;
                    382:   char *buf = data->state.buffer;
                    383:   curl_off_t bytecount = 0;
                    384:   int fd;
                    385:   struct FILEPROTO *file;
                    386: 
                    387:   *done = TRUE; /* unconditionally */
                    388: 
                    389:   Curl_pgrsStartNow(data);
                    390: 
                    391:   if(data->set.upload)
                    392:     return file_upload(conn);
                    393: 
                    394:   file = conn->data->req.protop;
                    395: 
                    396:   /* get the fd from the connection phase */
                    397:   fd = file->fd;
                    398: 
                    399:   /* VMS: This only works reliable for STREAMLF files */
                    400:   if(-1 != fstat(fd, &statbuf)) {
                    401:     /* we could stat it, then read out the size */
                    402:     expected_size = statbuf.st_size;
                    403:     /* and store the modification time */
                    404:     data->info.filetime = statbuf.st_mtime;
                    405:     fstated = TRUE;
                    406:   }
                    407: 
                    408:   if(fstated && !data->state.range && data->set.timecondition) {
                    409:     if(!Curl_meets_timecondition(data, data->info.filetime)) {
                    410:       *done = TRUE;
                    411:       return CURLE_OK;
                    412:     }
                    413:   }
                    414: 
                    415:   if(fstated) {
                    416:     time_t filetime;
                    417:     struct tm buffer;
                    418:     const struct tm *tm = &buffer;
                    419:     char header[80];
                    420:     msnprintf(header, sizeof(header),
                    421:               "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n",
                    422:               expected_size);
                    423:     result = Curl_client_write(conn, CLIENTWRITE_HEADER, header, 0);
                    424:     if(result)
                    425:       return result;
                    426: 
                    427:     result = Curl_client_write(conn, CLIENTWRITE_HEADER,
                    428:                                (char *)"Accept-ranges: bytes\r\n", 0);
                    429:     if(result)
                    430:       return result;
                    431: 
                    432:     filetime = (time_t)statbuf.st_mtime;
                    433:     result = Curl_gmtime(filetime, &buffer);
                    434:     if(result)
                    435:       return result;
                    436: 
                    437:     /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
                    438:     msnprintf(header, sizeof(header),
                    439:               "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s",
                    440:               Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
                    441:               tm->tm_mday,
                    442:               Curl_month[tm->tm_mon],
                    443:               tm->tm_year + 1900,
                    444:               tm->tm_hour,
                    445:               tm->tm_min,
                    446:               tm->tm_sec,
                    447:               data->set.opt_no_body ? "": "\r\n");
                    448:     result = Curl_client_write(conn, CLIENTWRITE_HEADER, header, 0);
                    449:     if(result)
                    450:       return result;
                    451:     /* set the file size to make it available post transfer */
                    452:     Curl_pgrsSetDownloadSize(data, expected_size);
                    453:     if(data->set.opt_no_body)
                    454:       return result;
                    455:   }
                    456: 
                    457:   /* Check whether file range has been specified */
                    458:   result = Curl_range(conn);
                    459:   if(result)
                    460:     return result;
                    461: 
                    462:   /* Adjust the start offset in case we want to get the N last bytes
                    463:    * of the stream if the filesize could be determined */
                    464:   if(data->state.resume_from < 0) {
                    465:     if(!fstated) {
                    466:       failf(data, "Can't get the size of file.");
                    467:       return CURLE_READ_ERROR;
                    468:     }
                    469:     data->state.resume_from += (curl_off_t)statbuf.st_size;
                    470:   }
                    471: 
                    472:   if(data->state.resume_from <= expected_size)
                    473:     expected_size -= data->state.resume_from;
                    474:   else {
                    475:     failf(data, "failed to resume file:// transfer");
                    476:     return CURLE_BAD_DOWNLOAD_RESUME;
                    477:   }
                    478: 
                    479:   /* A high water mark has been specified so we obey... */
                    480:   if(data->req.maxdownload > 0)
                    481:     expected_size = data->req.maxdownload;
                    482: 
                    483:   if(!fstated || (expected_size == 0))
                    484:     size_known = FALSE;
                    485:   else
                    486:     size_known = TRUE;
                    487: 
                    488:   /* The following is a shortcut implementation of file reading
                    489:      this is both more efficient than the former call to download() and
                    490:      it avoids problems with select() and recv() on file descriptors
                    491:      in Winsock */
                    492:   if(fstated)
                    493:     Curl_pgrsSetDownloadSize(data, expected_size);
                    494: 
                    495:   if(data->state.resume_from) {
                    496:     if(data->state.resume_from !=
                    497:        lseek(fd, data->state.resume_from, SEEK_SET))
                    498:       return CURLE_BAD_DOWNLOAD_RESUME;
                    499:   }
                    500: 
                    501:   Curl_pgrsTime(data, TIMER_STARTTRANSFER);
                    502: 
                    503:   while(!result) {
                    504:     ssize_t nread;
                    505:     /* Don't fill a whole buffer if we want less than all data */
                    506:     size_t bytestoread;
                    507: 
                    508:     if(size_known) {
                    509:       bytestoread = (expected_size < data->set.buffer_size) ?
                    510:         curlx_sotouz(expected_size) : (size_t)data->set.buffer_size;
                    511:     }
                    512:     else
                    513:       bytestoread = data->set.buffer_size-1;
                    514: 
                    515:     nread = read(fd, buf, bytestoread);
                    516: 
                    517:     if(nread > 0)
                    518:       buf[nread] = 0;
                    519: 
                    520:     if(nread <= 0 || (size_known && (expected_size == 0)))
                    521:       break;
                    522: 
                    523:     bytecount += nread;
                    524:     if(size_known)
                    525:       expected_size -= nread;
                    526: 
                    527:     result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
                    528:     if(result)
                    529:       return result;
                    530: 
                    531:     Curl_pgrsSetDownloadCounter(data, bytecount);
                    532: 
                    533:     if(Curl_pgrsUpdate(conn))
                    534:       result = CURLE_ABORTED_BY_CALLBACK;
                    535:     else
                    536:       result = Curl_speedcheck(data, Curl_now());
                    537:   }
                    538:   if(Curl_pgrsUpdate(conn))
                    539:     result = CURLE_ABORTED_BY_CALLBACK;
                    540: 
                    541:   return result;
                    542: }
                    543: 
                    544: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>