1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #ifndef CURL_DISABLE_FILE
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
33 #ifdef HAVE_ARPA_INET_H
34 #include <arpa/inet.h>
39 #ifdef HAVE_SYS_IOCTL_H
40 #include <sys/ioctl.h>
43 #ifdef HAVE_SYS_PARAM_H
44 #include <sys/param.h>
51 #include "strtoofft.h"
53 #include <curl/curl.h>
58 #include "speedcheck.h"
62 #include "parsedate.h" /* for the week day and month names */
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"
70 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
71 defined(__SYMBIAN32__)
72 #define DOS_FILESYSTEM 1
75 #ifdef OPEN_NEEDS_ARG3
76 # define open_readonly(p,f) open((p),(f),(0))
78 # define open_readonly(p,f) open((p),(f))
82 * Forward declarations.
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);
94 * FILE scheme handler.
97 const struct Curl_handler Curl_handler_file = {
99 file_setup_connection, /* setup_connection */
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 */
114 CURLPROTO_FILE, /* protocol */
115 PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
119 static CURLcode file_setup_connection(struct connectdata *conn)
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;
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
134 static CURLcode file_connect(struct connectdata *conn, bool *done)
136 struct Curl_easy *data = conn->data;
138 struct FILEPROTO *file = data->req.protop;
140 #ifdef DOS_FILESYSTEM
144 size_t real_path_len;
146 CURLcode result = Curl_urldecode(data, data->state.path, 0, &real_path,
147 &real_path_len, FALSE);
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
159 Some browsers accept | instead of : as the drive letter
160 separator, so we do too.
162 On other platforms, we need the slash to indicate an
163 absolute pathname. On Windows, absolute paths start
166 actual_path = real_path;
167 if((actual_path[0] == '/') &&
169 (actual_path[2] == ':' || actual_path[2] == '|')) {
170 actual_path[2] = ':';
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;
184 fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
185 file->path = actual_path;
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;
193 fd = open_readonly(real_path, O_RDONLY);
194 file->path = real_path;
196 file->freepath = real_path; /* free this when done */
199 if(!data->set.upload && (fd == -1)) {
200 failf(data, "Couldn't open file %s", data->state.path);
201 file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
202 return CURLE_FILE_COULDNT_READ_FILE;
209 static CURLcode file_done(struct connectdata *conn,
210 CURLcode status, bool premature)
212 struct FILEPROTO *file = conn->data->req.protop;
213 (void)status; /* not used */
214 (void)premature; /* not used */
217 Curl_safefree(file->freepath);
227 static CURLcode file_disconnect(struct connectdata *conn,
228 bool dead_connection)
230 struct FILEPROTO *file = conn->data->req.protop;
231 (void)dead_connection; /* not used */
234 Curl_safefree(file->freepath);
244 #ifdef DOS_FILESYSTEM
250 static CURLcode file_upload(struct connectdata *conn)
252 struct FILEPROTO *file = conn->data->req.protop;
253 const char *dir = strchr(file->path, DIRSEP);
256 CURLcode result = CURLE_OK;
257 struct Curl_easy *data = conn->data;
258 char *buf = data->state.buffer;
261 curl_off_t bytecount = 0;
262 struct_stat file_stat;
266 * Since FILE: doesn't do the full init, we need to provide some extra
269 conn->data->req.upload_fromhere = buf;
272 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
275 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
278 #define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
280 #define MODE_DEFAULT O_WRONLY|O_CREAT
283 if(data->state.resume_from)
284 mode = MODE_DEFAULT|O_APPEND;
286 mode = MODE_DEFAULT|O_TRUNC;
288 fd = open(file->path, mode, conn->data->set.new_file_perms);
290 failf(data, "Can't open %s for writing", file->path);
291 return CURLE_WRITE_ERROR;
294 if(-1 != data->state.infilesize)
295 /* known size of data to "upload" */
296 Curl_pgrsSetUploadSize(data, data->state.infilesize);
298 /* treat the negative resume offset value as the case of "-" */
299 if(data->state.resume_from < 0) {
300 if(fstat(fd, &file_stat)) {
302 failf(data, "Can't get the size of %s", file->path);
303 return CURLE_WRITE_ERROR;
305 data->state.resume_from = (curl_off_t)file_stat.st_size;
310 result = Curl_fillreadbuffer(conn, (int)data->set.buffer_size, &readcount);
314 if(readcount <= 0) /* fix questionable compare error. curlvms */
317 nread = (size_t)readcount;
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;
327 buf2 = buf + data->state.resume_from;
328 nread -= (size_t)data->state.resume_from;
329 data->state.resume_from = 0;
335 /* write the data to the target */
336 nwrite = write(fd, buf2, nread);
337 if(nwrite != nread) {
338 result = CURLE_SEND_ERROR;
344 Curl_pgrsSetUploadCounter(data, bytecount);
346 if(Curl_pgrsUpdate(conn))
347 result = CURLE_ABORTED_BY_CALLBACK;
349 result = Curl_speedcheck(data, Curl_now());
351 if(!result && Curl_pgrsUpdate(conn))
352 result = CURLE_ABORTED_BY_CALLBACK;
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
367 static CURLcode file_do(struct connectdata *conn, bool *done)
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
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;
380 bool fstated = FALSE;
382 struct Curl_easy *data = conn->data;
383 char *buf = data->state.buffer;
384 curl_off_t bytecount = 0;
386 struct FILEPROTO *file;
388 *done = TRUE; /* unconditionally */
391 Curl_pgrsStartNow(data);
394 return file_upload(conn);
396 file = conn->data->req.protop;
398 /* get the fd from the connection phase */
401 /* VMS: This only works reliable for STREAMLF files */
402 if(-1 != fstat(fd, &statbuf)) {
403 /* we could stat it, then read out the size */
404 expected_size = statbuf.st_size;
405 /* and store the modification time */
406 data->info.filetime = statbuf.st_mtime;
410 if(fstated && !data->state.range && data->set.timecondition) {
411 if(!Curl_meets_timecondition(data, data->info.filetime)) {
417 /* If we have selected NOBODY and HEADER, it means that we only want file
418 information. Which for FILE can't be much more than the file size and
420 if(data->set.opt_no_body && data->set.include_header && fstated) {
423 const struct tm *tm = &buffer;
425 snprintf(header, sizeof(header),
426 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
427 result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0);
431 result = Curl_client_write(conn, CLIENTWRITE_BOTH,
432 (char *)"Accept-ranges: bytes\r\n", 0);
436 filetime = (time_t)statbuf.st_mtime;
437 result = Curl_gmtime(filetime, &buffer);
441 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
442 snprintf(header, sizeof(header),
443 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
444 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
446 Curl_month[tm->tm_mon],
451 result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0);
453 /* set the file size to make it available post transfer */
454 Curl_pgrsSetDownloadSize(data, expected_size);
458 /* Check whether file range has been specified */
459 result = Curl_range(conn);
463 /* Adjust the start offset in case we want to get the N last bytes
464 * of the stream iff the filesize could be determined */
465 if(data->state.resume_from < 0) {
467 failf(data, "Can't get the size of file.");
468 return CURLE_READ_ERROR;
470 data->state.resume_from += (curl_off_t)statbuf.st_size;
473 if(data->state.resume_from <= expected_size)
474 expected_size -= data->state.resume_from;
476 failf(data, "failed to resume file:// transfer");
477 return CURLE_BAD_DOWNLOAD_RESUME;
480 /* A high water mark has been specified so we obey... */
481 if(data->req.maxdownload > 0)
482 expected_size = data->req.maxdownload;
484 if(!fstated || (expected_size == 0))
489 /* The following is a shortcut implementation of file reading
490 this is both more efficient than the former call to download() and
491 it avoids problems with select() and recv() on file descriptors
494 Curl_pgrsSetDownloadSize(data, expected_size);
496 if(data->state.resume_from) {
497 if(data->state.resume_from !=
498 lseek(fd, data->state.resume_from, SEEK_SET))
499 return CURLE_BAD_DOWNLOAD_RESUME;
502 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
505 /* Don't fill a whole buffer if we want less than all data */
509 bytestoread = (expected_size < data->set.buffer_size) ?
510 curlx_sotouz(expected_size) : (size_t)data->set.buffer_size;
513 bytestoread = data->set.buffer_size-1;
515 nread = read(fd, buf, bytestoread);
520 if(nread <= 0 || (size_known && (expected_size == 0)))
525 expected_size -= nread;
527 result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
531 Curl_pgrsSetDownloadCounter(data, bytecount);
533 if(Curl_pgrsUpdate(conn))
534 result = CURLE_ABORTED_BY_CALLBACK;
536 result = Curl_speedcheck(data, Curl_now());
538 if(Curl_pgrsUpdate(conn))
539 result = CURLE_ABORTED_BY_CALLBACK;