blob: db04cc2daf79939182662865d7250e1d4c96352b [file] [log] [blame]
Kristian Monsen5ab50182010-05-14 18:53:44 +01001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
Elliott Hughescac39802018-04-27 16:19:43 -07008 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
Kristian Monsen5ab50182010-05-14 18:53:44 +01009 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
Alex Deymod15eaac2016-06-28 14:49:26 -070012 * are also available at https://curl.haxx.se/docs/copyright.html.
Kristian Monsen5ab50182010-05-14 18:53:44 +010013 *
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
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070023#include "curl_setup.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010024
25#ifndef CURL_DISABLE_FILE
Kristian Monsen5ab50182010-05-14 18:53:44 +010026
Kristian Monsen5ab50182010-05-14 18:53:44 +010027#ifdef HAVE_NETINET_IN_H
28#include <netinet/in.h>
29#endif
Kristian Monsen5ab50182010-05-14 18:53:44 +010030#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
Kristian Monsen5ab50182010-05-14 18:53:44 +010051#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"
Kristian Monsen5ab50182010-05-14 18:53:44 +010062#include "parsedate.h" /* for the week day and month names */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070063#include "warnless.h"
Elliott Hughescac39802018-04-27 16:19:43 -070064#include "curl_range.h"
Alex Deymod15eaac2016-06-28 14:49:26 -070065/* The last 3 #include files should be in this order */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070066#include "curl_printf.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070067#include "curl_memory.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010068#include "memdebug.h"
69
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070070#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
71 defined(__SYMBIAN32__)
Kristian Monsen5ab50182010-05-14 18:53:44 +010072#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
85static CURLcode file_do(struct connectdata *, bool *done);
86static CURLcode file_done(struct connectdata *conn,
87 CURLcode status, bool premature);
88static CURLcode file_connect(struct connectdata *conn, bool *done);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070089static CURLcode file_disconnect(struct connectdata *conn,
90 bool dead_connection);
91static CURLcode file_setup_connection(struct connectdata *conn);
Kristian Monsen5ab50182010-05-14 18:53:44 +010092
93/*
94 * FILE scheme handler.
95 */
96
97const struct Curl_handler Curl_handler_file = {
98 "FILE", /* scheme */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070099 file_setup_connection, /* setup_connection */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100100 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 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700108 ZERO_NULL, /* domore_getsock */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100109 ZERO_NULL, /* perform_getsock */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700110 file_disconnect, /* disconnect */
111 ZERO_NULL, /* readwrite */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700112 ZERO_NULL, /* connection_check */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100113 0, /* defport */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700114 CURLPROTO_FILE, /* protocol */
115 PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100116};
117
118
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700119static 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
Kristian Monsen5ab50182010-05-14 18:53:44 +0100129/*
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 */
134static CURLcode file_connect(struct connectdata *conn, bool *done)
135{
Alex Deymoe3149cc2016-10-05 11:18:42 -0700136 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700137 char *real_path;
138 struct FILEPROTO *file = data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100139 int fd;
140#ifdef DOS_FILESYSTEM
Elliott Hughescee03382017-06-23 12:17:18 -0700141 size_t i;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100142 char *actual_path;
143#endif
Elliott Hughescee03382017-06-23 12:17:18 -0700144 size_t real_path_len;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100145
Elliott Hughescee03382017-06-23 12:17:18 -0700146 CURLcode result = Curl_urldecode(data, data->state.path, 0, &real_path,
147 &real_path_len, FALSE);
148 if(result)
149 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100150
Kristian Monsen5ab50182010-05-14 18:53:44 +0100151#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] &&
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700169 (actual_path[2] == ':' || actual_path[2] == '|')) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100170 actual_path[2] = ':';
171 actual_path++;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700172 real_path_len--;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100173 }
174
175 /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
Alex Deymo486467e2017-12-19 19:04:07 +0100176 for(i = 0; i < real_path_len; ++i)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100177 if(actual_path[i] == '/')
178 actual_path[i] = '\\';
Alex Deymoe3149cc2016-10-05 11:18:42 -0700179 else if(!actual_path[i]) { /* binary zero */
180 Curl_safefree(real_path);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700181 return CURLE_URL_MALFORMAT;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700182 }
Kristian Monsen5ab50182010-05-14 18:53:44 +0100183
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700184 fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100185 file->path = actual_path;
186#else
Alex Deymoe3149cc2016-10-05 11:18:42 -0700187 if(memchr(real_path, 0, real_path_len)) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700188 /* binary zeroes indicate foul play */
Alex Deymoe3149cc2016-10-05 11:18:42 -0700189 Curl_safefree(real_path);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700190 return CURLE_URL_MALFORMAT;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700191 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700192
Kristian Monsen5ab50182010-05-14 18:53:44 +0100193 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.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
209static CURLcode file_done(struct connectdata *conn,
210 CURLcode status, bool premature)
211{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700212 struct FILEPROTO *file = conn->data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100213 (void)status; /* not used */
214 (void)premature; /* not used */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100215
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700216 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
227static 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 }
Kristian Monsen5ab50182010-05-14 18:53:44 +0100240
241 return CURLE_OK;
242}
243
244#ifdef DOS_FILESYSTEM
245#define DIRSEP '\\'
246#else
247#define DIRSEP '/'
248#endif
249
250static CURLcode file_upload(struct connectdata *conn)
251{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700252 struct FILEPROTO *file = conn->data->req.protop;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100253 const char *dir = strchr(file->path, DIRSEP);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700254 int fd;
255 int mode;
256 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700257 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100258 char *buf = data->state.buffer;
259 size_t nread;
260 size_t nwrite;
261 curl_off_t bytecount = 0;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100262 struct_stat file_stat;
Elliott Hughescee03382017-06-23 12:17:18 -0700263 const char *buf2;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100264
265 /*
266 * Since FILE: doesn't do the full init, we need to provide some extra
267 * assignments here.
268 */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100269 conn->data->req.upload_fromhere = buf;
270
271 if(!dir)
272 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
273
274 if(!dir[1])
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700275 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
276
277#ifdef O_BINARY
278#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
279#else
280#define MODE_DEFAULT O_WRONLY|O_CREAT
281#endif
Kristian Monsen5ab50182010-05-14 18:53:44 +0100282
283 if(data->state.resume_from)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700284 mode = MODE_DEFAULT|O_APPEND;
285 else
286 mode = MODE_DEFAULT|O_TRUNC;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100287
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700288 fd = open(file->path, mode, conn->data->set.new_file_perms);
289 if(fd < 0) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100290 failf(data, "Can't open %s for writing", file->path);
291 return CURLE_WRITE_ERROR;
292 }
293
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700294 if(-1 != data->state.infilesize)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100295 /* known size of data to "upload" */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700296 Curl_pgrsSetUploadSize(data, data->state.infilesize);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100297
298 /* treat the negative resume offset value as the case of "-" */
299 if(data->state.resume_from < 0) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700300 if(fstat(fd, &file_stat)) {
301 close(fd);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100302 failf(data, "Can't get the size of %s", file->path);
303 return CURLE_WRITE_ERROR;
304 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700305 data->state.resume_from = (curl_off_t)file_stat.st_size;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100306 }
307
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700308 while(!result) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100309 int readcount;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700310 result = Curl_fillreadbuffer(conn, (int)data->set.buffer_size, &readcount);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700311 if(result)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100312 break;
313
314 if(readcount <= 0) /* fix questionable compare error. curlvms */
315 break;
316
317 nread = (size_t)readcount;
318
319 /*skip bytes before resume point*/
320 if(data->state.resume_from) {
Alex Deymod15eaac2016-06-28 14:49:26 -0700321 if((curl_off_t)nread <= data->state.resume_from) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100322 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 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700336 nwrite = write(fd, buf2, nread);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100337 if(nwrite != nread) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700338 result = CURLE_SEND_ERROR;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100339 break;
340 }
341
342 bytecount += nread;
343
344 Curl_pgrsSetUploadCounter(data, bytecount);
345
346 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700347 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100348 else
Alex Deymo486467e2017-12-19 19:04:07 +0100349 result = Curl_speedcheck(data, Curl_now());
Kristian Monsen5ab50182010-05-14 18:53:44 +0100350 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700351 if(!result && Curl_pgrsUpdate(conn))
352 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100353
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700354 close(fd);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100355
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700356 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100357}
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 */
367static 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 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700374 CURLcode result = CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100375 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' */
Alex Deymo486467e2017-12-19 19:04:07 +0100378 curl_off_t expected_size = 0;
Alex Deymod15eaac2016-06-28 14:49:26 -0700379 bool size_known;
Alex Deymo486467e2017-12-19 19:04:07 +0100380 bool fstated = FALSE;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100381 ssize_t nread;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700382 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100383 char *buf = data->state.buffer;
384 curl_off_t bytecount = 0;
385 int fd;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700386 struct FILEPROTO *file;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100387
388 *done = TRUE; /* unconditionally */
389
390 Curl_initinfo(data);
391 Curl_pgrsStartNow(data);
392
393 if(data->set.upload)
394 return file_upload(conn);
395
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700396 file = conn->data->req.protop;
397
Kristian Monsen5ab50182010-05-14 18:53:44 +0100398 /* get the fd from the connection phase */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700399 fd = file->fd;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100400
401 /* VMS: This only works reliable for STREAMLF files */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700402 if(-1 != fstat(fd, &statbuf)) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100403 /* we could stat it, then read out the size */
404 expected_size = statbuf.st_size;
405 /* and store the modification time */
Elliott Hughescac39802018-04-27 16:19:43 -0700406 data->info.filetime = statbuf.st_mtime;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100407 fstated = TRUE;
408 }
409
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700410 if(fstated && !data->state.range && data->set.timecondition) {
Elliott Hughescac39802018-04-27 16:19:43 -0700411 if(!Curl_meets_timecondition(data, data->info.filetime)) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700412 *done = TRUE;
413 return CURLE_OK;
414 }
415 }
416
Kristian Monsen5ab50182010-05-14 18:53:44 +0100417 /* 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
419 date. */
420 if(data->set.opt_no_body && data->set.include_header && fstated) {
Alex Deymod15eaac2016-06-28 14:49:26 -0700421 time_t filetime;
422 struct tm buffer;
423 const struct tm *tm = &buffer;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700424 char header[80];
425 snprintf(header, sizeof(header),
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700426 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
Elliott Hughes82be86d2017-09-20 17:00:17 -0700427 result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100428 if(result)
429 return result;
430
431 result = Curl_client_write(conn, CLIENTWRITE_BOTH,
432 (char *)"Accept-ranges: bytes\r\n", 0);
433 if(result)
434 return result;
435
Alex Deymod15eaac2016-06-28 14:49:26 -0700436 filetime = (time_t)statbuf.st_mtime;
437 result = Curl_gmtime(filetime, &buffer);
438 if(result)
439 return result;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700440
Alex Deymod15eaac2016-06-28 14:49:26 -0700441 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700442 snprintf(header, sizeof(header),
Alex Deymod15eaac2016-06-28 14:49:26 -0700443 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
444 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
445 tm->tm_mday,
446 Curl_month[tm->tm_mon],
447 tm->tm_year + 1900,
448 tm->tm_hour,
449 tm->tm_min,
450 tm->tm_sec);
Elliott Hughes82be86d2017-09-20 17:00:17 -0700451 result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0);
Alex Deymod15eaac2016-06-28 14:49:26 -0700452 if(!result)
453 /* set the file size to make it available post transfer */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100454 Curl_pgrsSetDownloadSize(data, expected_size);
455 return result;
456 }
457
458 /* Check whether file range has been specified */
Elliott Hughescac39802018-04-27 16:19:43 -0700459 result = Curl_range(conn);
460 if(result)
461 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100462
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) {
466 if(!fstated) {
467 failf(data, "Can't get the size of file.");
468 return CURLE_READ_ERROR;
469 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700470 data->state.resume_from += (curl_off_t)statbuf.st_size;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100471 }
472
473 if(data->state.resume_from <= expected_size)
474 expected_size -= data->state.resume_from;
475 else {
476 failf(data, "failed to resume file:// transfer");
477 return CURLE_BAD_DOWNLOAD_RESUME;
478 }
479
480 /* A high water mark has been specified so we obey... */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700481 if(data->req.maxdownload > 0)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100482 expected_size = data->req.maxdownload;
483
Alex Deymod15eaac2016-06-28 14:49:26 -0700484 if(!fstated || (expected_size == 0))
485 size_known = FALSE;
486 else
487 size_known = TRUE;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100488
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
492 in Winsock */
493 if(fstated)
494 Curl_pgrsSetDownloadSize(data, expected_size);
495
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;
500 }
501
502 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
503
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700504 while(!result) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100505 /* Don't fill a whole buffer if we want less than all data */
Alex Deymod15eaac2016-06-28 14:49:26 -0700506 size_t bytestoread;
507
508 if(size_known) {
Elliott Hughes82be86d2017-09-20 17:00:17 -0700509 bytestoread = (expected_size < data->set.buffer_size) ?
510 curlx_sotouz(expected_size) : (size_t)data->set.buffer_size;
Alex Deymod15eaac2016-06-28 14:49:26 -0700511 }
512 else
Elliott Hughes82be86d2017-09-20 17:00:17 -0700513 bytestoread = data->set.buffer_size-1;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700514
Kristian Monsen5ab50182010-05-14 18:53:44 +0100515 nread = read(fd, buf, bytestoread);
516
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700517 if(nread > 0)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100518 buf[nread] = 0;
519
Alex Deymod15eaac2016-06-28 14:49:26 -0700520 if(nread <= 0 || (size_known && (expected_size == 0)))
Kristian Monsen5ab50182010-05-14 18:53:44 +0100521 break;
522
523 bytecount += nread;
Alex Deymod15eaac2016-06-28 14:49:26 -0700524 if(size_known)
525 expected_size -= nread;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100526
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700527 result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
528 if(result)
529 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100530
531 Curl_pgrsSetDownloadCounter(data, bytecount);
532
533 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700534 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100535 else
Alex Deymo486467e2017-12-19 19:04:07 +0100536 result = Curl_speedcheck(data, Curl_now());
Kristian Monsen5ab50182010-05-14 18:53:44 +0100537 }
538 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700539 result = CURLE_ABORTED_BY_CALLBACK;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100540
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700541 return result;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100542}
543
544#endif