1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 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 #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 #if !defined(WINDOWS) && !defined(NETWARE)
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_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 unlink(ZSTR_VAL(data->temp_name));
475 /* temporary streams are never persistent */
476 zend_string_release(data->temp_name);
477 data->temp_name = NULL;
478 }
479 } else {
480 ret = 0;
481 data->file = NULL;
482 data->fd = -1;
483 }
484
485 pefree(data, stream->is_persistent);
486
487 return ret;
488 }
489
php_stdiop_flush(php_stream * stream)490 static int php_stdiop_flush(php_stream *stream)
491 {
492 php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
493
494 assert(data != NULL);
495
496 /*
497 * stdio buffers data in user land. By calling fflush(3), this
498 * data is send to the kernel using write(2). fsync'ing is
499 * something completely different.
500 */
501 if (data->file) {
502 return fflush(data->file);
503 }
504 return 0;
505 }
506
php_stdiop_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffset)507 static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
508 {
509 php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
510 int ret;
511
512 assert(data != NULL);
513
514 if (data->is_pipe) {
515 php_error_docref(NULL, E_WARNING, "cannot seek on a pipe");
516 return -1;
517 }
518
519 if (data->fd >= 0) {
520 zend_off_t result;
521
522 result = zend_lseek(data->fd, offset, whence);
523 if (result == (zend_off_t)-1)
524 return -1;
525
526 *newoffset = result;
527 return 0;
528
529 } else {
530 ret = zend_fseek(data->file, offset, whence);
531 *newoffset = zend_ftell(data->file);
532 return ret;
533 }
534 }
535
php_stdiop_cast(php_stream * stream,int castas,void ** ret)536 static int php_stdiop_cast(php_stream *stream, int castas, void **ret)
537 {
538 php_socket_t fd;
539 php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
540
541 assert(data != NULL);
542
543 /* as soon as someone touches the stdio layer, buffering may ensue,
544 * so we need to stop using the fd directly in that case */
545
546 switch (castas) {
547 case PHP_STREAM_AS_STDIO:
548 if (ret) {
549
550 if (data->file == NULL) {
551 /* we were opened as a plain file descriptor, so we
552 * need fdopen now */
553 char fixed_mode[5];
554 php_stream_mode_sanitize_fdopen_fopencookie(stream, fixed_mode);
555 data->file = fdopen(data->fd, fixed_mode);
556 if (data->file == NULL) {
557 return FAILURE;
558 }
559 }
560
561 *(FILE**)ret = data->file;
562 data->fd = SOCK_ERR;
563 }
564 return SUCCESS;
565
566 case PHP_STREAM_AS_FD_FOR_SELECT:
567 PHP_STDIOP_GET_FD(fd, data);
568 if (SOCK_ERR == fd) {
569 return FAILURE;
570 }
571 if (ret) {
572 *(php_socket_t *)ret = fd;
573 }
574 return SUCCESS;
575
576 case PHP_STREAM_AS_FD:
577 PHP_STDIOP_GET_FD(fd, data);
578
579 if (SOCK_ERR == fd) {
580 return FAILURE;
581 }
582 if (data->file) {
583 fflush(data->file);
584 }
585 if (ret) {
586 *(php_socket_t *)ret = fd;
587 }
588 return SUCCESS;
589 default:
590 return FAILURE;
591 }
592 }
593
php_stdiop_stat(php_stream * stream,php_stream_statbuf * ssb)594 static int php_stdiop_stat(php_stream *stream, php_stream_statbuf *ssb)
595 {
596 int ret;
597 php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
598
599 assert(data != NULL);
600 if((ret = do_fstat(data, 1)) == 0) {
601 memcpy(&ssb->sb, &data->sb, sizeof(ssb->sb));
602 }
603
604 return ret;
605 }
606
php_stdiop_set_option(php_stream * stream,int option,int value,void * ptrparam)607 static int php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam)
608 {
609 php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
610 size_t size;
611 int fd;
612 #ifdef O_NONBLOCK
613 /* FIXME: make this work for win32 */
614 int flags;
615 int oldval;
616 #endif
617
618 PHP_STDIOP_GET_FD(fd, data);
619
620 switch(option) {
621 case PHP_STREAM_OPTION_BLOCKING:
622 if (fd == -1)
623 return -1;
624 #ifdef O_NONBLOCK
625 flags = fcntl(fd, F_GETFL, 0);
626 oldval = (flags & O_NONBLOCK) ? 0 : 1;
627 if (value)
628 flags &= ~O_NONBLOCK;
629 else
630 flags |= O_NONBLOCK;
631
632 if (-1 == fcntl(fd, F_SETFL, flags))
633 return -1;
634 return oldval;
635 #else
636 return -1; /* not yet implemented */
637 #endif
638
639 case PHP_STREAM_OPTION_WRITE_BUFFER:
640
641 if (data->file == NULL) {
642 return -1;
643 }
644
645 if (ptrparam)
646 size = *(size_t *)ptrparam;
647 else
648 size = BUFSIZ;
649
650 switch(value) {
651 case PHP_STREAM_BUFFER_NONE:
652 return setvbuf(data->file, NULL, _IONBF, 0);
653
654 case PHP_STREAM_BUFFER_LINE:
655 return setvbuf(data->file, NULL, _IOLBF, size);
656
657 case PHP_STREAM_BUFFER_FULL:
658 return setvbuf(data->file, NULL, _IOFBF, size);
659
660 default:
661 return -1;
662 }
663 break;
664
665 case PHP_STREAM_OPTION_LOCKING:
666 if (fd == -1) {
667 return -1;
668 }
669
670 if ((zend_uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) {
671 return 0;
672 }
673
674 if (!flock(fd, value)) {
675 data->lock_flag = value;
676 return 0;
677 } else {
678 return -1;
679 }
680 break;
681
682 case PHP_STREAM_OPTION_MMAP_API:
683 #if HAVE_MMAP
684 {
685 php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
686 int prot, flags;
687
688 switch (value) {
689 case PHP_STREAM_MMAP_SUPPORTED:
690 return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
691
692 case PHP_STREAM_MMAP_MAP_RANGE:
693 if(do_fstat(data, 1) != 0) {
694 return PHP_STREAM_OPTION_RETURN_ERR;
695 }
696 if (range->length == 0 && range->offset > 0 && range->offset < data->sb.st_size) {
697 range->length = data->sb.st_size - range->offset;
698 }
699 if (range->length == 0 || range->length > data->sb.st_size) {
700 range->length = data->sb.st_size;
701 }
702 if (range->offset >= data->sb.st_size) {
703 range->offset = data->sb.st_size;
704 range->length = 0;
705 }
706 switch (range->mode) {
707 case PHP_STREAM_MAP_MODE_READONLY:
708 prot = PROT_READ;
709 flags = MAP_PRIVATE;
710 break;
711 case PHP_STREAM_MAP_MODE_READWRITE:
712 prot = PROT_READ | PROT_WRITE;
713 flags = MAP_PRIVATE;
714 break;
715 case PHP_STREAM_MAP_MODE_SHARED_READONLY:
716 prot = PROT_READ;
717 flags = MAP_SHARED;
718 break;
719 case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
720 prot = PROT_READ | PROT_WRITE;
721 flags = MAP_SHARED;
722 break;
723 default:
724 return PHP_STREAM_OPTION_RETURN_ERR;
725 }
726 range->mapped = (char*)mmap(NULL, range->length, prot, flags, fd, range->offset);
727 if (range->mapped == (char*)MAP_FAILED) {
728 range->mapped = NULL;
729 return PHP_STREAM_OPTION_RETURN_ERR;
730 }
731 /* remember the mapping */
732 data->last_mapped_addr = range->mapped;
733 data->last_mapped_len = range->length;
734 return PHP_STREAM_OPTION_RETURN_OK;
735
736 case PHP_STREAM_MMAP_UNMAP:
737 if (data->last_mapped_addr) {
738 munmap(data->last_mapped_addr, data->last_mapped_len);
739 data->last_mapped_addr = NULL;
740
741 return PHP_STREAM_OPTION_RETURN_OK;
742 }
743 return PHP_STREAM_OPTION_RETURN_ERR;
744 }
745 }
746 #elif defined(PHP_WIN32)
747 {
748 php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
749 HANDLE hfile = (HANDLE)_get_osfhandle(fd);
750 DWORD prot, acc, loffs = 0, delta = 0;
751
752 switch (value) {
753 case PHP_STREAM_MMAP_SUPPORTED:
754 return hfile == INVALID_HANDLE_VALUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
755
756 case PHP_STREAM_MMAP_MAP_RANGE:
757 switch (range->mode) {
758 case PHP_STREAM_MAP_MODE_READONLY:
759 prot = PAGE_READONLY;
760 acc = FILE_MAP_READ;
761 break;
762 case PHP_STREAM_MAP_MODE_READWRITE:
763 prot = PAGE_READWRITE;
764 acc = FILE_MAP_READ | FILE_MAP_WRITE;
765 break;
766 case PHP_STREAM_MAP_MODE_SHARED_READONLY:
767 prot = PAGE_READONLY;
768 acc = FILE_MAP_READ;
769 /* TODO: we should assign a name for the mapping */
770 break;
771 case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
772 prot = PAGE_READWRITE;
773 acc = FILE_MAP_READ | FILE_MAP_WRITE;
774 /* TODO: we should assign a name for the mapping */
775 break;
776 default:
777 return PHP_STREAM_OPTION_RETURN_ERR;
778 }
779
780 /* create a mapping capable of viewing the whole file (this costs no real resources) */
781 data->file_mapping = CreateFileMapping(hfile, NULL, prot, 0, 0, NULL);
782
783 if (data->file_mapping == NULL) {
784 return PHP_STREAM_OPTION_RETURN_ERR;
785 }
786
787 size = GetFileSize(hfile, NULL);
788 if (range->length == 0 && range->offset > 0 && range->offset < size) {
789 range->length = size - range->offset;
790 }
791 if (range->length == 0 || range->length > size) {
792 range->length = size;
793 }
794 if (range->offset >= size) {
795 range->offset = size;
796 range->length = 0;
797 }
798
799 /* figure out how big a chunk to map to be able to view the part that we need */
800 if (range->offset != 0) {
801 SYSTEM_INFO info;
802 DWORD gran;
803
804 GetSystemInfo(&info);
805 gran = info.dwAllocationGranularity;
806 loffs = ((DWORD)range->offset / gran) * gran;
807 delta = (DWORD)range->offset - loffs;
808 }
809
810 data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, 0, loffs, range->length + delta);
811
812 if (data->last_mapped_addr) {
813 /* give them back the address of the start offset they requested */
814 range->mapped = data->last_mapped_addr + delta;
815 return PHP_STREAM_OPTION_RETURN_OK;
816 }
817
818 CloseHandle(data->file_mapping);
819 data->file_mapping = NULL;
820
821 return PHP_STREAM_OPTION_RETURN_ERR;
822
823 case PHP_STREAM_MMAP_UNMAP:
824 if (data->last_mapped_addr) {
825 UnmapViewOfFile(data->last_mapped_addr);
826 data->last_mapped_addr = NULL;
827 CloseHandle(data->file_mapping);
828 data->file_mapping = NULL;
829 return PHP_STREAM_OPTION_RETURN_OK;
830 }
831 return PHP_STREAM_OPTION_RETURN_ERR;
832
833 default:
834 return PHP_STREAM_OPTION_RETURN_ERR;
835 }
836 }
837
838 #endif
839 return PHP_STREAM_OPTION_RETURN_NOTIMPL;
840
841 case PHP_STREAM_OPTION_TRUNCATE_API:
842 switch (value) {
843 case PHP_STREAM_TRUNCATE_SUPPORTED:
844 return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
845
846 case PHP_STREAM_TRUNCATE_SET_SIZE: {
847 ptrdiff_t new_size = *(ptrdiff_t*)ptrparam;
848 if (new_size < 0) {
849 return PHP_STREAM_OPTION_RETURN_ERR;
850 }
851 return ftruncate(fd, new_size) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
852 }
853 }
854
855 #ifdef PHP_WIN32
856 case PHP_STREAM_OPTION_PIPE_BLOCKING:
857 data->is_pipe_blocking = value;
858 return PHP_STREAM_OPTION_RETURN_OK;
859 #endif
860 case PHP_STREAM_OPTION_META_DATA_API:
861 if (fd == -1)
862 return -1;
863 #ifdef O_NONBLOCK
864 flags = fcntl(fd, F_GETFL, 0);
865
866 add_assoc_bool((zval*)ptrparam, "timed_out", 0);
867 add_assoc_bool((zval*)ptrparam, "blocked", (flags & O_NONBLOCK)? 0 : 1);
868 add_assoc_bool((zval*)ptrparam, "eof", stream->eof);
869
870 return PHP_STREAM_OPTION_RETURN_OK;
871 #endif
872 return -1;
873 default:
874 return PHP_STREAM_OPTION_RETURN_NOTIMPL;
875 }
876 }
877
878 PHPAPI php_stream_ops php_stream_stdio_ops = {
879 php_stdiop_write, php_stdiop_read,
880 php_stdiop_close, php_stdiop_flush,
881 "STDIO",
882 php_stdiop_seek,
883 php_stdiop_cast,
884 php_stdiop_stat,
885 php_stdiop_set_option
886 };
887 /* }}} */
888
889 /* {{{ plain files opendir/readdir implementation */
php_plain_files_dirstream_read(php_stream * stream,char * buf,size_t count)890 static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count)
891 {
892 DIR *dir = (DIR*)stream->abstract;
893 /* avoid libc5 readdir problems */
894 char entry[sizeof(struct dirent)+MAXPATHLEN];
895 struct dirent *result = (struct dirent *)&entry;
896 php_stream_dirent *ent = (php_stream_dirent*)buf;
897
898 /* avoid problems if someone mis-uses the stream */
899 if (count != sizeof(php_stream_dirent))
900 return 0;
901
902 if (php_readdir_r(dir, (struct dirent *)entry, &result) == 0 && result) {
903 PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name));
904 return sizeof(php_stream_dirent);
905 }
906 return 0;
907 }
908
php_plain_files_dirstream_close(php_stream * stream,int close_handle)909 static int php_plain_files_dirstream_close(php_stream *stream, int close_handle)
910 {
911 return closedir((DIR *)stream->abstract);
912 }
913
php_plain_files_dirstream_rewind(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)914 static int php_plain_files_dirstream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
915 {
916 rewinddir((DIR *)stream->abstract);
917 return 0;
918 }
919
920 static php_stream_ops php_plain_files_dirstream_ops = {
921 NULL, php_plain_files_dirstream_read,
922 php_plain_files_dirstream_close, NULL,
923 "dir",
924 php_plain_files_dirstream_rewind,
925 NULL, /* cast */
926 NULL, /* stat */
927 NULL /* set_option */
928 };
929
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)930 static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
931 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
932 {
933 DIR *dir = NULL;
934 php_stream *stream = NULL;
935
936 #ifdef HAVE_GLOB
937 if (options & STREAM_USE_GLOB_DIR_OPEN) {
938 return php_glob_stream_wrapper.wops->dir_opener(&php_glob_stream_wrapper, path, mode, options, opened_path, context STREAMS_REL_CC);
939 }
940 #endif
941
942 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {
943 return NULL;
944 }
945
946 dir = VCWD_OPENDIR(path);
947
948 #ifdef PHP_WIN32
949 if (!dir) {
950 php_win32_docref2_from_error(GetLastError(), path, path);
951 }
952
953 if (dir && dir->finished) {
954 closedir(dir);
955 dir = NULL;
956 }
957 #endif
958 if (dir) {
959 stream = php_stream_alloc(&php_plain_files_dirstream_ops, dir, 0, mode);
960 if (stream == NULL)
961 closedir(dir);
962 }
963
964 return stream;
965 }
966 /* }}} */
967
968 /* {{{ php_stream_fopen */
_php_stream_fopen(const char * filename,const char * mode,zend_string ** opened_path,int options STREAMS_DC)969 PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zend_string **opened_path, int options STREAMS_DC)
970 {
971 char realpath[MAXPATHLEN];
972 int open_flags;
973 int fd;
974 php_stream *ret;
975 int persistent = options & STREAM_OPEN_PERSISTENT;
976 char *persistent_id = NULL;
977
978 if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) {
979 if (options & REPORT_ERRORS) {
980 php_error_docref(NULL, E_WARNING, "`%s' is not a valid mode for fopen", mode);
981 }
982 return NULL;
983 }
984
985 if (options & STREAM_ASSUME_REALPATH) {
986 strlcpy(realpath, filename, sizeof(realpath));
987 } else {
988 if (expand_filepath(filename, realpath) == NULL) {
989 return NULL;
990 }
991 }
992
993 if (persistent) {
994 spprintf(&persistent_id, 0, "streams_stdio_%d_%s", open_flags, realpath);
995 switch (php_stream_from_persistent_id(persistent_id, &ret)) {
996 case PHP_STREAM_PERSISTENT_SUCCESS:
997 if (opened_path) {
998 //TODO: avoid reallocation???
999 *opened_path = zend_string_init(realpath, strlen(realpath), 0);
1000 }
1001 /* fall through */
1002
1003 case PHP_STREAM_PERSISTENT_FAILURE:
1004 efree(persistent_id);;
1005 return ret;
1006 }
1007 }
1008
1009 fd = open(realpath, open_flags, 0666);
1010
1011 if (fd != -1) {
1012
1013 if (options & STREAM_OPEN_FOR_INCLUDE) {
1014 ret = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);
1015 } else {
1016 ret = php_stream_fopen_from_fd_rel(fd, mode, persistent_id);
1017 }
1018
1019 if (ret) {
1020 if (opened_path) {
1021 *opened_path = zend_string_init(realpath, strlen(realpath), 0);
1022 }
1023 if (persistent_id) {
1024 efree(persistent_id);
1025 }
1026
1027 /* WIN32 always set ISREG flag */
1028 #ifndef PHP_WIN32
1029 /* sanity checks for include/require.
1030 * We check these after opening the stream, so that we save
1031 * on fstat() syscalls */
1032 if (options & STREAM_OPEN_FOR_INCLUDE) {
1033 php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract;
1034 int r;
1035
1036 r = do_fstat(self, 0);
1037 if ((r == 0 && !S_ISREG(self->sb.st_mode))) {
1038 if (opened_path) {
1039 zend_string_release(*opened_path);
1040 *opened_path = NULL;
1041 }
1042 php_stream_close(ret);
1043 return NULL;
1044 }
1045 }
1046
1047 if (options & STREAM_USE_BLOCKING_PIPE) {
1048 php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract;
1049 self->is_pipe_blocking = 1;
1050 }
1051 #endif
1052
1053 return ret;
1054 }
1055 close(fd);
1056 }
1057 if (persistent_id) {
1058 efree(persistent_id);
1059 }
1060 return NULL;
1061 }
1062 /* }}} */
1063
1064
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)1065 static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
1066 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
1067 {
1068 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {
1069 return NULL;
1070 }
1071
1072 return php_stream_fopen_rel(path, mode, opened_path, options);
1073 }
1074
php_plain_files_url_stater(php_stream_wrapper * wrapper,const char * url,int flags,php_stream_statbuf * ssb,php_stream_context * context)1075 static int php_plain_files_url_stater(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context)
1076 {
1077 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1078 url += sizeof("file://") - 1;
1079 }
1080
1081 if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1)) {
1082 return -1;
1083 }
1084
1085 #ifdef PHP_WIN32
1086 if (flags & PHP_STREAM_URL_STAT_LINK) {
1087 return VCWD_LSTAT(url, &ssb->sb);
1088 }
1089 #else
1090 # ifdef HAVE_SYMLINK
1091 if (flags & PHP_STREAM_URL_STAT_LINK) {
1092 return VCWD_LSTAT(url, &ssb->sb);
1093 } else
1094 # endif
1095 #endif
1096 return VCWD_STAT(url, &ssb->sb);
1097 }
1098
php_plain_files_unlink(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1099 static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1100 {
1101 int ret;
1102
1103 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1104 url += sizeof("file://") - 1;
1105 }
1106
1107 if (php_check_open_basedir(url)) {
1108 return 0;
1109 }
1110
1111 ret = VCWD_UNLINK(url);
1112 if (ret == -1) {
1113 if (options & REPORT_ERRORS) {
1114 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno));
1115 }
1116 return 0;
1117 }
1118
1119 /* Clear stat cache (and realpath cache) */
1120 php_clear_stat_cache(1, NULL, 0);
1121
1122 return 1;
1123 }
1124
php_plain_files_rename(php_stream_wrapper * wrapper,const char * url_from,const char * url_to,int options,php_stream_context * context)1125 static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context)
1126 {
1127 int ret;
1128
1129 if (!url_from || !url_to) {
1130 return 0;
1131 }
1132
1133 #ifdef PHP_WIN32
1134 if (!php_win32_check_trailing_space(url_from, (int)strlen(url_from))) {
1135 php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);
1136 return 0;
1137 }
1138 if (!php_win32_check_trailing_space(url_to, (int)strlen(url_to))) {
1139 php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);
1140 return 0;
1141 }
1142 #endif
1143
1144 if (strncasecmp(url_from, "file://", sizeof("file://") - 1) == 0) {
1145 url_from += sizeof("file://") - 1;
1146 }
1147
1148 if (strncasecmp(url_to, "file://", sizeof("file://") - 1) == 0) {
1149 url_to += sizeof("file://") - 1;
1150 }
1151
1152 if (php_check_open_basedir(url_from) || php_check_open_basedir(url_to)) {
1153 return 0;
1154 }
1155
1156 ret = VCWD_RENAME(url_from, url_to);
1157
1158 if (ret == -1) {
1159 #ifndef PHP_WIN32
1160 # ifdef EXDEV
1161 if (errno == EXDEV) {
1162 zend_stat_t sb;
1163 if (php_copy_file(url_from, url_to) == SUCCESS) {
1164 if (VCWD_STAT(url_from, &sb) == 0) {
1165 # if !defined(TSRM_WIN32) && !defined(NETWARE)
1166 if (VCWD_CHMOD(url_to, sb.st_mode)) {
1167 if (errno == EPERM) {
1168 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1169 VCWD_UNLINK(url_from);
1170 return 1;
1171 }
1172 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1173 return 0;
1174 }
1175 if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) {
1176 if (errno == EPERM) {
1177 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1178 VCWD_UNLINK(url_from);
1179 return 1;
1180 }
1181 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1182 return 0;
1183 }
1184 # endif
1185 VCWD_UNLINK(url_from);
1186 return 1;
1187 }
1188 }
1189 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1190 return 0;
1191 }
1192 # endif
1193 #endif
1194
1195 #ifdef PHP_WIN32
1196 php_win32_docref2_from_error(GetLastError(), url_from, url_to);
1197 #else
1198 php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno));
1199 #endif
1200 return 0;
1201 }
1202
1203 /* Clear stat cache (and realpath cache) */
1204 php_clear_stat_cache(1, NULL, 0);
1205
1206 return 1;
1207 }
1208
php_plain_files_mkdir(php_stream_wrapper * wrapper,const char * dir,int mode,int options,php_stream_context * context)1209 static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, int mode, int options, php_stream_context *context)
1210 {
1211 int ret, recursive = options & PHP_STREAM_MKDIR_RECURSIVE;
1212 char *p;
1213
1214 if (strncasecmp(dir, "file://", sizeof("file://") - 1) == 0) {
1215 dir += sizeof("file://") - 1;
1216 }
1217
1218 if (!recursive) {
1219 ret = php_mkdir(dir, mode);
1220 } else {
1221 /* we look for directory separator from the end of string, thus hopefuly reducing our work load */
1222 char *e;
1223 zend_stat_t sb;
1224 int dir_len = (int)strlen(dir);
1225 int offset = 0;
1226 char buf[MAXPATHLEN];
1227
1228 if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND )) {
1229 php_error_docref(NULL, E_WARNING, "Invalid path");
1230 return 0;
1231 }
1232
1233 e = buf + strlen(buf);
1234
1235 if ((p = memchr(buf, DEFAULT_SLASH, dir_len))) {
1236 offset = p - buf + 1;
1237 }
1238
1239 if (p && dir_len == 1) {
1240 /* buf == "DEFAULT_SLASH" */
1241 }
1242 else {
1243 /* find a top level directory we need to create */
1244 while ( (p = strrchr(buf + offset, DEFAULT_SLASH)) || (offset != 1 && (p = strrchr(buf, DEFAULT_SLASH))) ) {
1245 int n = 0;
1246
1247 *p = '\0';
1248 while (p > buf && *(p-1) == DEFAULT_SLASH) {
1249 ++n;
1250 --p;
1251 *p = '\0';
1252 }
1253 if (VCWD_STAT(buf, &sb) == 0) {
1254 while (1) {
1255 *p = DEFAULT_SLASH;
1256 if (!n) break;
1257 --n;
1258 ++p;
1259 }
1260 break;
1261 }
1262 }
1263 }
1264
1265 if (p == buf) {
1266 ret = php_mkdir(dir, mode);
1267 } else if (!(ret = php_mkdir(buf, mode))) {
1268 if (!p) {
1269 p = buf;
1270 }
1271 /* create any needed directories if the creation of the 1st directory worked */
1272 while (++p != e) {
1273 if (*p == '\0') {
1274 *p = DEFAULT_SLASH;
1275 if ((*(p+1) != '\0') &&
1276 (ret = VCWD_MKDIR(buf, (mode_t)mode)) < 0) {
1277 if (options & REPORT_ERRORS) {
1278 php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1279 }
1280 break;
1281 }
1282 }
1283 }
1284 }
1285 }
1286 if (ret < 0) {
1287 /* Failure */
1288 return 0;
1289 } else {
1290 /* Success */
1291 return 1;
1292 }
1293 }
1294
php_plain_files_rmdir(php_stream_wrapper * wrapper,const char * url,int options,php_stream_context * context)1295 static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1296 {
1297 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1298 url += sizeof("file://") - 1;
1299 }
1300
1301 if (php_check_open_basedir(url)) {
1302 return 0;
1303 }
1304
1305 #ifdef PHP_WIN32
1306 if (!php_win32_check_trailing_space(url, (int)strlen(url))) {
1307 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT));
1308 return 0;
1309 }
1310 #endif
1311
1312 if (VCWD_RMDIR(url) < 0) {
1313 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno));
1314 return 0;
1315 }
1316
1317 /* Clear stat cache (and realpath cache) */
1318 php_clear_stat_cache(1, NULL, 0);
1319
1320 return 1;
1321 }
1322
php_plain_files_metadata(php_stream_wrapper * wrapper,const char * url,int option,void * value,php_stream_context * context)1323 static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url, int option, void *value, php_stream_context *context)
1324 {
1325 struct utimbuf *newtime;
1326 #if !defined(WINDOWS) && !defined(NETWARE)
1327 uid_t uid;
1328 gid_t gid;
1329 #endif
1330 mode_t mode;
1331 int ret = 0;
1332 #ifdef PHP_WIN32
1333 int url_len = (int)strlen(url);
1334 #endif
1335
1336 #ifdef PHP_WIN32
1337 if (!php_win32_check_trailing_space(url, url_len)) {
1338 php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT));
1339 return 0;
1340 }
1341 #endif
1342
1343 if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1344 url += sizeof("file://") - 1;
1345 }
1346
1347 if (php_check_open_basedir(url)) {
1348 return 0;
1349 }
1350
1351 switch(option) {
1352 case PHP_STREAM_META_TOUCH:
1353 newtime = (struct utimbuf *)value;
1354 if (VCWD_ACCESS(url, F_OK) != 0) {
1355 FILE *file = VCWD_FOPEN(url, "w");
1356 if (file == NULL) {
1357 php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, strerror(errno));
1358 return 0;
1359 }
1360 fclose(file);
1361 }
1362
1363 ret = VCWD_UTIME(url, newtime);
1364 break;
1365 #if !defined(WINDOWS) && !defined(NETWARE)
1366 case PHP_STREAM_META_OWNER_NAME:
1367 case PHP_STREAM_META_OWNER:
1368 if(option == PHP_STREAM_META_OWNER_NAME) {
1369 if(php_get_uid_by_name((char *)value, &uid) != SUCCESS) {
1370 php_error_docref1(NULL, url, E_WARNING, "Unable to find uid for %s", (char *)value);
1371 return 0;
1372 }
1373 } else {
1374 uid = (uid_t)*(long *)value;
1375 }
1376 ret = VCWD_CHOWN(url, uid, -1);
1377 break;
1378 case PHP_STREAM_META_GROUP:
1379 case PHP_STREAM_META_GROUP_NAME:
1380 if(option == PHP_STREAM_META_GROUP_NAME) {
1381 if(php_get_gid_by_name((char *)value, &gid) != SUCCESS) {
1382 php_error_docref1(NULL, url, E_WARNING, "Unable to find gid for %s", (char *)value);
1383 return 0;
1384 }
1385 } else {
1386 gid = (gid_t)*(long *)value;
1387 }
1388 ret = VCWD_CHOWN(url, -1, gid);
1389 break;
1390 #endif
1391 case PHP_STREAM_META_ACCESS:
1392 mode = (mode_t)*(zend_long *)value;
1393 ret = VCWD_CHMOD(url, mode);
1394 break;
1395 default:
1396 php_error_docref1(NULL, url, E_WARNING, "Unknown option %d for stream_metadata", option);
1397 return 0;
1398 }
1399 if (ret == -1) {
1400 php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", strerror(errno));
1401 return 0;
1402 }
1403 php_clear_stat_cache(0, NULL, 0);
1404 return 1;
1405 }
1406
1407
1408 static php_stream_wrapper_ops php_plain_files_wrapper_ops = {
1409 php_plain_files_stream_opener,
1410 NULL,
1411 NULL,
1412 php_plain_files_url_stater,
1413 php_plain_files_dir_opener,
1414 "plainfile",
1415 php_plain_files_unlink,
1416 php_plain_files_rename,
1417 php_plain_files_mkdir,
1418 php_plain_files_rmdir,
1419 php_plain_files_metadata
1420 };
1421
1422 PHPAPI php_stream_wrapper php_plain_files_wrapper = {
1423 &php_plain_files_wrapper_ops,
1424 NULL,
1425 0
1426 };
1427
1428 /* {{{ 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)1429 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)
1430 {
1431 /* code ripped off from fopen_wrappers.c */
1432 char *pathbuf, *end;
1433 const char *ptr;
1434 char trypath[MAXPATHLEN];
1435 php_stream *stream;
1436 int filename_length;
1437 zend_string *exec_filename;
1438
1439 if (opened_path) {
1440 *opened_path = NULL;
1441 }
1442
1443 if(!filename) {
1444 return NULL;
1445 }
1446
1447 filename_length = (int)strlen(filename);
1448 #ifndef PHP_WIN32
1449 (void) filename_length;
1450 #endif
1451
1452 /* Relative path open */
1453 if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) {
1454 /* further checks, we could have ....... filenames */
1455 ptr = filename + 1;
1456 if (*ptr == '.') {
1457 while (*(++ptr) == '.');
1458 if (!IS_SLASH(*ptr)) { /* not a relative path after all */
1459 goto not_relative_path;
1460 }
1461 }
1462
1463
1464 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename)) {
1465 return NULL;
1466 }
1467
1468 return php_stream_fopen_rel(filename, mode, opened_path, options);
1469 }
1470
1471 not_relative_path:
1472
1473 /* Absolute path open */
1474 if (IS_ABSOLUTE_PATH(filename, filename_length)) {
1475
1476 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename)) {
1477 return NULL;
1478 }
1479
1480 return php_stream_fopen_rel(filename, mode, opened_path, options);
1481 }
1482
1483 #ifdef PHP_WIN32
1484 if (IS_SLASH(filename[0])) {
1485 size_t cwd_len;
1486 char *cwd;
1487 cwd = virtual_getcwd_ex(&cwd_len);
1488 /* getcwd() will return always return [DRIVE_LETTER]:/) on windows. */
1489 *(cwd+3) = '\0';
1490
1491 if (snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename) >= MAXPATHLEN) {
1492 php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN);
1493 }
1494
1495 efree(cwd);
1496
1497 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(trypath)) {
1498 return NULL;
1499 }
1500
1501 return php_stream_fopen_rel(trypath, mode, opened_path, options);
1502 }
1503 #endif
1504
1505 if (!path || !*path) {
1506 return php_stream_fopen_rel(filename, mode, opened_path, options);
1507 }
1508
1509 /* check in provided path */
1510 /* append the calling scripts' current working directory
1511 * as a fall back case
1512 */
1513 if (zend_is_executing() &&
1514 (exec_filename = zend_get_executed_filename_ex()) != NULL) {
1515 const char *exec_fname = ZSTR_VAL(exec_filename);
1516 size_t exec_fname_length = ZSTR_LEN(exec_filename);
1517
1518 while ((--exec_fname_length < SIZE_MAX) && !IS_SLASH(exec_fname[exec_fname_length]));
1519 if (exec_fname_length<=0) {
1520 /* no path */
1521 pathbuf = estrdup(path);
1522 } else {
1523 size_t path_length = strlen(path);
1524
1525 pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1);
1526 memcpy(pathbuf, path, path_length);
1527 pathbuf[path_length] = DEFAULT_DIR_SEPARATOR;
1528 memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length);
1529 pathbuf[path_length + exec_fname_length +1] = '\0';
1530 }
1531 } else {
1532 pathbuf = estrdup(path);
1533 }
1534
1535 ptr = pathbuf;
1536
1537 while (ptr && *ptr) {
1538 end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
1539 if (end != NULL) {
1540 *end = '\0';
1541 end++;
1542 }
1543 if (*ptr == '\0') {
1544 goto stream_skip;
1545 }
1546 if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) {
1547 php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN);
1548 }
1549
1550 if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0)) {
1551 goto stream_skip;
1552 }
1553
1554 stream = php_stream_fopen_rel(trypath, mode, opened_path, options);
1555 if (stream) {
1556 efree(pathbuf);
1557 return stream;
1558 }
1559 stream_skip:
1560 ptr = end;
1561 } /* end provided path */
1562
1563 efree(pathbuf);
1564 return NULL;
1565
1566 }
1567 /* }}} */
1568
1569 /*
1570 * Local variables:
1571 * tab-width: 4
1572 * c-basic-offset: 4
1573 * End:
1574 * vim600: noet sw=4 ts=4 fdm=marker
1575 * vim<600: noet sw=4 ts=4
1576 */
1577