Revert "Imported Upstream version 7.53.1"
[platform/upstream/curl.git] / lib / file.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2016, 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 /* The last 3 #include files should be in this order */
65 #include "curl_printf.h"
66 #include "curl_memory.h"
67 #include "memdebug.h"
68
69 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
70   defined(__SYMBIAN32__)
71 #define DOS_FILESYSTEM 1
72 #endif
73
74 #ifdef OPEN_NEEDS_ARG3
75 #  define open_readonly(p,f) open((p),(f),(0))
76 #else
77 #  define open_readonly(p,f) open((p),(f))
78 #endif
79
80 /*
81  * Forward declarations.
82  */
83
84 static CURLcode file_do(struct connectdata *, bool *done);
85 static CURLcode file_done(struct connectdata *conn,
86                           CURLcode status, bool premature);
87 static CURLcode file_connect(struct connectdata *conn, bool *done);
88 static CURLcode file_disconnect(struct connectdata *conn,
89                                 bool dead_connection);
90 static CURLcode file_setup_connection(struct connectdata *conn);
91
92 /*
93  * FILE scheme handler.
94  */
95
96 const struct Curl_handler Curl_handler_file = {
97   "FILE",                               /* scheme */
98   file_setup_connection,                /* setup_connection */
99   file_do,                              /* do_it */
100   file_done,                            /* done */
101   ZERO_NULL,                            /* do_more */
102   file_connect,                         /* connect_it */
103   ZERO_NULL,                            /* connecting */
104   ZERO_NULL,                            /* doing */
105   ZERO_NULL,                            /* proto_getsock */
106   ZERO_NULL,                            /* doing_getsock */
107   ZERO_NULL,                            /* domore_getsock */
108   ZERO_NULL,                            /* perform_getsock */
109   file_disconnect,                      /* disconnect */
110   ZERO_NULL,                            /* readwrite */
111   0,                                    /* defport */
112   CURLPROTO_FILE,                       /* protocol */
113   PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
114 };
115
116
117 static CURLcode file_setup_connection(struct connectdata *conn)
118 {
119   /* allocate the FILE specific struct */
120   conn->data->req.protop = calloc(1, sizeof(struct FILEPROTO));
121   if(!conn->data->req.protop)
122     return CURLE_OUT_OF_MEMORY;
123
124   return CURLE_OK;
125 }
126
127  /*
128   Check if this is a range download, and if so, set the internal variables
129   properly. This code is copied from the FTP implementation and might as
130   well be factored out.
131  */
132 static CURLcode file_range(struct connectdata *conn)
133 {
134   curl_off_t from, to;
135   curl_off_t totalsize=-1;
136   char *ptr;
137   char *ptr2;
138   struct Curl_easy *data = conn->data;
139
140   if(data->state.use_range && data->state.range) {
141     from=curlx_strtoofft(data->state.range, &ptr, 0);
142     while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
143       ptr++;
144     to=curlx_strtoofft(ptr, &ptr2, 0);
145     if(ptr == ptr2) {
146       /* we didn't get any digit */
147       to=-1;
148     }
149     if((-1 == to) && (from>=0)) {
150       /* X - */
151       data->state.resume_from = from;
152       DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n",
153                    from));
154     }
155     else if(from < 0) {
156       /* -Y */
157       data->req.maxdownload = -from;
158       data->state.resume_from = from;
159       DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n",
160                    -from));
161     }
162     else {
163       /* X-Y */
164       totalsize = to-from;
165       data->req.maxdownload = totalsize+1; /* include last byte */
166       data->state.resume_from = from;
167       DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T
168                    " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n",
169                    from, data->req.maxdownload));
170     }
171     DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T
172                  " to %" CURL_FORMAT_CURL_OFF_T ", totally %"
173                  CURL_FORMAT_CURL_OFF_T " bytes\n",
174                  from, to, data->req.maxdownload));
175   }
176   else
177     data->req.maxdownload = -1;
178   return CURLE_OK;
179 }
180
181 /*
182  * file_connect() gets called from Curl_protocol_connect() to allow us to
183  * do protocol-specific actions at connect-time.  We emulate a
184  * connect-then-transfer protocol and "connect" to the file here
185  */
186 static CURLcode file_connect(struct connectdata *conn, bool *done)
187 {
188   struct Curl_easy *data = conn->data;
189   char *real_path;
190   struct FILEPROTO *file = data->req.protop;
191   int fd;
192 #ifdef DOS_FILESYSTEM
193   int i;
194   char *actual_path;
195 #endif
196   int real_path_len;
197
198   real_path = curl_easy_unescape(data, data->state.path, 0, &real_path_len);
199   if(!real_path)
200     return CURLE_OUT_OF_MEMORY;
201
202 #ifdef DOS_FILESYSTEM
203   /* If the first character is a slash, and there's
204      something that looks like a drive at the beginning of
205      the path, skip the slash.  If we remove the initial
206      slash in all cases, paths without drive letters end up
207      relative to the current directory which isn't how
208      browsers work.
209
210      Some browsers accept | instead of : as the drive letter
211      separator, so we do too.
212
213      On other platforms, we need the slash to indicate an
214      absolute pathname.  On Windows, absolute paths start
215      with a drive letter.
216   */
217   actual_path = real_path;
218   if((actual_path[0] == '/') &&
219       actual_path[1] &&
220      (actual_path[2] == ':' || actual_path[2] == '|')) {
221     actual_path[2] = ':';
222     actual_path++;
223     real_path_len--;
224   }
225
226   /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
227   for(i=0; i < real_path_len; ++i)
228     if(actual_path[i] == '/')
229       actual_path[i] = '\\';
230     else if(!actual_path[i]) { /* binary zero */
231       Curl_safefree(real_path);
232       return CURLE_URL_MALFORMAT;
233     }
234
235   fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
236   file->path = actual_path;
237 #else
238   if(memchr(real_path, 0, real_path_len)) {
239     /* binary zeroes indicate foul play */
240     Curl_safefree(real_path);
241     return CURLE_URL_MALFORMAT;
242   }
243
244   fd = open_readonly(real_path, O_RDONLY);
245   file->path = real_path;
246 #endif
247   file->freepath = real_path; /* free this when done */
248
249   file->fd = fd;
250   if(!data->set.upload && (fd == -1)) {
251     failf(data, "Couldn't open file %s", data->state.path);
252     file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
253     return CURLE_FILE_COULDNT_READ_FILE;
254   }
255   *done = TRUE;
256
257   return CURLE_OK;
258 }
259
260 static CURLcode file_done(struct connectdata *conn,
261                                CURLcode status, bool premature)
262 {
263   struct FILEPROTO *file = conn->data->req.protop;
264   (void)status; /* not used */
265   (void)premature; /* not used */
266
267   if(file) {
268     Curl_safefree(file->freepath);
269     file->path = NULL;
270     if(file->fd != -1)
271       close(file->fd);
272     file->fd = -1;
273   }
274
275   return CURLE_OK;
276 }
277
278 static CURLcode file_disconnect(struct connectdata *conn,
279                                 bool dead_connection)
280 {
281   struct FILEPROTO *file = conn->data->req.protop;
282   (void)dead_connection; /* not used */
283
284   if(file) {
285     Curl_safefree(file->freepath);
286     file->path = NULL;
287     if(file->fd != -1)
288       close(file->fd);
289     file->fd = -1;
290   }
291
292   return CURLE_OK;
293 }
294
295 #ifdef DOS_FILESYSTEM
296 #define DIRSEP '\\'
297 #else
298 #define DIRSEP '/'
299 #endif
300
301 static CURLcode file_upload(struct connectdata *conn)
302 {
303   struct FILEPROTO *file = conn->data->req.protop;
304   const char *dir = strchr(file->path, DIRSEP);
305   int fd;
306   int mode;
307   CURLcode result = CURLE_OK;
308   struct Curl_easy *data = conn->data;
309   char *buf = data->state.buffer;
310   size_t nread;
311   size_t nwrite;
312   curl_off_t bytecount = 0;
313   struct timeval now = Curl_tvnow();
314   struct_stat file_stat;
315   const char* buf2;
316
317   /*
318    * Since FILE: doesn't do the full init, we need to provide some extra
319    * assignments here.
320    */
321   conn->data->req.upload_fromhere = buf;
322
323   if(!dir)
324     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
325
326   if(!dir[1])
327     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
328
329 #ifdef O_BINARY
330 #define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
331 #else
332 #define MODE_DEFAULT O_WRONLY|O_CREAT
333 #endif
334
335   if(data->state.resume_from)
336     mode = MODE_DEFAULT|O_APPEND;
337   else
338     mode = MODE_DEFAULT|O_TRUNC;
339
340   fd = open(file->path, mode, conn->data->set.new_file_perms);
341   if(fd < 0) {
342     failf(data, "Can't open %s for writing", file->path);
343     return CURLE_WRITE_ERROR;
344   }
345
346   if(-1 != data->state.infilesize)
347     /* known size of data to "upload" */
348     Curl_pgrsSetUploadSize(data, data->state.infilesize);
349
350   /* treat the negative resume offset value as the case of "-" */
351   if(data->state.resume_from < 0) {
352     if(fstat(fd, &file_stat)) {
353       close(fd);
354       failf(data, "Can't get the size of %s", file->path);
355       return CURLE_WRITE_ERROR;
356     }
357     else
358       data->state.resume_from = (curl_off_t)file_stat.st_size;
359   }
360
361   while(!result) {
362     int readcount;
363     result = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
364     if(result)
365       break;
366
367     if(readcount <= 0)  /* fix questionable compare error. curlvms */
368       break;
369
370     nread = (size_t)readcount;
371
372     /*skip bytes before resume point*/
373     if(data->state.resume_from) {
374       if((curl_off_t)nread <= data->state.resume_from) {
375         data->state.resume_from -= nread;
376         nread = 0;
377         buf2 = buf;
378       }
379       else {
380         buf2 = buf + data->state.resume_from;
381         nread -= (size_t)data->state.resume_from;
382         data->state.resume_from = 0;
383       }
384     }
385     else
386       buf2 = buf;
387
388     /* write the data to the target */
389     nwrite = write(fd, buf2, nread);
390     if(nwrite != nread) {
391       result = CURLE_SEND_ERROR;
392       break;
393     }
394
395     bytecount += nread;
396
397     Curl_pgrsSetUploadCounter(data, bytecount);
398
399     if(Curl_pgrsUpdate(conn))
400       result = CURLE_ABORTED_BY_CALLBACK;
401     else
402       result = Curl_speedcheck(data, now);
403   }
404   if(!result && Curl_pgrsUpdate(conn))
405     result = CURLE_ABORTED_BY_CALLBACK;
406
407   close(fd);
408
409   return result;
410 }
411
412 /*
413  * file_do() is the protocol-specific function for the do-phase, separated
414  * from the connect-phase above. Other protocols merely setup the transfer in
415  * the do-phase, to have it done in the main transfer loop but since some
416  * platforms we support don't allow select()ing etc on file handles (as
417  * opposed to sockets) we instead perform the whole do-operation in this
418  * function.
419  */
420 static CURLcode file_do(struct connectdata *conn, bool *done)
421 {
422   /* This implementation ignores the host name in conformance with
423      RFC 1738. Only local files (reachable via the standard file system)
424      are supported. This means that files on remotely mounted directories
425      (via NFS, Samba, NT sharing) can be accessed through a file:// URL
426   */
427   CURLcode result = CURLE_OK;
428   struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
429                           Windows version to have a different struct without
430                           having to redefine the simple word 'stat' */
431   curl_off_t expected_size=0;
432   bool size_known;
433   bool fstated=FALSE;
434   ssize_t nread;
435   struct Curl_easy *data = conn->data;
436   char *buf = data->state.buffer;
437   curl_off_t bytecount = 0;
438   int fd;
439   struct timeval now = Curl_tvnow();
440   struct FILEPROTO *file;
441
442   *done = TRUE; /* unconditionally */
443
444   Curl_initinfo(data);
445   Curl_pgrsStartNow(data);
446
447   if(data->set.upload)
448     return file_upload(conn);
449
450   file = conn->data->req.protop;
451
452   /* get the fd from the connection phase */
453   fd = file->fd;
454
455   /* VMS: This only works reliable for STREAMLF files */
456   if(-1 != fstat(fd, &statbuf)) {
457     /* we could stat it, then read out the size */
458     expected_size = statbuf.st_size;
459     /* and store the modification time */
460     data->info.filetime = (long)statbuf.st_mtime;
461     fstated = TRUE;
462   }
463
464   if(fstated && !data->state.range && data->set.timecondition) {
465     if(!Curl_meets_timecondition(data, (time_t)data->info.filetime)) {
466       *done = TRUE;
467       return CURLE_OK;
468     }
469   }
470
471   /* If we have selected NOBODY and HEADER, it means that we only want file
472      information. Which for FILE can't be much more than the file size and
473      date. */
474   if(data->set.opt_no_body && data->set.include_header && fstated) {
475     time_t filetime;
476     struct tm buffer;
477     const struct tm *tm = &buffer;
478     snprintf(buf, sizeof(data->state.buffer),
479              "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
480     result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
481     if(result)
482       return result;
483
484     result = Curl_client_write(conn, CLIENTWRITE_BOTH,
485                                (char *)"Accept-ranges: bytes\r\n", 0);
486     if(result)
487       return result;
488
489     filetime = (time_t)statbuf.st_mtime;
490     result = Curl_gmtime(filetime, &buffer);
491     if(result)
492       return result;
493
494     /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
495     snprintf(buf, BUFSIZE-1,
496              "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
497              Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
498              tm->tm_mday,
499              Curl_month[tm->tm_mon],
500              tm->tm_year + 1900,
501              tm->tm_hour,
502              tm->tm_min,
503              tm->tm_sec);
504     result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
505     if(!result)
506       /* set the file size to make it available post transfer */
507       Curl_pgrsSetDownloadSize(data, expected_size);
508     return result;
509   }
510
511   /* Check whether file range has been specified */
512   file_range(conn);
513
514   /* Adjust the start offset in case we want to get the N last bytes
515    * of the stream iff the filesize could be determined */
516   if(data->state.resume_from < 0) {
517     if(!fstated) {
518       failf(data, "Can't get the size of file.");
519       return CURLE_READ_ERROR;
520     }
521     else
522       data->state.resume_from += (curl_off_t)statbuf.st_size;
523   }
524
525   if(data->state.resume_from <= expected_size)
526     expected_size -= data->state.resume_from;
527   else {
528     failf(data, "failed to resume file:// transfer");
529     return CURLE_BAD_DOWNLOAD_RESUME;
530   }
531
532   /* A high water mark has been specified so we obey... */
533   if(data->req.maxdownload > 0)
534     expected_size = data->req.maxdownload;
535
536   if(!fstated || (expected_size == 0))
537     size_known = FALSE;
538   else
539     size_known = TRUE;
540
541   /* The following is a shortcut implementation of file reading
542      this is both more efficient than the former call to download() and
543      it avoids problems with select() and recv() on file descriptors
544      in Winsock */
545   if(fstated)
546     Curl_pgrsSetDownloadSize(data, expected_size);
547
548   if(data->state.resume_from) {
549     if(data->state.resume_from !=
550        lseek(fd, data->state.resume_from, SEEK_SET))
551       return CURLE_BAD_DOWNLOAD_RESUME;
552   }
553
554   Curl_pgrsTime(data, TIMER_STARTTRANSFER);
555
556   while(!result) {
557     /* Don't fill a whole buffer if we want less than all data */
558     size_t bytestoread;
559
560     if(size_known) {
561       bytestoread =
562         (expected_size < CURL_OFF_T_C(BUFSIZE) - CURL_OFF_T_C(1)) ?
563         curlx_sotouz(expected_size) : BUFSIZE - 1;
564     }
565     else
566       bytestoread = BUFSIZE-1;
567
568     nread = read(fd, buf, bytestoread);
569
570     if(nread > 0)
571       buf[nread] = 0;
572
573     if(nread <= 0 || (size_known && (expected_size == 0)))
574       break;
575
576     bytecount += nread;
577     if(size_known)
578       expected_size -= nread;
579
580     result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
581     if(result)
582       return result;
583
584     Curl_pgrsSetDownloadCounter(data, bytecount);
585
586     if(Curl_pgrsUpdate(conn))
587       result = CURLE_ABORTED_BY_CALLBACK;
588     else
589       result = Curl_speedcheck(data, now);
590   }
591   if(Curl_pgrsUpdate(conn))
592     result = CURLE_ABORTED_BY_CALLBACK;
593
594   return result;
595 }
596
597 #endif