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