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