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