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