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