Annotation of embedaddon/curl/lib/file.c, revision 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>