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 #ifndef PHP_WIN32
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->offset > data->sb.st_size) {
703 range->offset = data->sb.st_size;
704 }
705 if (range->length == 0 ||
706 range->length > data->sb.st_size - range->offset) {
707 range->length = data->sb.st_size - range->offset;
708 }
709 switch (range->mode) {
710 case PHP_STREAM_MAP_MODE_READONLY:
711 prot = PROT_READ;
712 flags = MAP_PRIVATE;
713 break;
714 case PHP_STREAM_MAP_MODE_READWRITE:
715 prot = PROT_READ | PROT_WRITE;
716 flags = MAP_PRIVATE;
717 break;
718 case PHP_STREAM_MAP_MODE_SHARED_READONLY:
719 prot = PROT_READ;
720 flags = MAP_SHARED;
721 break;
722 case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
723 prot = PROT_READ | PROT_WRITE;
724 flags = MAP_SHARED;
725 break;
726 default:
727 return PHP_STREAM_OPTION_RETURN_ERR;
728 }
729 range->mapped = (char*)mmap(NULL, range->length, prot, flags, fd, range->offset);
730 if (range->mapped == (char*)MAP_FAILED) {
731 range->mapped = NULL;
732 return PHP_STREAM_OPTION_RETURN_ERR;
733 }
734 /* remember the mapping */
735 data->last_mapped_addr = range->mapped;
736 data->last_mapped_len = range->length;
737 return PHP_STREAM_OPTION_RETURN_OK;
738
739 case PHP_STREAM_MMAP_UNMAP:
740 if (data->last_mapped_addr) {
741 munmap(data->last_mapped_addr, data->last_mapped_len);
742 data->last_mapped_addr = NULL;
743
744 return PHP_STREAM_OPTION_RETURN_OK;
745 }
746 return PHP_STREAM_OPTION_RETURN_ERR;
747 }
748 }
749 #elif defined(PHP_WIN32)
750 {
751 php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
752 HANDLE hfile = (HANDLE)_get_osfhandle(fd);
753 DWORD prot, acc, loffs = 0, delta = 0;
754
755 switch (value) {
756 case PHP_STREAM_MMAP_SUPPORTED:
757 return hfile == INVALID_HANDLE_VALUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
758
759 case PHP_STREAM_MMAP_MAP_RANGE:
760 switch (range->mode) {
761 case PHP_STREAM_MAP_MODE_READONLY:
762 prot = PAGE_READONLY;
763 acc = FILE_MAP_READ;
764 break;
765 case PHP_STREAM_MAP_MODE_READWRITE:
766 prot = PAGE_READWRITE;
767 acc = FILE_MAP_READ | FILE_MAP_WRITE;
768 break;
769 case PHP_STREAM_MAP_MODE_SHARED_READONLY:
770 prot = PAGE_READONLY;
771 acc = FILE_MAP_READ;
772 /* TODO: we should assign a name for the mapping */
773 break;
774 case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
775 prot = PAGE_READWRITE;
776 acc = FILE_MAP_READ | FILE_MAP_WRITE;
777 /* TODO: we should assign a name for the mapping */
778 break;
779 default:
780 return PHP_STREAM_OPTION_RETURN_ERR;
781 }
782
783 /* create a mapping capable of viewing the whole file (this costs no real resources) */
784 data->file_mapping = CreateFileMapping(hfile, NULL, prot, 0, 0, NULL);
785
786 if (data->file_mapping == NULL) {
787 return PHP_STREAM_OPTION_RETURN_ERR;
788 }
789
790 size = GetFileSize(hfile, NULL);
791 if (range->length == 0 && range->offset > 0 && range->offset < size) {
792 range->length = size - range->offset;
793 }
794 if (range->length == 0 || range->length > size) {
795 range->length = size;
796 }
797 if (range->offset >= size) {
798 range->offset = size;
799 range->length = 0;
800 }
801
802 /* figure out how big a chunk to map to be able to view the part that we need */
803 if (range->offset != 0) {
804 SYSTEM_INFO info;
805 DWORD gran;
806
807 GetSystemInfo(&info);
808 gran = info.dwAllocationGranularity;
809 loffs = ((DWORD)range->offset / gran) * gran;
810 delta = (DWORD)range->offset - loffs;
811 }
812
813 data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, 0, loffs, range->length + delta);
814
815 if (data->last_mapped_addr) {
816 /* give them back the address of the start offset they requested */
817 range->mapped = data->last_mapped_addr + delta;
818 return PHP_STREAM_OPTION_RETURN_OK;
819 }
820
821 CloseHandle(data->file_mapping);
822 data->file_mapping = NULL;
823
824 return PHP_STREAM_OPTION_RETURN_ERR;
825
826 case PHP_STREAM_MMAP_UNMAP:
827 if (data->last_mapped_addr) {
828 UnmapViewOfFile(data->last_mapped_addr);
829 data->last_mapped_addr = NULL;
830 CloseHandle(data->file_mapping);
831 data->file_mapping = NULL;
832 return PHP_STREAM_OPTION_RETURN_OK;
833 }
834 return PHP_STREAM_OPTION_RETURN_ERR;
835
836 default:
837 return PHP_STREAM_OPTION_RETURN_ERR;
838 }
839 }
840
841 #endif
842 return PHP_STREAM_OPTION_RETURN_NOTIMPL;
843
844 case PHP_STREAM_OPTION_TRUNCATE_API:
845 switch (value) {
846 case PHP_STREAM_TRUNCATE_SUPPORTED:
847 return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
848
849 case PHP_STREAM_TRUNCATE_SET_SIZE: {
850 ptrdiff_t new_size = *(ptrdiff_t*)ptrparam;
851 if (new_size < 0) {
852 return PHP_STREAM_OPTION_RETURN_ERR;
853 }
854 return ftruncate(fd, new_size) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
855 }
856 }
857
858 #ifdef PHP_WIN32
859 case PHP_STREAM_OPTION_PIPE_BLOCKING:
860 data->is_pipe_blocking = value;
861 return PHP_STREAM_OPTION_RETURN_OK;
862 #endif
863 case PHP_STREAM_OPTION_META_DATA_API:
864 if (fd == -1)
865 return -1;
866 #ifdef O_NONBLOCK
867 flags = fcntl(fd, F_GETFL, 0);
868
869 add_assoc_bool((zval*)ptrparam, "timed_out", 0);
870 add_assoc_bool((zval*)ptrparam, "blocked", (flags & O_NONBLOCK)? 0 : 1);
871 add_assoc_bool((zval*)ptrparam, "eof", stream->eof);
872
873 return PHP_STREAM_OPTION_RETURN_OK;
874 #endif
875 return -1;
876 default:
877 return PHP_STREAM_OPTION_RETURN_NOTIMPL;
878 }
879 }
880
881 PHPAPI php_stream_ops php_stream_stdio_ops = {
882 php_stdiop_write, php_stdiop_read,
883 php_stdiop_close, php_stdiop_flush,
884 "STDIO",
885 php_stdiop_seek,
886 php_stdiop_cast,
887 php_stdiop_stat,
888 php_stdiop_set_option
889 };
890 /* }}} */
891
892 /* {{{ plain files opendir/readdir implementation */
php_plain_files_dirstream_read(php_stream * stream,char * buf,size_t count)893 static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count)
894 {
895 DIR *dir = (DIR*)stream->abstract;
896 /* avoid libc5 readdir problems */
897 char entry[sizeof(struct dirent)+MAXPATHLEN];
898 struct dirent *result = (struct dirent *)&entry;
899 php_stream_dirent *ent = (php_stream_dirent*)buf;
900
901 /* avoid problems if someone mis-uses the stream */
902 if (count != sizeof(php_stream_dirent))
903 return 0;
904
905 if (php_readdir_r(dir, (struct dirent *)entry, &result) == 0 && result) {
906 PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name));
907 return sizeof(php_stream_dirent);
908 }
909 return 0;
910 }
911
php_plain_files_dirstream_close(php_stream * stream,int close_handle)912 static int php_plain_files_dirstream_close(php_stream *stream, int close_handle)
913 {
914 return closedir((DIR *)stream->abstract);
915 }
916
php_plain_files_dirstream_rewind(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)917 static int php_plain_files_dirstream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
918 {
919 rewinddir((DIR *)stream->abstract);
920 return 0;
921 }
922
923 static php_stream_ops php_plain_files_dirstream_ops = {
924 NULL, php_plain_files_dirstream_read,
925 php_plain_files_dirstream_close, NULL,
926 "dir",
927 php_plain_files_dirstream_rewind,
928 NULL, /* cast */
929 NULL, /* stat */
930 NULL /* set_option */
931 };
932
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)933 static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
934 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
935 {
936 DIR *dir = NULL;
937 php_stream *stream = NULL;
938
939 #ifdef HAVE_GLOB
940 if (options & STREAM_USE_GLOB_DIR_OPEN) {
941 return php_glob_stream_wrapper.wops->dir_opener(&php_glob_stream_wrapper, path, mode, options, opened_path, context STREAMS_REL_CC);
942 }
943 #endif
944
945 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {
946 return NULL;
947 }
948
949 dir = VCWD_OPENDIR(path);
950
951 #ifdef PHP_WIN32
952 if (!dir) {
953 php_win32_docref2_from_error(GetLastError(), path, path);
954 }
955
956 if (dir && dir->finished) {
957 closedir(dir);
958 dir = NULL;
959 }
960 #endif
961 if (dir) {
962 stream = php_stream_alloc(&php_plain_files_dirstream_ops, dir, 0, mode);
963 if (stream == NULL)
964 closedir(dir);
965 }
966
967 return stream;
968 }
969 /* }}} */
970
971 /* {{{ php_stream_fopen */
_php_stream_fopen(const char * filename,const char * mode,zend_string ** opened_path,int options STREAMS_DC)972 PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zend_string **opened_path, int options STREAMS_DC)
973 {
974 char realpath[MAXPATHLEN];
975 int open_flags;
976 int fd;
977 php_stream *ret;
978 int persistent = options & STREAM_OPEN_PERSISTENT;
979 char *persistent_id = NULL;
980
981 if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) {
982 if (options & REPORT_ERRORS) {
983 php_error_docref(NULL, E_WARNING, "`%s' is not a valid mode for fopen", mode);
984 }
985 return NULL;
986 }
987
988 if (options & STREAM_ASSUME_REALPATH) {
989 strlcpy(realpath, filename, sizeof(realpath));
990 } else {
991 if (expand_filepath(filename, realpath) == NULL) {
992 return NULL;
993 }
994 }
995
996 if (persistent) {
997 spprintf(&persistent_id, 0, "streams_stdio_%d_%s", open_flags, realpath);
998 switch (php_stream_from_persistent_id(persistent_id, &ret)) {
999 case PHP_STREAM_PERSISTENT_SUCCESS:
1000 if (opened_path) {
1001 //TODO: avoid reallocation???
1002 *opened_path = zend_string_init(realpath, strlen(realpath), 0);
1003 }
1004 /* fall through */
1005
1006 case PHP_STREAM_PERSISTENT_FAILURE:
1007 efree(persistent_id);
1008 return ret;
1009 }
1010 }
1011 #ifdef PHP_WIN32
1012 fd = php_win32_ioutil_open(realpath, open_flags, 0666);
1013 #else
1014 fd = open(realpath, open_flags, 0666);
1015 #endif
1016 if (fd != -1) {
1017
1018 if (options & STREAM_OPEN_FOR_INCLUDE) {
1019 ret = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);
1020 } else {
1021 ret = php_stream_fopen_from_fd_rel(fd, mode, persistent_id);
1022 }
1023
1024 if (ret) {
1025 if (opened_path) {
1026 *opened_path = zend_string_init(realpath, strlen(realpath), 0);
1027 }
1028 if (persistent_id) {
1029 efree(persistent_id);
1030 }
1031
1032 /* WIN32 always set ISREG flag */
1033 #ifndef PHP_WIN32
1034 /* sanity checks for include/require.
1035 * We check these after opening the stream, so that we save
1036 * on fstat() syscalls */
1037 if (options & STREAM_OPEN_FOR_INCLUDE) {
1038 php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract;
1039 int r;
1040
1041 r = do_fstat(self, 0);
1042 if ((r == 0 && !S_ISREG(self->sb.st_mode))) {
1043 if (opened_path) {
1044 zend_string_release(*opened_path);
1045 *opened_path = NULL;
1046 }
1047 php_stream_close(ret);
1048 return NULL;
1049 }
1050 }
1051
1052 if (options & STREAM_USE_BLOCKING_PIPE) {
1053 php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract;
1054 self->is_pipe_blocking = 1;
1055 }
1056 #endif
1057
1058 return ret;
1059 }
1060 close(fd);
1061 }
1062 if (persistent_id) {
1063 efree(persistent_id);
1064 }
1065 return NULL;
1066 }
1067 /* }}} */
1068
1069
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)1070 static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
1071 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
1072 {
1073 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {
1074 return NULL;
1075 }
1076
1077 return php_stream_fopen_rel(path, mode, opened_path, options);
1078 }
1079
php_plain_files_url_stater(php_stream_wrapper * wrapper,const char * url,int flags,php_stream_statbuf * ssb,php_stream_context * context)1080 static int php_plain_files_url_stater(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context)
1081 {
1082 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1083 url += sizeof("file://") - 1;
1084 }
1085
1086 if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1)) {
1087 return -1;
1088 }
1089
1090 #ifdef PHP_WIN32
1091 if (flags & PHP_STREAM_URL_STAT_LINK) {
1092 return VCWD_LSTAT(url, &ssb->sb);
1093 }
1094 #else
1095 # ifdef HAVE_SYMLINK
1096 if (flags & PHP_STREAM_URL_STAT_LINK) {
1097 return VCWD_LSTAT(url, &ssb->sb);
1098 } else
1099 # endif
1100 #endif
1101 return VCWD_STAT(url, &ssb->sb);
1102 }
1103
php_plain_files_unlink(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1104 static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1105 {
1106 int ret;
1107
1108 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1109 url += sizeof("file://") - 1;
1110 }
1111
1112 if (php_check_open_basedir(url)) {
1113 return 0;
1114 }
1115
1116 ret = VCWD_UNLINK(url);
1117 if (ret == -1) {
1118 if (options & REPORT_ERRORS) {
1119 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno));
1120 }
1121 return 0;
1122 }
1123
1124 /* Clear stat cache (and realpath cache) */
1125 php_clear_stat_cache(1, NULL, 0);
1126
1127 return 1;
1128 }
1129
php_plain_files_rename(php_stream_wrapper * wrapper,const char * url_from,const char * url_to,int options,php_stream_context * context)1130 static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context)
1131 {
1132 int ret;
1133
1134 if (!url_from || !url_to) {
1135 return 0;
1136 }
1137
1138 #ifdef PHP_WIN32
1139 if (!php_win32_check_trailing_space(url_from, strlen(url_from))) {
1140 php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);
1141 return 0;
1142 }
1143 if (!php_win32_check_trailing_space(url_to, strlen(url_to))) {
1144 php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);
1145 return 0;
1146 }
1147 #endif
1148
1149 if (strncasecmp(url_from, "file://", sizeof("file://") - 1) == 0) {
1150 url_from += sizeof("file://") - 1;
1151 }
1152
1153 if (strncasecmp(url_to, "file://", sizeof("file://") - 1) == 0) {
1154 url_to += sizeof("file://") - 1;
1155 }
1156
1157 if (php_check_open_basedir(url_from) || php_check_open_basedir(url_to)) {
1158 return 0;
1159 }
1160
1161 ret = VCWD_RENAME(url_from, url_to);
1162
1163 if (ret == -1) {
1164 #ifndef PHP_WIN32
1165 # ifdef EXDEV
1166 if (errno == EXDEV) {
1167 zend_stat_t sb;
1168 # if !defined(ZTS) && !defined(TSRM_WIN32)
1169 /* not sure what to do in ZTS case, umask is not thread-safe */
1170 int oldmask = umask(077);
1171 # endif
1172 int success = 0;
1173 if (php_copy_file(url_from, url_to) == SUCCESS) {
1174 if (VCWD_STAT(url_from, &sb) == 0) {
1175 success = 1;
1176 # if !defined(TSRM_WIN32)
1177 /*
1178 * Try to set user and permission info on the target.
1179 * If we're not root, then some of these may fail.
1180 * We try chown first, to set proper group info, relying
1181 * on the system environment to have proper umask to not allow
1182 * access to the file in the meantime.
1183 */
1184 if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) {
1185 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1186 if (errno != EPERM) {
1187 success = 0;
1188 }
1189 }
1190
1191 if (success) {
1192 if (VCWD_CHMOD(url_to, sb.st_mode)) {
1193 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1194 if (errno != EPERM) {
1195 success = 0;
1196 }
1197 }
1198 }
1199 # endif
1200 if (success) {
1201 VCWD_UNLINK(url_from);
1202 }
1203 } else {
1204 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1205 }
1206 } else {
1207 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1208 }
1209 # if !defined(ZTS) && !defined(TSRM_WIN32)
1210 umask(oldmask);
1211 # endif
1212 return success;
1213 }
1214 # endif
1215 #endif
1216
1217 #ifdef PHP_WIN32
1218 php_win32_docref2_from_error(GetLastError(), url_from, url_to);
1219 #else
1220 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1221 #endif
1222 return 0;
1223 }
1224
1225 /* Clear stat cache (and realpath cache) */
1226 php_clear_stat_cache(1, NULL, 0);
1227
1228 return 1;
1229 }
1230
php_plain_files_mkdir(php_stream_wrapper * wrapper,const char * dir,int mode,int options,php_stream_context * context)1231 static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, int mode, int options, php_stream_context *context)
1232 {
1233 int ret, recursive = options & PHP_STREAM_MKDIR_RECURSIVE;
1234 char *p;
1235
1236 if (strncasecmp(dir, "file://", sizeof("file://") - 1) == 0) {
1237 dir += sizeof("file://") - 1;
1238 }
1239
1240 if (!recursive) {
1241 ret = php_mkdir(dir, mode);
1242 } else {
1243 /* we look for directory separator from the end of string, thus hopefuly reducing our work load */
1244 char *e;
1245 zend_stat_t sb;
1246 size_t dir_len = strlen(dir), offset = 0;
1247 char buf[MAXPATHLEN];
1248
1249 if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND )) {
1250 php_error_docref(NULL, E_WARNING, "Invalid path");
1251 return 0;
1252 }
1253
1254 e = buf + strlen(buf);
1255
1256 if ((p = memchr(buf, DEFAULT_SLASH, dir_len))) {
1257 offset = p - buf + 1;
1258 }
1259
1260 if (p && dir_len == 1) {
1261 /* buf == "DEFAULT_SLASH" */
1262 }
1263 else {
1264 /* find a top level directory we need to create */
1265 while ( (p = strrchr(buf + offset, DEFAULT_SLASH)) || (offset != 1 && (p = strrchr(buf, DEFAULT_SLASH))) ) {
1266 int n = 0;
1267
1268 *p = '\0';
1269 while (p > buf && *(p-1) == DEFAULT_SLASH) {
1270 ++n;
1271 --p;
1272 *p = '\0';
1273 }
1274 if (VCWD_STAT(buf, &sb) == 0) {
1275 while (1) {
1276 *p = DEFAULT_SLASH;
1277 if (!n) break;
1278 --n;
1279 ++p;
1280 }
1281 break;
1282 }
1283 }
1284 }
1285
1286 if (p == buf) {
1287 ret = php_mkdir(dir, mode);
1288 } else if (!(ret = php_mkdir(buf, mode))) {
1289 if (!p) {
1290 p = buf;
1291 }
1292 /* create any needed directories if the creation of the 1st directory worked */
1293 while (++p != e) {
1294 if (*p == '\0') {
1295 *p = DEFAULT_SLASH;
1296 if ((*(p+1) != '\0') &&
1297 (ret = VCWD_MKDIR(buf, (mode_t)mode)) < 0) {
1298 if (options & REPORT_ERRORS) {
1299 php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1300 }
1301 break;
1302 }
1303 }
1304 }
1305 }
1306 }
1307 if (ret < 0) {
1308 /* Failure */
1309 return 0;
1310 } else {
1311 /* Success */
1312 return 1;
1313 }
1314 }
1315
php_plain_files_rmdir(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1316 static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1317 {
1318 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1319 url += sizeof("file://") - 1;
1320 }
1321
1322 if (php_check_open_basedir(url)) {
1323 return 0;
1324 }
1325
1326 #ifdef PHP_WIN32
1327 if (!php_win32_check_trailing_space(url, strlen(url))) {
1328 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT));
1329 return 0;
1330 }
1331 #endif
1332
1333 if (VCWD_RMDIR(url) < 0) {
1334 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno));
1335 return 0;
1336 }
1337
1338 /* Clear stat cache (and realpath cache) */
1339 php_clear_stat_cache(1, NULL, 0);
1340
1341 return 1;
1342 }
1343
php_plain_files_metadata(php_stream_wrapper * wrapper,const char * url,int option,void * value,php_stream_context * context)1344 static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url, int option, void *value, php_stream_context *context)
1345 {
1346 struct utimbuf *newtime;
1347 #ifndef PHP_WIN32
1348 uid_t uid;
1349 gid_t gid;
1350 #endif
1351 mode_t mode;
1352 int ret = 0;
1353
1354 #ifdef PHP_WIN32
1355 if (!php_win32_check_trailing_space(url, strlen(url))) {
1356 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT));
1357 return 0;
1358 }
1359 #endif
1360
1361 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1362 url += sizeof("file://") - 1;
1363 }
1364
1365 if (php_check_open_basedir(url)) {
1366 return 0;
1367 }
1368
1369 switch(option) {
1370 case PHP_STREAM_META_TOUCH:
1371 newtime = (struct utimbuf *)value;
1372 if (VCWD_ACCESS(url, F_OK) != 0) {
1373 FILE *file = VCWD_FOPEN(url, "w");
1374 if (file == NULL) {
1375 php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, strerror(errno));
1376 return 0;
1377 }
1378 fclose(file);
1379 }
1380
1381 ret = VCWD_UTIME(url, newtime);
1382 break;
1383 #ifndef PHP_WIN32
1384 case PHP_STREAM_META_OWNER_NAME:
1385 case PHP_STREAM_META_OWNER:
1386 if(option == PHP_STREAM_META_OWNER_NAME) {
1387 if(php_get_uid_by_name((char *)value, &uid) != SUCCESS) {
1388 php_error_docref1(NULL, url, E_WARNING, "Unable to find uid for %s", (char *)value);
1389 return 0;
1390 }
1391 } else {
1392 uid = (uid_t)*(long *)value;
1393 }
1394 ret = VCWD_CHOWN(url, uid, -1);
1395 break;
1396 case PHP_STREAM_META_GROUP:
1397 case PHP_STREAM_META_GROUP_NAME:
1398 if(option == PHP_STREAM_META_GROUP_NAME) {
1399 if(php_get_gid_by_name((char *)value, &gid) != SUCCESS) {
1400 php_error_docref1(NULL, url, E_WARNING, "Unable to find gid for %s", (char *)value);
1401 return 0;
1402 }
1403 } else {
1404 gid = (gid_t)*(long *)value;
1405 }
1406 ret = VCWD_CHOWN(url, -1, gid);
1407 break;
1408 #endif
1409 case PHP_STREAM_META_ACCESS:
1410 mode = (mode_t)*(zend_long *)value;
1411 ret = VCWD_CHMOD(url, mode);
1412 break;
1413 default:
1414 php_error_docref1(NULL, url, E_WARNING, "Unknown option %d for stream_metadata", option);
1415 return 0;
1416 }
1417 if (ret == -1) {
1418 php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", strerror(errno));
1419 return 0;
1420 }
1421 php_clear_stat_cache(0, NULL, 0);
1422 return 1;
1423 }
1424
1425
1426 static php_stream_wrapper_ops php_plain_files_wrapper_ops = {
1427 php_plain_files_stream_opener,
1428 NULL,
1429 NULL,
1430 php_plain_files_url_stater,
1431 php_plain_files_dir_opener,
1432 "plainfile",
1433 php_plain_files_unlink,
1434 php_plain_files_rename,
1435 php_plain_files_mkdir,
1436 php_plain_files_rmdir,
1437 php_plain_files_metadata
1438 };
1439
1440 PHPAPI php_stream_wrapper php_plain_files_wrapper = {
1441 &php_plain_files_wrapper_ops,
1442 NULL,
1443 0
1444 };
1445
1446 /* {{{ 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)1447 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)
1448 {
1449 /* code ripped off from fopen_wrappers.c */
1450 char *pathbuf, *end;
1451 const char *ptr;
1452 char trypath[MAXPATHLEN];
1453 php_stream *stream;
1454 size_t filename_length;
1455 zend_string *exec_filename;
1456
1457 if (opened_path) {
1458 *opened_path = NULL;
1459 }
1460
1461 if(!filename) {
1462 return NULL;
1463 }
1464
1465 filename_length = strlen(filename);
1466 #ifndef PHP_WIN32
1467 (void) filename_length;
1468 #endif
1469
1470 /* Relative path open */
1471 if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) {
1472 /* further checks, we could have ....... filenames */
1473 ptr = filename + 1;
1474 if (*ptr == '.') {
1475 while (*(++ptr) == '.');
1476 if (!IS_SLASH(*ptr)) { /* not a relative path after all */
1477 goto not_relative_path;
1478 }
1479 }
1480
1481
1482 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename)) {
1483 return NULL;
1484 }
1485
1486 return php_stream_fopen_rel(filename, mode, opened_path, options);
1487 }
1488
1489 not_relative_path:
1490
1491 /* Absolute path open */
1492 if (IS_ABSOLUTE_PATH(filename, filename_length)) {
1493
1494 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename)) {
1495 return NULL;
1496 }
1497
1498 return php_stream_fopen_rel(filename, mode, opened_path, options);
1499 }
1500
1501 #ifdef PHP_WIN32
1502 if (IS_SLASH(filename[0])) {
1503 size_t cwd_len;
1504 char *cwd;
1505 cwd = virtual_getcwd_ex(&cwd_len);
1506 /* getcwd() will return always return [DRIVE_LETTER]:/) on windows. */
1507 *(cwd+3) = '\0';
1508
1509 if (snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename) >= MAXPATHLEN) {
1510 php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN);
1511 }
1512
1513 efree(cwd);
1514
1515 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(trypath)) {
1516 return NULL;
1517 }
1518
1519 return php_stream_fopen_rel(trypath, mode, opened_path, options);
1520 }
1521 #endif
1522
1523 if (!path || !*path) {
1524 return php_stream_fopen_rel(filename, mode, opened_path, options);
1525 }
1526
1527 /* check in provided path */
1528 /* append the calling scripts' current working directory
1529 * as a fall back case
1530 */
1531 if (zend_is_executing() &&
1532 (exec_filename = zend_get_executed_filename_ex()) != NULL) {
1533 const char *exec_fname = ZSTR_VAL(exec_filename);
1534 size_t exec_fname_length = ZSTR_LEN(exec_filename);
1535
1536 while ((--exec_fname_length < SIZE_MAX) && !IS_SLASH(exec_fname[exec_fname_length]));
1537 if (exec_fname_length<=0) {
1538 /* no path */
1539 pathbuf = estrdup(path);
1540 } else {
1541 size_t path_length = strlen(path);
1542
1543 pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1);
1544 memcpy(pathbuf, path, path_length);
1545 pathbuf[path_length] = DEFAULT_DIR_SEPARATOR;
1546 memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length);
1547 pathbuf[path_length + exec_fname_length +1] = '\0';
1548 }
1549 } else {
1550 pathbuf = estrdup(path);
1551 }
1552
1553 ptr = pathbuf;
1554
1555 while (ptr && *ptr) {
1556 end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
1557 if (end != NULL) {
1558 *end = '\0';
1559 end++;
1560 }
1561 if (*ptr == '\0') {
1562 goto stream_skip;
1563 }
1564 if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) {
1565 php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN);
1566 }
1567
1568 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0)) {
1569 goto stream_skip;
1570 }
1571
1572 stream = php_stream_fopen_rel(trypath, mode, opened_path, options);
1573 if (stream) {
1574 efree(pathbuf);
1575 return stream;
1576 }
1577 stream_skip:
1578 ptr = end;
1579 } /* end provided path */
1580
1581 efree(pathbuf);
1582 return NULL;
1583
1584 }
1585 /* }}} */
1586
1587 /*
1588 * Local variables:
1589 * tab-width: 4
1590 * c-basic-offset: 4
1591 * End:
1592 * vim600: noet sw=4 ts=4 fdm=marker
1593 * vim<600: noet sw=4 ts=4
1594 */
1595