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