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