1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Wez Furlong <wez@thebrainroom.com> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $Id$ */
20
21 #include "php.h"
22 #include "php_globals.h"
23 #include "php_network.h"
24 #include "php_open_temporary_file.h"
25 #include "ext/standard/file.h"
26 #include "ext/standard/flock_compat.h"
27 #include "ext/standard/php_filestat.h"
28 #include <stddef.h>
29 #include <fcntl.h>
30 #if HAVE_SYS_WAIT_H
31 #include <sys/wait.h>
32 #endif
33 #if HAVE_SYS_FILE_H
34 #include <sys/file.h>
35 #endif
36 #ifdef HAVE_SYS_MMAN_H
37 #include <sys/mman.h>
38 #endif
39 #include "SAPI.h"
40
41 #include "php_streams_int.h"
42 #ifdef PHP_WIN32
43 # include "win32/winutil.h"
44 # include "win32/time.h"
45 # include "win32/ioutil.h"
46 # include "win32/readdir.h"
47 #endif
48
49 #define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC)
50 #define php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC)
51 #define php_stream_fopen_from_file_int(file, mode) _php_stream_fopen_from_file_int((file), (mode) STREAMS_CC)
52 #define php_stream_fopen_from_file_int_rel(file, mode) _php_stream_fopen_from_file_int((file), (mode) STREAMS_REL_CC)
53
54 #if !defined(WINDOWS) && !defined(NETWARE)
55 extern int php_get_uid_by_name(const char *name, uid_t *uid);
56 extern int php_get_gid_by_name(const char *name, gid_t *gid);
57 #endif
58
59 #if defined(PHP_WIN32)
60 # define PLAIN_WRAP_BUF_SIZE(st) (((st) > UINT_MAX) ? UINT_MAX : (unsigned int)(st))
61 #else
62 # define PLAIN_WRAP_BUF_SIZE(st) (st)
63 #endif
64
65 /* parse standard "fopen" modes into open() flags */
php_stream_parse_fopen_modes(const char * mode,int * open_flags)66 PHPAPI int php_stream_parse_fopen_modes(const char *mode, int *open_flags)
67 {
68 int flags;
69
70 switch (mode[0]) {
71 case 'r':
72 flags = 0;
73 break;
74 case 'w':
75 flags = O_TRUNC|O_CREAT;
76 break;
77 case 'a':
78 flags = O_CREAT|O_APPEND;
79 break;
80 case 'x':
81 flags = O_CREAT|O_EXCL;
82 break;
83 case 'c':
84 flags = O_CREAT;
85 break;
86 default:
87 /* unknown mode */
88 return FAILURE;
89 }
90
91 if (strchr(mode, '+')) {
92 flags |= O_RDWR;
93 } else if (flags) {
94 flags |= O_WRONLY;
95 } else {
96 flags |= O_RDONLY;
97 }
98
99 #if defined(O_CLOEXEC)
100 if (strchr(mode, 'e')) {
101 flags |= O_CLOEXEC;
102 }
103 #endif
104
105 #if defined(O_NONBLOCK)
106 if (strchr(mode, 'n')) {
107 flags |= O_NONBLOCK;
108 }
109 #endif
110
111 #if defined(_O_TEXT) && defined(O_BINARY)
112 if (strchr(mode, 't')) {
113 flags |= _O_TEXT;
114 } else {
115 flags |= O_BINARY;
116 }
117 #endif
118
119 *open_flags = flags;
120 return SUCCESS;
121 }
122
123
124 /* {{{ ------- STDIO stream implementation -------*/
125
126 typedef struct {
127 FILE *file;
128 int fd; /* underlying file descriptor */
129 unsigned is_process_pipe:1; /* use pclose instead of fclose */
130 unsigned is_pipe:1; /* don't try and seek */
131 unsigned cached_fstat:1; /* sb is valid */
132 unsigned is_pipe_blocking:1; /* allow blocking read() on pipes, currently Windows only */
133 unsigned _reserved:28;
134
135 int lock_flag; /* stores the lock state */
136 zend_string *temp_name; /* if non-null, this is the path to a temporary file that
137 * is to be deleted when the stream is closed */
138 #if HAVE_FLUSHIO
139 char last_op;
140 #endif
141
142 #if HAVE_MMAP
143 char *last_mapped_addr;
144 size_t last_mapped_len;
145 #endif
146 #ifdef PHP_WIN32
147 char *last_mapped_addr;
148 HANDLE file_mapping;
149 #endif
150
151 zend_stat_t sb;
152 } php_stdio_stream_data;
153 #define PHP_STDIOP_GET_FD(anfd, data) anfd = (data)->file ? fileno((data)->file) : (data)->fd
154
do_fstat(php_stdio_stream_data * d,int force)155 static int do_fstat(php_stdio_stream_data *d, int force)
156 {
157 if (!d->cached_fstat || force) {
158 int fd;
159 int r;
160
161 PHP_STDIOP_GET_FD(fd, d);
162 r = zend_fstat(fd, &d->sb);
163 d->cached_fstat = r == 0;
164
165 return r;
166 }
167 return 0;
168 }
169
_php_stream_fopen_from_fd_int(int fd,const char * mode,const char * persistent_id STREAMS_DC)170 static php_stream *_php_stream_fopen_from_fd_int(int fd, const char *mode, const char *persistent_id STREAMS_DC)
171 {
172 php_stdio_stream_data *self;
173
174 self = pemalloc_rel_orig(sizeof(*self), persistent_id);
175 memset(self, 0, sizeof(*self));
176 self->file = NULL;
177 self->is_pipe = 0;
178 self->lock_flag = LOCK_UN;
179 self->is_process_pipe = 0;
180 self->temp_name = NULL;
181 self->fd = fd;
182 #ifdef PHP_WIN32
183 self->is_pipe_blocking = 0;
184 #endif
185
186 return php_stream_alloc_rel(&php_stream_stdio_ops, self, persistent_id, mode);
187 }
188
_php_stream_fopen_from_file_int(FILE * file,const char * mode STREAMS_DC)189 static php_stream *_php_stream_fopen_from_file_int(FILE *file, const char *mode STREAMS_DC)
190 {
191 php_stdio_stream_data *self;
192
193 self = emalloc_rel_orig(sizeof(*self));
194 memset(self, 0, sizeof(*self));
195 self->file = file;
196 self->is_pipe = 0;
197 self->lock_flag = LOCK_UN;
198 self->is_process_pipe = 0;
199 self->temp_name = NULL;
200 self->fd = fileno(file);
201 #ifdef PHP_WIN32
202 self->is_pipe_blocking = 0;
203 #endif
204
205 return php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode);
206 }
207
_php_stream_fopen_temporary_file(const char * dir,const char * pfx,zend_string ** opened_path_ptr STREAMS_DC)208 PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, zend_string **opened_path_ptr STREAMS_DC)
209 {
210 zend_string *opened_path = NULL;
211 int fd;
212
213 fd = php_open_temporary_fd(dir, pfx, &opened_path);
214 if (fd != -1) {
215 php_stream *stream;
216
217 if (opened_path_ptr) {
218 *opened_path_ptr = opened_path;
219 }
220
221 stream = php_stream_fopen_from_fd_int_rel(fd, "r+b", NULL);
222 if (stream) {
223 php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
224 stream->wrapper = &php_plain_files_wrapper;
225 stream->orig_path = estrndup(ZSTR_VAL(opened_path), ZSTR_LEN(opened_path));
226
227 self->temp_name = opened_path;
228 self->lock_flag = LOCK_UN;
229
230 return stream;
231 }
232 close(fd);
233
234 php_error_docref(NULL, E_WARNING, "unable to allocate stream");
235
236 return NULL;
237 }
238 return NULL;
239 }
240
_php_stream_fopen_tmpfile(int dummy STREAMS_DC)241 PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC)
242 {
243 return php_stream_fopen_temporary_file(NULL, "php", NULL);
244 }
245
_php_stream_fopen_from_fd(int fd,const char * mode,const char * persistent_id STREAMS_DC)246 PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC)
247 {
248 php_stream *stream = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);
249
250 if (stream) {
251 php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
252
253 #ifdef S_ISFIFO
254 /* detect if this is a pipe */
255 if (self->fd >= 0) {
256 self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0;
257 }
258 #elif defined(PHP_WIN32)
259 {
260 zend_uintptr_t handle = _get_osfhandle(self->fd);
261
262 if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
263 self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE;
264 }
265 }
266 #endif
267
268 if (self->is_pipe) {
269 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
270 } else {
271 stream->position = zend_lseek(self->fd, 0, SEEK_CUR);
272 #ifdef ESPIPE
273 if (stream->position == (zend_off_t)-1 && errno == ESPIPE) {
274 stream->position = 0;
275 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
276 self->is_pipe = 1;
277 }
278 #endif
279 }
280 }
281
282 return stream;
283 }
284
_php_stream_fopen_from_file(FILE * file,const char * mode STREAMS_DC)285 PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC)
286 {
287 php_stream *stream = php_stream_fopen_from_file_int_rel(file, mode);
288
289 if (stream) {
290 php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
291
292 #ifdef S_ISFIFO
293 /* detect if this is a pipe */
294 if (self->fd >= 0) {
295 self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0;
296 }
297 #elif defined(PHP_WIN32)
298 {
299 zend_uintptr_t handle = _get_osfhandle(self->fd);
300
301 if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {
302 self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE;
303 }
304 }
305 #endif
306
307 if (self->is_pipe) {
308 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
309 } else {
310 stream->position = zend_ftell(file);
311 }
312 }
313
314 return stream;
315 }
316
_php_stream_fopen_from_pipe(FILE * file,const char * mode STREAMS_DC)317 PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STREAMS_DC)
318 {
319 php_stdio_stream_data *self;
320 php_stream *stream;
321
322 self = emalloc_rel_orig(sizeof(*self));
323 memset(self, 0, sizeof(*self));
324 self->file = file;
325 self->is_pipe = 1;
326 self->lock_flag = LOCK_UN;
327 self->is_process_pipe = 1;
328 self->fd = fileno(file);
329 self->temp_name = NULL;
330 #ifdef PHP_WIN32
331 self->is_pipe_blocking = 0;
332 #endif
333
334 stream = php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode);
335 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
336 return stream;
337 }
338
php_stdiop_write(php_stream * stream,const char * buf,size_t count)339 static size_t php_stdiop_write(php_stream *stream, const char *buf, size_t count)
340 {
341 php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
342
343 assert(data != NULL);
344
345 if (data->fd >= 0) {
346 #ifdef PHP_WIN32
347 int bytes_written;
348 if (ZEND_SIZE_T_UINT_OVFL(count)) {
349 count = UINT_MAX;
350 }
351 bytes_written = _write(data->fd, buf, (unsigned int)count);
352 #else
353 int bytes_written = write(data->fd, buf, count);
354 #endif
355 if (bytes_written < 0) return 0;
356 return (size_t) bytes_written;
357 } else {
358
359 #if HAVE_FLUSHIO
360 if (!data->is_pipe && data->last_op == 'r') {
361 zend_fseek(data->file, 0, SEEK_CUR);
362 }
363 data->last_op = 'w';
364 #endif
365
366 return fwrite(buf, 1, count, data->file);
367 }
368 }
369
php_stdiop_read(php_stream * stream,char * buf,size_t count)370 static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
371 {
372 php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
373 size_t ret;
374
375 assert(data != NULL);
376
377 if (data->fd >= 0) {
378 #ifdef PHP_WIN32
379 php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
380
381 if ((self->is_pipe || self->is_process_pipe) && !self->is_pipe_blocking) {
382 HANDLE ph = (HANDLE)_get_osfhandle(data->fd);
383 int retry = 0;
384 DWORD avail_read = 0;
385
386 do {
387 /* Look ahead to get the available data amount to read. Do the same
388 as read() does, however not blocking forever. In case it failed,
389 no data will be read (better than block). */
390 if (!PeekNamedPipe(ph, NULL, 0, NULL, &avail_read, NULL)) {
391 break;
392 }
393 /* If there's nothing to read, wait in 10ms periods. */
394 if (0 == avail_read) {
395 usleep(10);
396 }
397 } while (0 == avail_read && retry++ < 3200000);
398
399 /* Reduce the required data amount to what is available, otherwise read()
400 will block.*/
401 if (avail_read < count) {
402 count = avail_read;
403 }
404 }
405 #endif
406 ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count));
407
408 if (ret == (size_t)-1 && errno == EINTR) {
409 /* Read was interrupted, retry once,
410 If read still fails, giveup with feof==0
411 so script can retry if desired */
412 ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count));
413 }
414
415 stream->eof = (ret == 0 || (ret == (size_t)-1 && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF));
416
417 } else {
418 #if HAVE_FLUSHIO
419 if (!data->is_pipe && data->last_op == 'w')
420 zend_fseek(data->file, 0, SEEK_CUR);
421 data->last_op = 'r';
422 #endif
423
424 ret = fread(buf, 1, count, data->file);
425
426 stream->eof = feof(data->file);
427 }
428 return ret;
429 }
430
php_stdiop_close(php_stream * stream,int close_handle)431 static int php_stdiop_close(php_stream *stream, int close_handle)
432 {
433 int ret;
434 php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
435
436 assert(data != NULL);
437
438 #if HAVE_MMAP
439 if (data->last_mapped_addr) {
440 munmap(data->last_mapped_addr, data->last_mapped_len);
441 data->last_mapped_addr = NULL;
442 }
443 #elif defined(PHP_WIN32)
444 if (data->last_mapped_addr) {
445 UnmapViewOfFile(data->last_mapped_addr);
446 data->last_mapped_addr = NULL;
447 }
448 if (data->file_mapping) {
449 CloseHandle(data->file_mapping);
450 data->file_mapping = NULL;
451 }
452 #endif
453
454 if (close_handle) {
455 if (data->file) {
456 if (data->is_process_pipe) {
457 errno = 0;
458 ret = pclose(data->file);
459
460 #if HAVE_SYS_WAIT_H
461 if (WIFEXITED(ret)) {
462 ret = WEXITSTATUS(ret);
463 }
464 #endif
465 } else {
466 ret = fclose(data->file);
467 data->file = NULL;
468 }
469 } else if (data->fd != -1) {
470 ret = close(data->fd);
471 data->fd = -1;
472 } else {
473 return 0; /* everything should be closed already -> success */
474 }
475 if (data->temp_name) {
476 #ifdef PHP_WIN32
477 php_win32_ioutil_unlink(ZSTR_VAL(data->temp_name));
478 #else
479 unlink(ZSTR_VAL(data->temp_name));
480 #endif
481 /* temporary streams are never persistent */
482 zend_string_release(data->temp_name);
483 data->temp_name = NULL;
484 }
485 } else {
486 ret = 0;
487 data->file = NULL;
488 data->fd = -1;
489 }
490
491 pefree(data, stream->is_persistent);
492
493 return ret;
494 }
495
php_stdiop_flush(php_stream * stream)496 static int php_stdiop_flush(php_stream *stream)
497 {
498 php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
499
500 assert(data != NULL);
501
502 /*
503 * stdio buffers data in user land. By calling fflush(3), this
504 * data is send to the kernel using write(2). fsync'ing is
505 * something completely different.
506 */
507 if (data->file) {
508 return fflush(data->file);
509 }
510 return 0;
511 }
512
php_stdiop_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffset)513 static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
514 {
515 php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
516 int ret;
517
518 assert(data != NULL);
519
520 if (data->is_pipe) {
521 php_error_docref(NULL, E_WARNING, "cannot seek on a pipe");
522 return -1;
523 }
524
525 if (data->fd >= 0) {
526 zend_off_t result;
527
528 result = zend_lseek(data->fd, offset, whence);
529 if (result == (zend_off_t)-1)
530 return -1;
531
532 *newoffset = result;
533 return 0;
534
535 } else {
536 ret = zend_fseek(data->file, offset, whence);
537 *newoffset = zend_ftell(data->file);
538 return ret;
539 }
540 }
541
php_stdiop_cast(php_stream * stream,int castas,void ** ret)542 static int php_stdiop_cast(php_stream *stream, int castas, void **ret)
543 {
544 php_socket_t fd;
545 php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
546
547 assert(data != NULL);
548
549 /* as soon as someone touches the stdio layer, buffering may ensue,
550 * so we need to stop using the fd directly in that case */
551
552 switch (castas) {
553 case PHP_STREAM_AS_STDIO:
554 if (ret) {
555
556 if (data->file == NULL) {
557 /* we were opened as a plain file descriptor, so we
558 * need fdopen now */
559 char fixed_mode[5];
560 php_stream_mode_sanitize_fdopen_fopencookie(stream, fixed_mode);
561 data->file = fdopen(data->fd, fixed_mode);
562 if (data->file == NULL) {
563 return FAILURE;
564 }
565 }
566
567 *(FILE**)ret = data->file;
568 data->fd = SOCK_ERR;
569 }
570 return SUCCESS;
571
572 case PHP_STREAM_AS_FD_FOR_SELECT:
573 PHP_STDIOP_GET_FD(fd, data);
574 if (SOCK_ERR == fd) {
575 return FAILURE;
576 }
577 if (ret) {
578 *(php_socket_t *)ret = fd;
579 }
580 return SUCCESS;
581
582 case PHP_STREAM_AS_FD:
583 PHP_STDIOP_GET_FD(fd, data);
584
585 if (SOCK_ERR == fd) {
586 return FAILURE;
587 }
588 if (data->file) {
589 fflush(data->file);
590 }
591 if (ret) {
592 *(php_socket_t *)ret = fd;
593 }
594 return SUCCESS;
595 default:
596 return FAILURE;
597 }
598 }
599
php_stdiop_stat(php_stream * stream,php_stream_statbuf * ssb)600 static int php_stdiop_stat(php_stream *stream, php_stream_statbuf *ssb)
601 {
602 int ret;
603 php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
604
605 assert(data != NULL);
606 if((ret = do_fstat(data, 1)) == 0) {
607 memcpy(&ssb->sb, &data->sb, sizeof(ssb->sb));
608 }
609
610 return ret;
611 }
612
php_stdiop_set_option(php_stream * stream,int option,int value,void * ptrparam)613 static int php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam)
614 {
615 php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
616 size_t size;
617 int fd;
618 #ifdef O_NONBLOCK
619 /* FIXME: make this work for win32 */
620 int flags;
621 int oldval;
622 #endif
623
624 PHP_STDIOP_GET_FD(fd, data);
625
626 switch(option) {
627 case PHP_STREAM_OPTION_BLOCKING:
628 if (fd == -1)
629 return -1;
630 #ifdef O_NONBLOCK
631 flags = fcntl(fd, F_GETFL, 0);
632 oldval = (flags & O_NONBLOCK) ? 0 : 1;
633 if (value)
634 flags &= ~O_NONBLOCK;
635 else
636 flags |= O_NONBLOCK;
637
638 if (-1 == fcntl(fd, F_SETFL, flags))
639 return -1;
640 return oldval;
641 #else
642 return -1; /* not yet implemented */
643 #endif
644
645 case PHP_STREAM_OPTION_WRITE_BUFFER:
646
647 if (data->file == NULL) {
648 return -1;
649 }
650
651 if (ptrparam)
652 size = *(size_t *)ptrparam;
653 else
654 size = BUFSIZ;
655
656 switch(value) {
657 case PHP_STREAM_BUFFER_NONE:
658 return setvbuf(data->file, NULL, _IONBF, 0);
659
660 case PHP_STREAM_BUFFER_LINE:
661 return setvbuf(data->file, NULL, _IOLBF, size);
662
663 case PHP_STREAM_BUFFER_FULL:
664 return setvbuf(data->file, NULL, _IOFBF, size);
665
666 default:
667 return -1;
668 }
669 break;
670
671 case PHP_STREAM_OPTION_LOCKING:
672 if (fd == -1) {
673 return -1;
674 }
675
676 if ((zend_uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) {
677 return 0;
678 }
679
680 if (!flock(fd, value)) {
681 data->lock_flag = value;
682 return 0;
683 } else {
684 return -1;
685 }
686 break;
687
688 case PHP_STREAM_OPTION_MMAP_API:
689 #if HAVE_MMAP
690 {
691 php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
692 int prot, flags;
693
694 switch (value) {
695 case PHP_STREAM_MMAP_SUPPORTED:
696 return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
697
698 case PHP_STREAM_MMAP_MAP_RANGE:
699 if(do_fstat(data, 1) != 0) {
700 return PHP_STREAM_OPTION_RETURN_ERR;
701 }
702 if (range->length == 0 && range->offset > 0 && range->offset < data->sb.st_size) {
703 range->length = data->sb.st_size - range->offset;
704 }
705 if (range->length == 0 || range->length > data->sb.st_size) {
706 range->length = data->sb.st_size;
707 }
708 if (range->offset >= data->sb.st_size) {
709 range->offset = data->sb.st_size;
710 range->length = 0;
711 }
712 switch (range->mode) {
713 case PHP_STREAM_MAP_MODE_READONLY:
714 prot = PROT_READ;
715 flags = MAP_PRIVATE;
716 break;
717 case PHP_STREAM_MAP_MODE_READWRITE:
718 prot = PROT_READ | PROT_WRITE;
719 flags = MAP_PRIVATE;
720 break;
721 case PHP_STREAM_MAP_MODE_SHARED_READONLY:
722 prot = PROT_READ;
723 flags = MAP_SHARED;
724 break;
725 case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
726 prot = PROT_READ | PROT_WRITE;
727 flags = MAP_SHARED;
728 break;
729 default:
730 return PHP_STREAM_OPTION_RETURN_ERR;
731 }
732 range->mapped = (char*)mmap(NULL, range->length, prot, flags, fd, range->offset);
733 if (range->mapped == (char*)MAP_FAILED) {
734 range->mapped = NULL;
735 return PHP_STREAM_OPTION_RETURN_ERR;
736 }
737 /* remember the mapping */
738 data->last_mapped_addr = range->mapped;
739 data->last_mapped_len = range->length;
740 return PHP_STREAM_OPTION_RETURN_OK;
741
742 case PHP_STREAM_MMAP_UNMAP:
743 if (data->last_mapped_addr) {
744 munmap(data->last_mapped_addr, data->last_mapped_len);
745 data->last_mapped_addr = NULL;
746
747 return PHP_STREAM_OPTION_RETURN_OK;
748 }
749 return PHP_STREAM_OPTION_RETURN_ERR;
750 }
751 }
752 #elif defined(PHP_WIN32)
753 {
754 php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
755 HANDLE hfile = (HANDLE)_get_osfhandle(fd);
756 DWORD prot, acc, loffs = 0, delta = 0;
757
758 switch (value) {
759 case PHP_STREAM_MMAP_SUPPORTED:
760 return hfile == INVALID_HANDLE_VALUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
761
762 case PHP_STREAM_MMAP_MAP_RANGE:
763 switch (range->mode) {
764 case PHP_STREAM_MAP_MODE_READONLY:
765 prot = PAGE_READONLY;
766 acc = FILE_MAP_READ;
767 break;
768 case PHP_STREAM_MAP_MODE_READWRITE:
769 prot = PAGE_READWRITE;
770 acc = FILE_MAP_READ | FILE_MAP_WRITE;
771 break;
772 case PHP_STREAM_MAP_MODE_SHARED_READONLY:
773 prot = PAGE_READONLY;
774 acc = FILE_MAP_READ;
775 /* TODO: we should assign a name for the mapping */
776 break;
777 case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
778 prot = PAGE_READWRITE;
779 acc = FILE_MAP_READ | FILE_MAP_WRITE;
780 /* TODO: we should assign a name for the mapping */
781 break;
782 default:
783 return PHP_STREAM_OPTION_RETURN_ERR;
784 }
785
786 /* create a mapping capable of viewing the whole file (this costs no real resources) */
787 data->file_mapping = CreateFileMapping(hfile, NULL, prot, 0, 0, NULL);
788
789 if (data->file_mapping == NULL) {
790 return PHP_STREAM_OPTION_RETURN_ERR;
791 }
792
793 size = GetFileSize(hfile, NULL);
794 if (range->length == 0 && range->offset > 0 && range->offset < size) {
795 range->length = size - range->offset;
796 }
797 if (range->length == 0 || range->length > size) {
798 range->length = size;
799 }
800 if (range->offset >= size) {
801 range->offset = size;
802 range->length = 0;
803 }
804
805 /* figure out how big a chunk to map to be able to view the part that we need */
806 if (range->offset != 0) {
807 SYSTEM_INFO info;
808 DWORD gran;
809
810 GetSystemInfo(&info);
811 gran = info.dwAllocationGranularity;
812 loffs = ((DWORD)range->offset / gran) * gran;
813 delta = (DWORD)range->offset - loffs;
814 }
815
816 data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, 0, loffs, range->length + delta);
817
818 if (data->last_mapped_addr) {
819 /* give them back the address of the start offset they requested */
820 range->mapped = data->last_mapped_addr + delta;
821 return PHP_STREAM_OPTION_RETURN_OK;
822 }
823
824 CloseHandle(data->file_mapping);
825 data->file_mapping = NULL;
826
827 return PHP_STREAM_OPTION_RETURN_ERR;
828
829 case PHP_STREAM_MMAP_UNMAP:
830 if (data->last_mapped_addr) {
831 UnmapViewOfFile(data->last_mapped_addr);
832 data->last_mapped_addr = NULL;
833 CloseHandle(data->file_mapping);
834 data->file_mapping = NULL;
835 return PHP_STREAM_OPTION_RETURN_OK;
836 }
837 return PHP_STREAM_OPTION_RETURN_ERR;
838
839 default:
840 return PHP_STREAM_OPTION_RETURN_ERR;
841 }
842 }
843
844 #endif
845 return PHP_STREAM_OPTION_RETURN_NOTIMPL;
846
847 case PHP_STREAM_OPTION_TRUNCATE_API:
848 switch (value) {
849 case PHP_STREAM_TRUNCATE_SUPPORTED:
850 return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
851
852 case PHP_STREAM_TRUNCATE_SET_SIZE: {
853 ptrdiff_t new_size = *(ptrdiff_t*)ptrparam;
854 if (new_size < 0) {
855 return PHP_STREAM_OPTION_RETURN_ERR;
856 }
857 return ftruncate(fd, new_size) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
858 }
859 }
860
861 #ifdef PHP_WIN32
862 case PHP_STREAM_OPTION_PIPE_BLOCKING:
863 data->is_pipe_blocking = value;
864 return PHP_STREAM_OPTION_RETURN_OK;
865 #endif
866 case PHP_STREAM_OPTION_META_DATA_API:
867 if (fd == -1)
868 return -1;
869 #ifdef O_NONBLOCK
870 flags = fcntl(fd, F_GETFL, 0);
871
872 add_assoc_bool((zval*)ptrparam, "timed_out", 0);
873 add_assoc_bool((zval*)ptrparam, "blocked", (flags & O_NONBLOCK)? 0 : 1);
874 add_assoc_bool((zval*)ptrparam, "eof", stream->eof);
875
876 return PHP_STREAM_OPTION_RETURN_OK;
877 #endif
878 return -1;
879 default:
880 return PHP_STREAM_OPTION_RETURN_NOTIMPL;
881 }
882 }
883
884 PHPAPI php_stream_ops php_stream_stdio_ops = {
885 php_stdiop_write, php_stdiop_read,
886 php_stdiop_close, php_stdiop_flush,
887 "STDIO",
888 php_stdiop_seek,
889 php_stdiop_cast,
890 php_stdiop_stat,
891 php_stdiop_set_option
892 };
893 /* }}} */
894
895 /* {{{ plain files opendir/readdir implementation */
php_plain_files_dirstream_read(php_stream * stream,char * buf,size_t count)896 static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count)
897 {
898 DIR *dir = (DIR*)stream->abstract;
899 /* avoid libc5 readdir problems */
900 char entry[sizeof(struct dirent)+MAXPATHLEN];
901 struct dirent *result = (struct dirent *)&entry;
902 php_stream_dirent *ent = (php_stream_dirent*)buf;
903
904 /* avoid problems if someone mis-uses the stream */
905 if (count != sizeof(php_stream_dirent))
906 return 0;
907
908 if (php_readdir_r(dir, (struct dirent *)entry, &result) == 0 && result) {
909 PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name));
910 return sizeof(php_stream_dirent);
911 }
912 return 0;
913 }
914
php_plain_files_dirstream_close(php_stream * stream,int close_handle)915 static int php_plain_files_dirstream_close(php_stream *stream, int close_handle)
916 {
917 return closedir((DIR *)stream->abstract);
918 }
919
php_plain_files_dirstream_rewind(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)920 static int php_plain_files_dirstream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
921 {
922 rewinddir((DIR *)stream->abstract);
923 return 0;
924 }
925
926 static php_stream_ops php_plain_files_dirstream_ops = {
927 NULL, php_plain_files_dirstream_read,
928 php_plain_files_dirstream_close, NULL,
929 "dir",
930 php_plain_files_dirstream_rewind,
931 NULL, /* cast */
932 NULL, /* stat */
933 NULL /* set_option */
934 };
935
php_plain_files_dir_opener(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)936 static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
937 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
938 {
939 DIR *dir = NULL;
940 php_stream *stream = NULL;
941
942 #ifdef HAVE_GLOB
943 if (options & STREAM_USE_GLOB_DIR_OPEN) {
944 return php_glob_stream_wrapper.wops->dir_opener(&php_glob_stream_wrapper, path, mode, options, opened_path, context STREAMS_REL_CC);
945 }
946 #endif
947
948 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {
949 return NULL;
950 }
951
952 dir = VCWD_OPENDIR(path);
953
954 #ifdef PHP_WIN32
955 if (!dir) {
956 php_win32_docref2_from_error(GetLastError(), path, path);
957 }
958
959 if (dir && dir->finished) {
960 closedir(dir);
961 dir = NULL;
962 }
963 #endif
964 if (dir) {
965 stream = php_stream_alloc(&php_plain_files_dirstream_ops, dir, 0, mode);
966 if (stream == NULL)
967 closedir(dir);
968 }
969
970 return stream;
971 }
972 /* }}} */
973
974 /* {{{ php_stream_fopen */
_php_stream_fopen(const char * filename,const char * mode,zend_string ** opened_path,int options STREAMS_DC)975 PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zend_string **opened_path, int options STREAMS_DC)
976 {
977 char realpath[MAXPATHLEN];
978 int open_flags;
979 int fd;
980 php_stream *ret;
981 int persistent = options & STREAM_OPEN_PERSISTENT;
982 char *persistent_id = NULL;
983
984 if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) {
985 if (options & REPORT_ERRORS) {
986 php_error_docref(NULL, E_WARNING, "`%s' is not a valid mode for fopen", mode);
987 }
988 return NULL;
989 }
990
991 if (options & STREAM_ASSUME_REALPATH) {
992 strlcpy(realpath, filename, sizeof(realpath));
993 } else {
994 if (expand_filepath(filename, realpath) == NULL) {
995 return NULL;
996 }
997 }
998
999 if (persistent) {
1000 spprintf(&persistent_id, 0, "streams_stdio_%d_%s", open_flags, realpath);
1001 switch (php_stream_from_persistent_id(persistent_id, &ret)) {
1002 case PHP_STREAM_PERSISTENT_SUCCESS:
1003 if (opened_path) {
1004 //TODO: avoid reallocation???
1005 *opened_path = zend_string_init(realpath, strlen(realpath), 0);
1006 }
1007 /* fall through */
1008
1009 case PHP_STREAM_PERSISTENT_FAILURE:
1010 efree(persistent_id);;
1011 return ret;
1012 }
1013 }
1014 #ifdef PHP_WIN32
1015 fd = php_win32_ioutil_open(realpath, open_flags, 0666);
1016 #else
1017 fd = open(realpath, open_flags, 0666);
1018 #endif
1019 if (fd != -1) {
1020
1021 if (options & STREAM_OPEN_FOR_INCLUDE) {
1022 ret = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);
1023 } else {
1024 ret = php_stream_fopen_from_fd_rel(fd, mode, persistent_id);
1025 }
1026
1027 if (ret) {
1028 if (opened_path) {
1029 *opened_path = zend_string_init(realpath, strlen(realpath), 0);
1030 }
1031 if (persistent_id) {
1032 efree(persistent_id);
1033 }
1034
1035 /* WIN32 always set ISREG flag */
1036 #ifndef PHP_WIN32
1037 /* sanity checks for include/require.
1038 * We check these after opening the stream, so that we save
1039 * on fstat() syscalls */
1040 if (options & STREAM_OPEN_FOR_INCLUDE) {
1041 php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract;
1042 int r;
1043
1044 r = do_fstat(self, 0);
1045 if ((r == 0 && !S_ISREG(self->sb.st_mode))) {
1046 if (opened_path) {
1047 zend_string_release(*opened_path);
1048 *opened_path = NULL;
1049 }
1050 php_stream_close(ret);
1051 return NULL;
1052 }
1053 }
1054
1055 if (options & STREAM_USE_BLOCKING_PIPE) {
1056 php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract;
1057 self->is_pipe_blocking = 1;
1058 }
1059 #endif
1060
1061 return ret;
1062 }
1063 close(fd);
1064 }
1065 if (persistent_id) {
1066 efree(persistent_id);
1067 }
1068 return NULL;
1069 }
1070 /* }}} */
1071
1072
php_plain_files_stream_opener(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)1073 static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
1074 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
1075 {
1076 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {
1077 return NULL;
1078 }
1079
1080 return php_stream_fopen_rel(path, mode, opened_path, options);
1081 }
1082
php_plain_files_url_stater(php_stream_wrapper * wrapper,const char * url,int flags,php_stream_statbuf * ssb,php_stream_context * context)1083 static int php_plain_files_url_stater(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context)
1084 {
1085 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1086 url += sizeof("file://") - 1;
1087 }
1088
1089 if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1)) {
1090 return -1;
1091 }
1092
1093 #ifdef PHP_WIN32
1094 if (flags & PHP_STREAM_URL_STAT_LINK) {
1095 return VCWD_LSTAT(url, &ssb->sb);
1096 }
1097 #else
1098 # ifdef HAVE_SYMLINK
1099 if (flags & PHP_STREAM_URL_STAT_LINK) {
1100 return VCWD_LSTAT(url, &ssb->sb);
1101 } else
1102 # endif
1103 #endif
1104 return VCWD_STAT(url, &ssb->sb);
1105 }
1106
php_plain_files_unlink(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1107 static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1108 {
1109 int ret;
1110
1111 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1112 url += sizeof("file://") - 1;
1113 }
1114
1115 if (php_check_open_basedir(url)) {
1116 return 0;
1117 }
1118
1119 ret = VCWD_UNLINK(url);
1120 if (ret == -1) {
1121 if (options & REPORT_ERRORS) {
1122 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno));
1123 }
1124 return 0;
1125 }
1126
1127 /* Clear stat cache (and realpath cache) */
1128 php_clear_stat_cache(1, NULL, 0);
1129
1130 return 1;
1131 }
1132
php_plain_files_rename(php_stream_wrapper * wrapper,const char * url_from,const char * url_to,int options,php_stream_context * context)1133 static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context)
1134 {
1135 int ret;
1136
1137 if (!url_from || !url_to) {
1138 return 0;
1139 }
1140
1141 #ifdef PHP_WIN32
1142 if (!php_win32_check_trailing_space(url_from, (int)strlen(url_from))) {
1143 php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);
1144 return 0;
1145 }
1146 if (!php_win32_check_trailing_space(url_to, (int)strlen(url_to))) {
1147 php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);
1148 return 0;
1149 }
1150 #endif
1151
1152 if (strncasecmp(url_from, "file://", sizeof("file://") - 1) == 0) {
1153 url_from += sizeof("file://") - 1;
1154 }
1155
1156 if (strncasecmp(url_to, "file://", sizeof("file://") - 1) == 0) {
1157 url_to += sizeof("file://") - 1;
1158 }
1159
1160 if (php_check_open_basedir(url_from) || php_check_open_basedir(url_to)) {
1161 return 0;
1162 }
1163
1164 ret = VCWD_RENAME(url_from, url_to);
1165
1166 if (ret == -1) {
1167 #ifndef PHP_WIN32
1168 # ifdef EXDEV
1169 if (errno == EXDEV) {
1170 zend_stat_t sb;
1171 # if !defined(ZTS) && !defined(TSRM_WIN32) && !defined(NETWARE)
1172 /* not sure what to do in ZTS case, umask is not thread-safe */
1173 int oldmask = umask(077);
1174 # endif
1175 int success = 0;
1176 if (php_copy_file(url_from, url_to) == SUCCESS) {
1177 if (VCWD_STAT(url_from, &sb) == 0) {
1178 success = 1;
1179 # if !defined(TSRM_WIN32) && !defined(NETWARE)
1180 /*
1181 * Try to set user and permission info on the target.
1182 * If we're not root, then some of these may fail.
1183 * We try chown first, to set proper group info, relying
1184 * on the system environment to have proper umask to not allow
1185 * access to the file in the meantime.
1186 */
1187 if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) {
1188 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1189 if (errno != EPERM) {
1190 success = 0;
1191 }
1192 }
1193
1194 if (success) {
1195 if (VCWD_CHMOD(url_to, sb.st_mode)) {
1196 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1197 if (errno != EPERM) {
1198 success = 0;
1199 }
1200 }
1201 }
1202 # endif
1203 if (success) {
1204 VCWD_UNLINK(url_from);
1205 }
1206 } else {
1207 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1208 }
1209 } else {
1210 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1211 }
1212 # if !defined(ZTS) && !defined(TSRM_WIN32) && !defined(NETWARE)
1213 umask(oldmask);
1214 # endif
1215 return success;
1216 }
1217 # endif
1218 #endif
1219
1220 #ifdef PHP_WIN32
1221 php_win32_docref2_from_error(GetLastError(), url_from, url_to);
1222 #else
1223 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1224 #endif
1225 return 0;
1226 }
1227
1228 /* Clear stat cache (and realpath cache) */
1229 php_clear_stat_cache(1, NULL, 0);
1230
1231 return 1;
1232 }
1233
php_plain_files_mkdir(php_stream_wrapper * wrapper,const char * dir,int mode,int options,php_stream_context * context)1234 static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, int mode, int options, php_stream_context *context)
1235 {
1236 int ret, recursive = options & PHP_STREAM_MKDIR_RECURSIVE;
1237 char *p;
1238
1239 if (strncasecmp(dir, "file://", sizeof("file://") - 1) == 0) {
1240 dir += sizeof("file://") - 1;
1241 }
1242
1243 if (!recursive) {
1244 ret = php_mkdir(dir, mode);
1245 } else {
1246 /* we look for directory separator from the end of string, thus hopefuly reducing our work load */
1247 char *e;
1248 zend_stat_t sb;
1249 int dir_len = (int)strlen(dir);
1250 int offset = 0;
1251 char buf[MAXPATHLEN];
1252
1253 if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND )) {
1254 php_error_docref(NULL, E_WARNING, "Invalid path");
1255 return 0;
1256 }
1257
1258 e = buf + strlen(buf);
1259
1260 if ((p = memchr(buf, DEFAULT_SLASH, dir_len))) {
1261 offset = p - buf + 1;
1262 }
1263
1264 if (p && dir_len == 1) {
1265 /* buf == "DEFAULT_SLASH" */
1266 }
1267 else {
1268 /* find a top level directory we need to create */
1269 while ( (p = strrchr(buf + offset, DEFAULT_SLASH)) || (offset != 1 && (p = strrchr(buf, DEFAULT_SLASH))) ) {
1270 int n = 0;
1271
1272 *p = '\0';
1273 while (p > buf && *(p-1) == DEFAULT_SLASH) {
1274 ++n;
1275 --p;
1276 *p = '\0';
1277 }
1278 if (VCWD_STAT(buf, &sb) == 0) {
1279 while (1) {
1280 *p = DEFAULT_SLASH;
1281 if (!n) break;
1282 --n;
1283 ++p;
1284 }
1285 break;
1286 }
1287 }
1288 }
1289
1290 if (p == buf) {
1291 ret = php_mkdir(dir, mode);
1292 } else if (!(ret = php_mkdir(buf, mode))) {
1293 if (!p) {
1294 p = buf;
1295 }
1296 /* create any needed directories if the creation of the 1st directory worked */
1297 while (++p != e) {
1298 if (*p == '\0') {
1299 *p = DEFAULT_SLASH;
1300 if ((*(p+1) != '\0') &&
1301 (ret = VCWD_MKDIR(buf, (mode_t)mode)) < 0) {
1302 if (options & REPORT_ERRORS) {
1303 php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1304 }
1305 break;
1306 }
1307 }
1308 }
1309 }
1310 }
1311 if (ret < 0) {
1312 /* Failure */
1313 return 0;
1314 } else {
1315 /* Success */
1316 return 1;
1317 }
1318 }
1319
php_plain_files_rmdir(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1320 static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1321 {
1322 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1323 url += sizeof("file://") - 1;
1324 }
1325
1326 if (php_check_open_basedir(url)) {
1327 return 0;
1328 }
1329
1330 #ifdef PHP_WIN32
1331 if (!php_win32_check_trailing_space(url, (int)strlen(url))) {
1332 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT));
1333 return 0;
1334 }
1335 #endif
1336
1337 if (VCWD_RMDIR(url) < 0) {
1338 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno));
1339 return 0;
1340 }
1341
1342 /* Clear stat cache (and realpath cache) */
1343 php_clear_stat_cache(1, NULL, 0);
1344
1345 return 1;
1346 }
1347
php_plain_files_metadata(php_stream_wrapper * wrapper,const char * url,int option,void * value,php_stream_context * context)1348 static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url, int option, void *value, php_stream_context *context)
1349 {
1350 struct utimbuf *newtime;
1351 #if !defined(WINDOWS) && !defined(NETWARE)
1352 uid_t uid;
1353 gid_t gid;
1354 #endif
1355 mode_t mode;
1356 int ret = 0;
1357 #ifdef PHP_WIN32
1358 int url_len = (int)strlen(url);
1359 #endif
1360
1361 #ifdef PHP_WIN32
1362 if (!php_win32_check_trailing_space(url, url_len)) {
1363 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT));
1364 return 0;
1365 }
1366 #endif
1367
1368 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1369 url += sizeof("file://") - 1;
1370 }
1371
1372 if (php_check_open_basedir(url)) {
1373 return 0;
1374 }
1375
1376 switch(option) {
1377 case PHP_STREAM_META_TOUCH:
1378 newtime = (struct utimbuf *)value;
1379 if (VCWD_ACCESS(url, F_OK) != 0) {
1380 FILE *file = VCWD_FOPEN(url, "w");
1381 if (file == NULL) {
1382 php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, strerror(errno));
1383 return 0;
1384 }
1385 fclose(file);
1386 }
1387
1388 ret = VCWD_UTIME(url, newtime);
1389 break;
1390 #if !defined(WINDOWS) && !defined(NETWARE)
1391 case PHP_STREAM_META_OWNER_NAME:
1392 case PHP_STREAM_META_OWNER:
1393 if(option == PHP_STREAM_META_OWNER_NAME) {
1394 if(php_get_uid_by_name((char *)value, &uid) != SUCCESS) {
1395 php_error_docref1(NULL, url, E_WARNING, "Unable to find uid for %s", (char *)value);
1396 return 0;
1397 }
1398 } else {
1399 uid = (uid_t)*(long *)value;
1400 }
1401 ret = VCWD_CHOWN(url, uid, -1);
1402 break;
1403 case PHP_STREAM_META_GROUP:
1404 case PHP_STREAM_META_GROUP_NAME:
1405 if(option == PHP_STREAM_META_GROUP_NAME) {
1406 if(php_get_gid_by_name((char *)value, &gid) != SUCCESS) {
1407 php_error_docref1(NULL, url, E_WARNING, "Unable to find gid for %s", (char *)value);
1408 return 0;
1409 }
1410 } else {
1411 gid = (gid_t)*(long *)value;
1412 }
1413 ret = VCWD_CHOWN(url, -1, gid);
1414 break;
1415 #endif
1416 case PHP_STREAM_META_ACCESS:
1417 mode = (mode_t)*(zend_long *)value;
1418 ret = VCWD_CHMOD(url, mode);
1419 break;
1420 default:
1421 php_error_docref1(NULL, url, E_WARNING, "Unknown option %d for stream_metadata", option);
1422 return 0;
1423 }
1424 if (ret == -1) {
1425 php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", strerror(errno));
1426 return 0;
1427 }
1428 php_clear_stat_cache(0, NULL, 0);
1429 return 1;
1430 }
1431
1432
1433 static php_stream_wrapper_ops php_plain_files_wrapper_ops = {
1434 php_plain_files_stream_opener,
1435 NULL,
1436 NULL,
1437 php_plain_files_url_stater,
1438 php_plain_files_dir_opener,
1439 "plainfile",
1440 php_plain_files_unlink,
1441 php_plain_files_rename,
1442 php_plain_files_mkdir,
1443 php_plain_files_rmdir,
1444 php_plain_files_metadata
1445 };
1446
1447 PHPAPI php_stream_wrapper php_plain_files_wrapper = {
1448 &php_plain_files_wrapper_ops,
1449 NULL,
1450 0
1451 };
1452
1453 /* {{{ php_stream_fopen_with_path */
_php_stream_fopen_with_path(const char * filename,const char * mode,const char * path,zend_string ** opened_path,int options STREAMS_DC)1454 PHPAPI php_stream *_php_stream_fopen_with_path(const char *filename, const char *mode, const char *path, zend_string **opened_path, int options STREAMS_DC)
1455 {
1456 /* code ripped off from fopen_wrappers.c */
1457 char *pathbuf, *end;
1458 const char *ptr;
1459 char trypath[MAXPATHLEN];
1460 php_stream *stream;
1461 int filename_length;
1462 zend_string *exec_filename;
1463
1464 if (opened_path) {
1465 *opened_path = NULL;
1466 }
1467
1468 if(!filename) {
1469 return NULL;
1470 }
1471
1472 filename_length = (int)strlen(filename);
1473 #ifndef PHP_WIN32
1474 (void) filename_length;
1475 #endif
1476
1477 /* Relative path open */
1478 if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) {
1479 /* further checks, we could have ....... filenames */
1480 ptr = filename + 1;
1481 if (*ptr == '.') {
1482 while (*(++ptr) == '.');
1483 if (!IS_SLASH(*ptr)) { /* not a relative path after all */
1484 goto not_relative_path;
1485 }
1486 }
1487
1488
1489 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename)) {
1490 return NULL;
1491 }
1492
1493 return php_stream_fopen_rel(filename, mode, opened_path, options);
1494 }
1495
1496 not_relative_path:
1497
1498 /* Absolute path open */
1499 if (IS_ABSOLUTE_PATH(filename, filename_length)) {
1500
1501 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename)) {
1502 return NULL;
1503 }
1504
1505 return php_stream_fopen_rel(filename, mode, opened_path, options);
1506 }
1507
1508 #ifdef PHP_WIN32
1509 if (IS_SLASH(filename[0])) {
1510 size_t cwd_len;
1511 char *cwd;
1512 cwd = virtual_getcwd_ex(&cwd_len);
1513 /* getcwd() will return always return [DRIVE_LETTER]:/) on windows. */
1514 *(cwd+3) = '\0';
1515
1516 if (snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename) >= MAXPATHLEN) {
1517 php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN);
1518 }
1519
1520 efree(cwd);
1521
1522 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(trypath)) {
1523 return NULL;
1524 }
1525
1526 return php_stream_fopen_rel(trypath, mode, opened_path, options);
1527 }
1528 #endif
1529
1530 if (!path || !*path) {
1531 return php_stream_fopen_rel(filename, mode, opened_path, options);
1532 }
1533
1534 /* check in provided path */
1535 /* append the calling scripts' current working directory
1536 * as a fall back case
1537 */
1538 if (zend_is_executing() &&
1539 (exec_filename = zend_get_executed_filename_ex()) != NULL) {
1540 const char *exec_fname = ZSTR_VAL(exec_filename);
1541 size_t exec_fname_length = ZSTR_LEN(exec_filename);
1542
1543 while ((--exec_fname_length < SIZE_MAX) && !IS_SLASH(exec_fname[exec_fname_length]));
1544 if (exec_fname_length<=0) {
1545 /* no path */
1546 pathbuf = estrdup(path);
1547 } else {
1548 size_t path_length = strlen(path);
1549
1550 pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1);
1551 memcpy(pathbuf, path, path_length);
1552 pathbuf[path_length] = DEFAULT_DIR_SEPARATOR;
1553 memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length);
1554 pathbuf[path_length + exec_fname_length +1] = '\0';
1555 }
1556 } else {
1557 pathbuf = estrdup(path);
1558 }
1559
1560 ptr = pathbuf;
1561
1562 while (ptr && *ptr) {
1563 end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
1564 if (end != NULL) {
1565 *end = '\0';
1566 end++;
1567 }
1568 if (*ptr == '\0') {
1569 goto stream_skip;
1570 }
1571 if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) {
1572 php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN);
1573 }
1574
1575 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0)) {
1576 goto stream_skip;
1577 }
1578
1579 stream = php_stream_fopen_rel(trypath, mode, opened_path, options);
1580 if (stream) {
1581 efree(pathbuf);
1582 return stream;
1583 }
1584 stream_skip:
1585 ptr = end;
1586 } /* end provided path */
1587
1588 efree(pathbuf);
1589 return NULL;
1590
1591 }
1592 /* }}} */
1593
1594 /*
1595 * Local variables:
1596 * tab-width: 4
1597 * c-basic-offset: 4
1598 * End:
1599 * vim600: noet sw=4 ts=4 fdm=marker
1600 * vim<600: noet sw=4 ts=4
1601 */
1602