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