root/main/streams/plain_wrapper.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_stream_parse_fopen_modes
  2. do_fstat
  3. _php_stream_fopen_from_fd_int
  4. _php_stream_fopen_from_file_int
  5. _php_stream_fopen_temporary_file
  6. _php_stream_fopen_tmpfile
  7. _php_stream_fopen_from_fd
  8. _php_stream_fopen_from_file
  9. _php_stream_fopen_from_pipe
  10. php_stdiop_write
  11. php_stdiop_read
  12. php_stdiop_close
  13. php_stdiop_flush
  14. php_stdiop_seek
  15. php_stdiop_cast
  16. php_stdiop_stat
  17. php_stdiop_set_option
  18. php_plain_files_dirstream_read
  19. php_plain_files_dirstream_close
  20. php_plain_files_dirstream_rewind
  21. php_plain_files_dir_opener
  22. _php_stream_fopen
  23. php_plain_files_stream_opener
  24. php_plain_files_url_stater
  25. php_plain_files_unlink
  26. php_plain_files_rename
  27. php_plain_files_mkdir
  28. php_plain_files_rmdir
  29. php_plain_files_metadata
  30. _php_stream_fopen_with_path

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

/* [<][>][^][v][top][bottom][index][help] */