xref: /PHP-5.6/main/streams/plain_wrapper.c (revision abe00908)
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