Coverage Report

Created: 2026-06-02 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/main/streams/plain_wrapper.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Wez Furlong <wez@thebrainroom.com>                          |
12
   +----------------------------------------------------------------------+
13
 */
14
15
#include "php.h"
16
#include "php_globals.h"
17
#include "php_network.h"
18
#include "php_open_temporary_file.h"
19
#include "ext/standard/file.h"
20
#include "ext/standard/flock_compat.h"
21
#include "ext/standard/php_filestat.h"
22
#include <stddef.h>
23
#include <fcntl.h>
24
#ifdef HAVE_SYS_WAIT_H
25
#include <sys/wait.h>
26
#endif
27
#ifdef HAVE_SYS_FILE_H
28
#include <sys/file.h>
29
#endif
30
#ifdef HAVE_SYS_MMAN_H
31
#include <sys/mman.h>
32
#endif
33
#include "SAPI.h"
34
35
#include "php_streams_int.h"
36
#ifdef PHP_WIN32
37
# include "win32/winutil.h"
38
# include "win32/time.h"
39
# include "win32/ioutil.h"
40
# include "win32/readdir.h"
41
# include <limits.h>
42
#endif
43
44
#ifdef __linux__
45
# include <sys/sysmacros.h>
46
#endif
47
48
#define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC)
49
0
#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)
50
#define php_stream_fopen_from_file_int(file, mode)  _php_stream_fopen_from_file_int((file), (mode) STREAMS_CC)
51
0
#define php_stream_fopen_from_file_int_rel(file, mode)   _php_stream_fopen_from_file_int((file), (mode) STREAMS_REL_CC)
52
53
#ifndef PHP_WIN32
54
extern int php_get_uid_by_name(const char *name, uid_t *uid);
55
extern int php_get_gid_by_name(const char *name, gid_t *gid);
56
#endif
57
58
#if defined(PHP_WIN32)
59
# define PLAIN_WRAP_BUF_SIZE(st) ((unsigned int)(st > INT_MAX ? INT_MAX : st))
60
#define fsync _commit
61
#define fdatasync fsync
62
#else
63
0
# define PLAIN_WRAP_BUF_SIZE(st) (st)
64
# if !defined(HAVE_FDATASYNC)
65
#  define fdatasync fsync
66
# elif defined(__APPLE__)
67
  // The symbol is present, however not in the headers
68
  extern int fdatasync(int);
69
# endif
70
#endif
71
72
/* parse standard "fopen" modes into open() flags */
73
PHPAPI int php_stream_parse_fopen_modes(const char *mode, int *open_flags)
74
0
{
75
0
  int flags;
76
77
0
  switch (mode[0]) {
78
0
    case 'r':
79
0
      flags = 0;
80
0
      break;
81
0
    case 'w':
82
0
      flags = O_TRUNC|O_CREAT;
83
0
      break;
84
0
    case 'a':
85
0
      flags = O_CREAT|O_APPEND;
86
0
      break;
87
0
    case 'x':
88
0
      flags = O_CREAT|O_EXCL;
89
0
      break;
90
0
    case 'c':
91
0
      flags = O_CREAT;
92
0
      break;
93
0
    default:
94
      /* unknown mode */
95
0
      return FAILURE;
96
0
  }
97
98
0
  if (strchr(mode, '+')) {
99
0
    flags |= O_RDWR;
100
0
  } else if (flags) {
101
0
    flags |= O_WRONLY;
102
0
  } else {
103
0
    flags |= O_RDONLY;
104
0
  }
105
106
0
#if defined(O_CLOEXEC)
107
0
  if (strchr(mode, 'e')) {
108
0
    flags |= O_CLOEXEC;
109
0
  }
110
0
#endif
111
112
0
#if defined(O_NONBLOCK)
113
0
  if (strchr(mode, 'n')) {
114
0
    flags |= O_NONBLOCK;
115
0
  }
116
0
#endif
117
118
#if defined(_O_TEXT) && defined(O_BINARY)
119
  if (strchr(mode, 't')) {
120
    flags |= _O_TEXT;
121
  } else {
122
    flags |= O_BINARY;
123
  }
124
#endif
125
126
0
  *open_flags = flags;
127
0
  return SUCCESS;
128
0
}
129
130
131
/* {{{ ------- STDIO stream implementation -------*/
132
133
typedef struct {
134
  FILE *file;
135
  int fd;         /* underlying file descriptor */
136
  unsigned is_process_pipe:1; /* use pclose instead of fclose */
137
  unsigned is_pipe:1;   /* stream is an actual pipe, currently Windows only*/
138
  unsigned cached_fstat:1;  /* sb is valid */
139
  unsigned is_pipe_blocking:1; /* allow blocking read() on pipes, currently Windows only */
140
  unsigned no_forced_fstat:1;  /* Use fstat cache even if forced */
141
  unsigned is_seekable:1;   /* don't try and seek, if not set */
142
  unsigned _reserved:26;
143
144
  int lock_flag;      /* stores the lock state */
145
  zend_string *temp_name; /* if non-null, this is the path to a temporary file that
146
               * is to be deleted when the stream is closed */
147
#ifdef HAVE_FLUSHIO
148
  char last_op;
149
#endif
150
151
#ifdef HAVE_MMAP
152
  char *last_mapped_addr;
153
  size_t last_mapped_len;
154
#endif
155
#ifdef PHP_WIN32
156
  char *last_mapped_addr;
157
  HANDLE file_mapping;
158
#endif
159
160
  zend_stat_t sb;
161
} php_stdio_stream_data;
162
0
#define PHP_STDIOP_GET_FD(anfd, data) anfd = (data)->file ? fileno((data)->file) : (data)->fd
163
164
static int do_fstat(php_stdio_stream_data *d, int force)
165
0
{
166
0
  if (!d->cached_fstat || (force && !d->no_forced_fstat)) {
167
0
    int fd;
168
0
    int r;
169
170
0
    PHP_STDIOP_GET_FD(fd, d);
171
0
    r = zend_fstat(fd, &d->sb);
172
0
    d->cached_fstat = r == 0;
173
174
0
    return r;
175
0
  }
176
0
  return 0;
177
0
}
178
179
static php_stream *_php_stream_fopen_from_fd_int(int fd, const char *mode, const char *persistent_id STREAMS_DC)
180
0
{
181
0
  php_stdio_stream_data *self;
182
183
0
  self = pemalloc_rel_orig(sizeof(*self), persistent_id);
184
0
  memset(self, 0, sizeof(*self));
185
0
  self->file = NULL;
186
0
  self->is_seekable = 1;
187
0
  self->is_pipe = 0;
188
0
  self->lock_flag = LOCK_UN;
189
0
  self->is_process_pipe = 0;
190
0
  self->temp_name = NULL;
191
0
  self->fd = fd;
192
#ifdef PHP_WIN32
193
  self->is_pipe_blocking = 0;
194
#endif
195
196
0
  return php_stream_alloc_rel(&php_stream_stdio_ops, self, persistent_id, mode);
197
0
}
198
199
static php_stream *_php_stream_fopen_from_file_int(FILE *file, const char *mode STREAMS_DC)
200
0
{
201
0
  php_stdio_stream_data *self;
202
203
0
  self = emalloc_rel_orig(sizeof(*self));
204
0
  memset(self, 0, sizeof(*self));
205
0
  self->file = file;
206
0
  self->is_seekable = 1;
207
0
  self->is_pipe = 0;
208
0
  self->lock_flag = LOCK_UN;
209
0
  self->is_process_pipe = 0;
210
0
  self->temp_name = NULL;
211
0
  self->fd = fileno(file);
212
#ifdef PHP_WIN32
213
  self->is_pipe_blocking = 0;
214
#endif
215
216
0
  return php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode);
217
0
}
218
219
PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, zend_string **opened_path_ptr STREAMS_DC)
220
0
{
221
0
  zend_string *opened_path = NULL;
222
0
  int fd;
223
224
0
  fd = php_open_temporary_fd(dir, pfx, &opened_path);
225
0
  if (fd != -1) {
226
0
    php_stream *stream;
227
228
0
    if (opened_path_ptr) {
229
0
      *opened_path_ptr = opened_path;
230
0
    }
231
232
0
    stream = php_stream_fopen_from_fd_int_rel(fd, "r+b", NULL);
233
0
    if (stream) {
234
0
      php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
235
0
      stream->wrapper = (php_stream_wrapper*)&php_plain_files_wrapper;
236
0
      stream->orig_path = estrndup(ZSTR_VAL(opened_path), ZSTR_LEN(opened_path));
237
238
0
      self->temp_name = opened_path;
239
0
      self->lock_flag = LOCK_UN;
240
241
0
      return stream;
242
0
    }
243
0
    close(fd);
244
245
0
    php_error_docref(NULL, E_WARNING, "Unable to allocate stream");
246
247
0
    return NULL;
248
0
  }
249
0
  return NULL;
250
0
}
251
252
PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC)
253
0
{
254
0
  return php_stream_fopen_temporary_file(NULL, "php", NULL);
255
0
}
256
257
0
static void detect_is_seekable(php_stdio_stream_data *self) {
258
0
#if defined(S_ISFIFO) && defined(S_ISCHR)
259
0
  if (self->fd >= 0 && do_fstat(self, 0) == 0) {
260
0
#ifdef __linux__
261
0
    if (S_ISCHR(self->sb.st_mode)) {
262
      /* Some character devices are exceptions, check their major/minor ID
263
       * https://www.kernel.org/doc/Documentation/admin-guide/devices.txt */
264
0
      if (major(self->sb.st_rdev) == 1) {
265
0
        unsigned m = minor(self->sb.st_rdev);
266
0
        self->is_seekable =
267
0
          m == 1 ||   /* /dev/mem   */
268
0
          m == 2 ||   /* /dev/kmem  */
269
0
          m == 3 ||   /* /dev/null  */
270
0
          m == 4 ||   /* /dev/port  (seekable, offset = I/O port) */
271
0
          m == 5 ||   /* /dev/zero  */
272
0
          m == 7;     /* /dev/full  */
273
0
      } else {
274
0
        self->is_seekable = false;
275
0
      }
276
0
    } else {
277
0
      self->is_seekable = !S_ISFIFO(self->sb.st_mode);
278
0
    }
279
#else
280
    self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode));
281
#endif
282
0
    self->is_pipe = S_ISFIFO(self->sb.st_mode);
283
0
  }
284
#elif defined(PHP_WIN32)
285
  uintptr_t handle = _get_osfhandle(self->fd);
286
287
  if (handle != (uintptr_t)INVALID_HANDLE_VALUE) {
288
    DWORD file_type = GetFileType((HANDLE)handle);
289
290
    self->is_seekable = !(file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR);
291
    self->is_pipe = file_type == FILE_TYPE_PIPE;
292
293
    /* Additional check needed to distinguish between pipes and sockets. */
294
    if (self->is_pipe && !GetNamedPipeInfo((HANDLE) handle, NULL, NULL, NULL, NULL)) {
295
      self->is_pipe = 0;
296
    }
297
  }
298
#endif
299
0
}
300
301
PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id, bool zero_position STREAMS_DC)
302
0
{
303
0
  php_stream *stream = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);
304
305
0
  if (stream) {
306
0
    php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
307
308
0
    detect_is_seekable(self);
309
0
    if (!self->is_seekable) {
310
0
      stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
311
0
      stream->position = -1;
312
0
    } else if (zero_position) {
313
0
      ZEND_ASSERT(zend_lseek(self->fd, 0, SEEK_CUR) == 0);
314
0
      stream->position = 0;
315
0
    } else {
316
0
      stream->position = zend_lseek(self->fd, 0, SEEK_CUR);
317
0
#ifdef ESPIPE
318
      /* FIXME: Is this code still needed? */
319
0
      if (stream->position == (zend_off_t)-1 && errno == ESPIPE) {
320
0
        stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
321
0
        self->is_seekable = 0;
322
0
      }
323
0
#endif
324
0
    }
325
0
  }
326
327
0
  return stream;
328
0
}
329
330
PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC)
331
0
{
332
0
  php_stream *stream = php_stream_fopen_from_file_int_rel(file, mode);
333
334
0
  if (stream) {
335
0
    php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
336
337
0
    detect_is_seekable(self);
338
0
    if (!self->is_seekable) {
339
0
      stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
340
0
      stream->position = -1;
341
0
    } else {
342
0
      stream->position = zend_ftell(file);
343
0
    }
344
0
  }
345
346
0
  return stream;
347
0
}
348
349
PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STREAMS_DC)
350
0
{
351
0
  php_stdio_stream_data *self;
352
0
  php_stream *stream;
353
354
0
  self = emalloc_rel_orig(sizeof(*self));
355
0
  memset(self, 0, sizeof(*self));
356
0
  self->file = file;
357
0
  self->is_seekable = 0;
358
0
  self->is_pipe = 1;
359
0
  self->lock_flag = LOCK_UN;
360
0
  self->is_process_pipe = 1;
361
0
  self->fd = fileno(file);
362
0
  self->temp_name = NULL;
363
#ifdef PHP_WIN32
364
  self->is_pipe_blocking = 0;
365
#endif
366
367
0
  stream = php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode);
368
0
  stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
369
0
  return stream;
370
0
}
371
372
static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t count)
373
0
{
374
0
  php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
375
0
  ssize_t bytes_written;
376
377
0
  assert(data != NULL);
378
379
0
  if (data->fd >= 0) {
380
#ifdef PHP_WIN32
381
    bytes_written = _write(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count));
382
#else
383
0
    bytes_written = write(data->fd, buf, count);
384
0
#endif
385
0
    if (bytes_written < 0) {
386
0
      if (PHP_IS_TRANSIENT_ERROR(errno)) {
387
0
        return 0;
388
0
      }
389
0
      if (errno == EINTR) {
390
        /* TODO: Should this be treated as a proper error or not? */
391
0
        return bytes_written;
392
0
      }
393
0
      if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
394
0
        char errstr[256];
395
0
        php_stream_notice(stream, WriteFailed,
396
0
            "Write of %zu bytes failed with errno=%d %s",
397
0
            count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr)));
398
0
      }
399
0
    }
400
0
  } else {
401
402
#ifdef HAVE_FLUSHIO
403
    if (data->is_seekable && data->last_op == 'r') {
404
      zend_fseek(data->file, 0, SEEK_CUR);
405
    }
406
    data->last_op = 'w';
407
#endif
408
409
0
    bytes_written = (ssize_t) fwrite(buf, 1, count, data->file);
410
0
  }
411
412
0
  if (EG(active)) {
413
    /* clear stat cache as mtime and ctime got changed */
414
0
    php_clear_stat_cache(0, NULL, 0);
415
0
  }
416
417
0
  return bytes_written;
418
0
}
419
420
static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
421
0
{
422
0
  php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
423
0
  ssize_t ret;
424
425
0
  assert(data != NULL);
426
427
0
  if (data->fd >= 0) {
428
#ifdef PHP_WIN32
429
    php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
430
431
    if ((self->is_pipe || self->is_process_pipe) && !self->is_pipe_blocking) {
432
      HANDLE ph = (HANDLE)_get_osfhandle(data->fd);
433
      int retry = 0;
434
      DWORD avail_read = 0;
435
436
      do {
437
        /* Look ahead to get the available data amount to read. Do the same
438
          as read() does, however not blocking forever. In case it failed,
439
          no data will be read (better than block). */
440
        if (!PeekNamedPipe(ph, NULL, 0, NULL, &avail_read, NULL)) {
441
          break;
442
        }
443
        /* If there's nothing to read, wait in 10us periods. */
444
        if (0 == avail_read) {
445
          usleep(10);
446
        }
447
      } while (0 == avail_read && retry++ < 3200000);
448
449
      /* Reduce the required data amount to what is available, otherwise read()
450
        will block.*/
451
      if (avail_read < count) {
452
        count = avail_read;
453
      }
454
    }
455
#endif
456
0
    ret = read(data->fd, buf,  PLAIN_WRAP_BUF_SIZE(count));
457
458
0
    if (ret == (size_t)-1 && errno == EINTR) {
459
      /* Read was interrupted, retry once,
460
         If read still fails, give up with feof==0
461
         so script can retry if desired */
462
0
      ret = read(data->fd, buf,  PLAIN_WRAP_BUF_SIZE(count));
463
0
    }
464
465
0
    if (ret < 0) {
466
0
      if (PHP_IS_TRANSIENT_ERROR(errno)) {
467
        /* Not an error. */
468
0
        ret = 0;
469
0
      } else if (errno == EINTR) {
470
        /* TODO: Should this be treated as a proper error or not? */
471
0
      } else {
472
0
        if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
473
0
          char errstr[256];
474
0
          php_stream_notice(stream, ReadFailed,
475
0
              "Read of %zu bytes failed with errno=%d %s",
476
0
              count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr)));
477
0
        }
478
479
        /* TODO: Remove this special-case? */
480
0
        if (errno != EBADF) {
481
0
          stream->eof = 1;
482
0
        }
483
0
      }
484
0
    } else if (ret == 0) {
485
0
      stream->eof = 1;
486
0
    }
487
488
0
  } else {
489
#ifdef HAVE_FLUSHIO
490
    if (data->is_seekable && data->last_op == 'w')
491
      zend_fseek(data->file, 0, SEEK_CUR);
492
    data->last_op = 'r';
493
#endif
494
495
0
    ret = fread(buf, 1, count, data->file);
496
497
0
    stream->eof = feof(data->file);
498
0
  }
499
500
0
  if (EG(active)) {
501
    /* clear stat cache as atime got changed */
502
0
    php_clear_stat_cache(0, NULL, 0);
503
0
  }
504
505
0
  return ret;
506
0
}
507
508
static int php_stdiop_close(php_stream *stream, int close_handle)
509
0
{
510
0
  int ret;
511
0
  php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
512
513
0
  assert(data != NULL);
514
515
0
#ifdef HAVE_MMAP
516
0
  if (data->last_mapped_addr) {
517
0
    munmap(data->last_mapped_addr, data->last_mapped_len);
518
0
    data->last_mapped_addr = NULL;
519
0
  }
520
#elif defined(PHP_WIN32)
521
  if (data->last_mapped_addr) {
522
    UnmapViewOfFile(data->last_mapped_addr);
523
    data->last_mapped_addr = NULL;
524
  }
525
  if (data->file_mapping) {
526
    CloseHandle(data->file_mapping);
527
    data->file_mapping = NULL;
528
  }
529
#endif
530
531
0
  if (close_handle) {
532
0
    if (data->file) {
533
0
      if (data->is_process_pipe) {
534
0
        errno = 0;
535
0
        ret = pclose(data->file);
536
537
0
#ifdef HAVE_SYS_WAIT_H
538
0
        if (WIFEXITED(ret)) {
539
0
          ret = WEXITSTATUS(ret);
540
0
        }
541
0
#endif
542
0
      } else {
543
0
        ret = fclose(data->file);
544
0
        data->file = NULL;
545
0
      }
546
0
    } else if (data->fd != -1) {
547
0
      ret = close(data->fd);
548
0
      data->fd = -1;
549
0
    } else {
550
0
      return 0; /* everything should be closed already -> success */
551
0
    }
552
0
    if (data->temp_name) {
553
#ifdef PHP_WIN32
554
      php_win32_ioutil_unlink(ZSTR_VAL(data->temp_name));
555
#else
556
0
      unlink(ZSTR_VAL(data->temp_name));
557
0
#endif
558
      /* temporary streams are never persistent */
559
0
      zend_string_release_ex(data->temp_name, 0);
560
0
      data->temp_name = NULL;
561
0
    }
562
0
  } else {
563
0
    ret = 0;
564
0
    data->file = NULL;
565
0
    data->fd = -1;
566
0
  }
567
568
0
  pefree(data, stream->is_persistent);
569
570
0
  return ret;
571
0
}
572
573
static int php_stdiop_flush(php_stream *stream)
574
0
{
575
0
  php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
576
577
0
  assert(data != NULL);
578
579
  /*
580
   * stdio buffers data in user land. By calling fflush(3), this
581
   * data is sent to the kernel using write(2). fsync'ing is
582
   * something completely different.
583
   */
584
0
  if (data->file) {
585
0
    if (EG(active)) {
586
      /* clear stat cache as there might be a write so mtime and ctime might have changed */
587
0
      php_clear_stat_cache(0, NULL, 0);
588
0
    }
589
0
    return fflush(data->file);
590
0
  }
591
0
  return 0;
592
0
}
593
594
595
static int php_stdiop_sync(php_stream *stream, bool dataonly)
596
0
{
597
0
  php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
598
0
  FILE *fp;
599
0
  int fd;
600
601
0
  if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS) == FAILURE) {
602
0
    return -1;
603
0
  }
604
605
0
  if (php_stdiop_flush(stream) == 0) {
606
0
    PHP_STDIOP_GET_FD(fd, data);
607
0
    if (dataonly) {
608
0
      return fdatasync(fd);
609
0
    } else {
610
0
      return fsync(fd);
611
0
    }
612
0
  }
613
0
  return -1;
614
0
}
615
616
static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
617
0
{
618
0
  php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
619
0
  int ret;
620
621
0
  assert(data != NULL);
622
623
0
  if (!data->is_seekable) {
624
0
    php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS,
625
0
        SeekNotSupported, "Cannot seek on this stream");
626
0
    return -1;
627
0
  }
628
629
0
  if (data->fd >= 0) {
630
0
    zend_off_t result;
631
632
0
    result = zend_lseek(data->fd, offset, whence);
633
0
    if (result == (zend_off_t)-1)
634
0
      return -1;
635
636
0
    *newoffset = result;
637
0
    return 0;
638
639
0
  } else {
640
0
    ret = zend_fseek(data->file, offset, whence);
641
0
    *newoffset = zend_ftell(data->file);
642
0
    return ret;
643
0
  }
644
0
}
645
646
static int php_stdiop_cast(php_stream *stream, int castas, void **ret)
647
0
{
648
0
  php_socket_t fd;
649
0
  php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
650
651
0
  assert(data != NULL);
652
653
  /* as soon as someone touches the stdio layer, buffering may ensue,
654
   * so we need to stop using the fd directly in that case */
655
656
0
  switch (castas) {
657
0
    case PHP_STREAM_AS_STDIO:
658
0
      if (ret) {
659
660
0
        if (data->file == NULL) {
661
          /* we were opened as a plain file descriptor, so we
662
           * need fdopen now */
663
0
          char fixed_mode[5];
664
0
          php_stream_mode_sanitize_fdopen_fopencookie(stream, fixed_mode);
665
0
          data->file = fdopen(data->fd, fixed_mode);
666
0
          if (data->file == NULL) {
667
0
            return FAILURE;
668
0
          }
669
0
        }
670
671
0
        *(FILE**)ret = data->file;
672
0
        data->fd = SOCK_ERR;
673
0
      }
674
0
      return SUCCESS;
675
676
0
    case PHP_STREAM_AS_FD_FOR_SELECT:
677
0
      PHP_STDIOP_GET_FD(fd, data);
678
0
      if (SOCK_ERR == fd) {
679
0
        return FAILURE;
680
0
      }
681
0
      if (ret) {
682
0
        *(php_socket_t *)ret = fd;
683
0
      }
684
0
      return SUCCESS;
685
686
0
    case PHP_STREAM_AS_FD:
687
0
      PHP_STDIOP_GET_FD(fd, data);
688
689
0
      if (SOCK_ERR == fd) {
690
0
        return FAILURE;
691
0
      }
692
0
      if (data->file) {
693
0
        fflush(data->file);
694
0
      }
695
0
      if (ret) {
696
0
        *(php_socket_t *)ret = fd;
697
0
      }
698
0
      return SUCCESS;
699
0
    default:
700
0
      return FAILURE;
701
0
  }
702
0
}
703
704
static int php_stdiop_stat(php_stream *stream, php_stream_statbuf *ssb)
705
0
{
706
0
  int ret;
707
0
  php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
708
709
0
  assert(data != NULL);
710
0
  if((ret = do_fstat(data, 1)) == 0) {
711
0
    memcpy(&ssb->sb, &data->sb, sizeof(ssb->sb));
712
0
  }
713
714
0
  return ret;
715
0
}
716
717
static int php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam)
718
0
{
719
0
  php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
720
0
  size_t size;
721
0
  int fd;
722
0
#ifdef O_NONBLOCK
723
  /* FIXME: make this work for win32 */
724
0
  int flags;
725
0
  int oldval;
726
0
#endif
727
728
0
  PHP_STDIOP_GET_FD(fd, data);
729
730
0
  switch(option) {
731
0
    case PHP_STREAM_OPTION_BLOCKING:
732
0
      if (fd == -1)
733
0
        return -1;
734
0
#ifdef O_NONBLOCK
735
0
      flags = fcntl(fd, F_GETFL, 0);
736
0
      oldval = (flags & O_NONBLOCK) ? 0 : 1;
737
0
      if (value)
738
0
        flags &= ~O_NONBLOCK;
739
0
      else
740
0
        flags |= O_NONBLOCK;
741
742
0
      if (-1 == fcntl(fd, F_SETFL, flags))
743
0
        return -1;
744
0
      return oldval;
745
#else
746
      return -1; /* not yet implemented */
747
#endif
748
749
0
    case PHP_STREAM_OPTION_WRITE_BUFFER:
750
751
0
      if (data->file == NULL) {
752
0
        return -1;
753
0
      }
754
755
0
      if (ptrparam)
756
0
        size = *(size_t *)ptrparam;
757
0
      else
758
0
        size = BUFSIZ;
759
760
0
      switch(value) {
761
0
        case PHP_STREAM_BUFFER_NONE:
762
0
          return setvbuf(data->file, NULL, _IONBF, 0);
763
764
0
        case PHP_STREAM_BUFFER_LINE:
765
0
          return setvbuf(data->file, NULL, _IOLBF, size);
766
767
0
        case PHP_STREAM_BUFFER_FULL:
768
0
          return setvbuf(data->file, NULL, _IOFBF, size);
769
770
0
        default:
771
0
          return -1;
772
0
      }
773
0
      break;
774
775
0
    case PHP_STREAM_OPTION_LOCKING:
776
0
      if (fd == -1) {
777
0
        return -1;
778
0
      }
779
780
0
      if ((uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) {
781
0
        return 0;
782
0
      }
783
784
0
      if (!flock(fd, value)) {
785
0
        data->lock_flag = value;
786
0
        return 0;
787
0
      } else {
788
0
        return -1;
789
0
      }
790
0
      break;
791
792
0
    case PHP_STREAM_OPTION_MMAP_API:
793
0
#ifdef HAVE_MMAP
794
0
      {
795
0
        php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
796
0
        int prot, flags;
797
798
0
        switch (value) {
799
0
          case PHP_STREAM_MMAP_SUPPORTED:
800
0
            return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
801
802
0
          case PHP_STREAM_MMAP_MAP_RANGE:
803
0
            if (do_fstat(data, 1) != 0) {
804
0
              return PHP_STREAM_OPTION_RETURN_ERR;
805
0
            }
806
0
            if (range->offset > data->sb.st_size) {
807
0
              range->offset = data->sb.st_size;
808
0
            }
809
0
            if (range->length == 0 ||
810
0
                range->length > data->sb.st_size - range->offset) {
811
0
              range->length = data->sb.st_size - range->offset;
812
0
            }
813
0
            switch (range->mode) {
814
0
              case PHP_STREAM_MAP_MODE_READONLY:
815
0
                prot = PROT_READ;
816
0
                flags = MAP_PRIVATE;
817
0
                break;
818
0
              case PHP_STREAM_MAP_MODE_READWRITE:
819
0
                prot = PROT_READ | PROT_WRITE;
820
0
                flags = MAP_PRIVATE;
821
0
                break;
822
0
              case PHP_STREAM_MAP_MODE_SHARED_READONLY:
823
0
                prot = PROT_READ;
824
0
                flags = MAP_SHARED;
825
0
                break;
826
0
              case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
827
0
                prot = PROT_READ | PROT_WRITE;
828
0
                flags = MAP_SHARED;
829
0
                break;
830
0
              default:
831
0
                return PHP_STREAM_OPTION_RETURN_ERR;
832
0
            }
833
0
            range->mapped = (char*)mmap(NULL, range->length, prot, flags, fd, range->offset);
834
0
            if (range->mapped == (char*)MAP_FAILED) {
835
0
              range->mapped = NULL;
836
0
              return PHP_STREAM_OPTION_RETURN_ERR;
837
0
            }
838
            /* remember the mapping */
839
0
            data->last_mapped_addr = range->mapped;
840
0
            data->last_mapped_len = range->length;
841
0
            return PHP_STREAM_OPTION_RETURN_OK;
842
843
0
          case PHP_STREAM_MMAP_UNMAP:
844
0
            if (data->last_mapped_addr) {
845
0
              munmap(data->last_mapped_addr, data->last_mapped_len);
846
0
              data->last_mapped_addr = NULL;
847
848
0
              return PHP_STREAM_OPTION_RETURN_OK;
849
0
            }
850
0
            return PHP_STREAM_OPTION_RETURN_ERR;
851
0
        }
852
0
      }
853
#elif defined(PHP_WIN32)
854
      {
855
        php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
856
        HANDLE hfile = (HANDLE)_get_osfhandle(fd);
857
        DWORD prot, acc, loffs = 0, hoffs = 0, delta = 0;
858
        LARGE_INTEGER file_size;
859
860
        switch (value) {
861
          case PHP_STREAM_MMAP_SUPPORTED:
862
            return hfile == INVALID_HANDLE_VALUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
863
864
          case PHP_STREAM_MMAP_MAP_RANGE:
865
            switch (range->mode) {
866
              case PHP_STREAM_MAP_MODE_READONLY:
867
                prot = PAGE_READONLY;
868
                acc = FILE_MAP_READ;
869
                break;
870
              case PHP_STREAM_MAP_MODE_READWRITE:
871
                prot = PAGE_READWRITE;
872
                acc = FILE_MAP_READ | FILE_MAP_WRITE;
873
                break;
874
              case PHP_STREAM_MAP_MODE_SHARED_READONLY:
875
                prot = PAGE_READONLY;
876
                acc = FILE_MAP_READ;
877
                /* TODO: we should assign a name for the mapping */
878
                break;
879
              case PHP_STREAM_MAP_MODE_SHARED_READWRITE:
880
                prot = PAGE_READWRITE;
881
                acc = FILE_MAP_READ | FILE_MAP_WRITE;
882
                /* TODO: we should assign a name for the mapping */
883
                break;
884
              default:
885
                return PHP_STREAM_OPTION_RETURN_ERR;
886
            }
887
888
            /* create a mapping capable of viewing the whole file (this costs no real resources) */
889
            data->file_mapping = CreateFileMapping(hfile, NULL, prot, 0, 0, NULL);
890
891
            if (data->file_mapping == NULL) {
892
              return PHP_STREAM_OPTION_RETURN_ERR;
893
            }
894
895
            if (!GetFileSizeEx(hfile, &file_size)) {
896
              CloseHandle(data->file_mapping);
897
              data->file_mapping = NULL;
898
              return PHP_STREAM_OPTION_RETURN_ERR;
899
            }
900
# if defined(_WIN64)
901
            size = file_size.QuadPart;
902
# else
903
            if (file_size.HighPart) {
904
              CloseHandle(data->file_mapping);
905
              data->file_mapping = NULL;
906
              return PHP_STREAM_OPTION_RETURN_ERR;
907
            } else {
908
              size = file_size.LowPart;
909
            }
910
# endif
911
            if (range->offset > size) {
912
              range->offset = size;
913
            }
914
            if (range->length == 0 || range->length > size - range->offset) {
915
              range->length = size - range->offset;
916
            }
917
918
            /* figure out how big a chunk to map to be able to view the part that we need */
919
            if (range->offset != 0) {
920
              SYSTEM_INFO info;
921
              DWORD gran;
922
923
              GetSystemInfo(&info);
924
              gran = info.dwAllocationGranularity;
925
              ZEND_ASSERT(gran != 0 && (gran & (gran - 1)) == 0);
926
              size_t rounded_offset = (range->offset / gran) * gran;
927
              delta = range->offset - rounded_offset;
928
              loffs = (DWORD)rounded_offset;
929
#ifdef _WIN64
930
              hoffs = (DWORD)(rounded_offset >> 32);
931
#else
932
              hoffs = 0;
933
#endif
934
            }
935
936
            /* MapViewOfFile()ing zero bytes would map to the end of the file; match *nix behavior instead */
937
            if (range->length + delta == 0) {
938
              return PHP_STREAM_OPTION_RETURN_ERR;
939
            }
940
941
            data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, hoffs, loffs, range->length + delta);
942
943
            if (data->last_mapped_addr) {
944
              /* give them back the address of the start offset they requested */
945
              range->mapped = data->last_mapped_addr + delta;
946
              return PHP_STREAM_OPTION_RETURN_OK;
947
            }
948
949
            CloseHandle(data->file_mapping);
950
            data->file_mapping = NULL;
951
952
            return PHP_STREAM_OPTION_RETURN_ERR;
953
954
          case PHP_STREAM_MMAP_UNMAP:
955
            if (data->last_mapped_addr) {
956
              UnmapViewOfFile(data->last_mapped_addr);
957
              data->last_mapped_addr = NULL;
958
              CloseHandle(data->file_mapping);
959
              data->file_mapping = NULL;
960
              return PHP_STREAM_OPTION_RETURN_OK;
961
            }
962
            return PHP_STREAM_OPTION_RETURN_ERR;
963
964
          default:
965
            return PHP_STREAM_OPTION_RETURN_ERR;
966
        }
967
      }
968
969
#endif
970
0
      return PHP_STREAM_OPTION_RETURN_NOTIMPL;
971
972
0
    case PHP_STREAM_OPTION_SYNC_API:
973
0
      switch (value) {
974
0
        case PHP_STREAM_SYNC_SUPPORTED:
975
0
          return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
976
0
        case PHP_STREAM_SYNC_FSYNC:
977
0
          return php_stdiop_sync(stream, 0) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
978
0
        case PHP_STREAM_SYNC_FDSYNC:
979
0
          return php_stdiop_sync(stream, 1) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
980
0
      }
981
      /* Invalid option passed */
982
0
      return PHP_STREAM_OPTION_RETURN_ERR;
983
984
0
    case PHP_STREAM_OPTION_TRUNCATE_API:
985
0
      switch (value) {
986
0
        case PHP_STREAM_TRUNCATE_SUPPORTED:
987
0
          return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
988
989
0
        case PHP_STREAM_TRUNCATE_SET_SIZE: {
990
0
          ptrdiff_t new_size = *(ptrdiff_t*)ptrparam;
991
0
          if (new_size < 0) {
992
0
            return PHP_STREAM_OPTION_RETURN_ERR;
993
0
          }
994
#ifdef PHP_WIN32
995
          HANDLE h = (HANDLE) _get_osfhandle(fd);
996
          if (INVALID_HANDLE_VALUE == h) {
997
            return PHP_STREAM_OPTION_RETURN_ERR;
998
          }
999
1000
          LARGE_INTEGER sz, old_sz;
1001
          sz.QuadPart = 0;
1002
1003
          if (!SetFilePointerEx(h, sz, &old_sz, FILE_CURRENT)) {
1004
            return PHP_STREAM_OPTION_RETURN_ERR;
1005
          }
1006
1007
#ifdef _WIN64
1008
          sz.QuadPart = new_size;
1009
#else
1010
          sz.HighPart = 0;
1011
          sz.LowPart = new_size;
1012
#endif
1013
          if (!SetFilePointerEx(h, sz, NULL, FILE_BEGIN)) {
1014
            return PHP_STREAM_OPTION_RETURN_ERR;
1015
          }
1016
          if (0 == SetEndOfFile(h)) {
1017
            return PHP_STREAM_OPTION_RETURN_ERR;
1018
          }
1019
          if (!SetFilePointerEx(h, old_sz, NULL, FILE_BEGIN)) {
1020
            return PHP_STREAM_OPTION_RETURN_ERR;
1021
          }
1022
          return PHP_STREAM_OPTION_RETURN_OK;
1023
#else
1024
0
          return ftruncate(fd, new_size) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
1025
0
#endif
1026
0
        }
1027
0
      }
1028
0
      return PHP_STREAM_OPTION_RETURN_NOTIMPL;
1029
1030
#ifdef PHP_WIN32
1031
    case PHP_STREAM_OPTION_PIPE_BLOCKING:
1032
      data->is_pipe_blocking = value;
1033
      return PHP_STREAM_OPTION_RETURN_OK;
1034
#endif
1035
0
    case PHP_STREAM_OPTION_META_DATA_API:
1036
0
      if (fd == -1)
1037
0
        return -1;
1038
0
#ifdef O_NONBLOCK
1039
0
      flags = fcntl(fd, F_GETFL, 0);
1040
1041
0
      add_assoc_bool((zval*)ptrparam, "timed_out", 0);
1042
0
      add_assoc_bool((zval*)ptrparam, "blocked", (flags & O_NONBLOCK)? 0 : 1);
1043
0
      add_assoc_bool((zval*)ptrparam, "eof", stream->eof);
1044
1045
0
      return PHP_STREAM_OPTION_RETURN_OK;
1046
0
#endif
1047
0
      return -1;
1048
0
    default:
1049
0
      return PHP_STREAM_OPTION_RETURN_NOTIMPL;
1050
0
  }
1051
0
}
1052
1053
/* This should be "const", but phpdbg overwrite it */
1054
PHPAPI php_stream_ops php_stream_stdio_ops = {
1055
  php_stdiop_write, php_stdiop_read,
1056
  php_stdiop_close, php_stdiop_flush,
1057
  "STDIO",
1058
  php_stdiop_seek,
1059
  php_stdiop_cast,
1060
  php_stdiop_stat,
1061
  php_stdiop_set_option
1062
};
1063
/* }}} */
1064
1065
/* {{{ plain files opendir/readdir implementation */
1066
static ssize_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count)
1067
0
{
1068
0
  DIR *dir = (DIR*)stream->abstract;
1069
0
  struct dirent *result;
1070
0
  php_stream_dirent *ent = (php_stream_dirent*)buf;
1071
1072
  /* avoid problems if someone mis-uses the stream */
1073
0
  if (count != sizeof(php_stream_dirent))
1074
0
    return -1;
1075
1076
0
  result = readdir(dir);
1077
0
  if (result) {
1078
0
    size_t len = strlen(result->d_name);
1079
0
    if (UNEXPECTED(len >= sizeof(ent->d_name))) {
1080
0
      return -1;
1081
0
    }
1082
    /* Include null byte */
1083
0
    memcpy(ent->d_name, result->d_name, len+1);
1084
0
#ifdef _DIRENT_HAVE_D_TYPE
1085
0
    ent->d_type = result->d_type;
1086
#else
1087
    ent->d_type = DT_UNKNOWN;
1088
#endif
1089
0
    return sizeof(php_stream_dirent);
1090
0
  }
1091
0
  return 0;
1092
0
}
1093
1094
static int php_plain_files_dirstream_close(php_stream *stream, int close_handle)
1095
0
{
1096
0
  return closedir((DIR *)stream->abstract);
1097
0
}
1098
1099
static int php_plain_files_dirstream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
1100
0
{
1101
0
  rewinddir((DIR *)stream->abstract);
1102
0
  return 0;
1103
0
}
1104
1105
static const php_stream_ops php_plain_files_dirstream_ops = {
1106
  NULL, php_plain_files_dirstream_read,
1107
  php_plain_files_dirstream_close, NULL,
1108
  "dir",
1109
  php_plain_files_dirstream_rewind,
1110
  NULL, /* cast */
1111
  NULL, /* stat */
1112
  NULL  /* set_option */
1113
};
1114
1115
static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
1116
    int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
1117
0
{
1118
0
  DIR *dir = NULL;
1119
0
  php_stream *stream = NULL;
1120
1121
0
  if (options & STREAM_USE_GLOB_DIR_OPEN) {
1122
0
    return php_glob_stream_wrapper.wops->dir_opener((php_stream_wrapper*)&php_glob_stream_wrapper, path, mode, options, opened_path, context STREAMS_REL_CC);
1123
0
  }
1124
1125
0
  if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {
1126
0
    return NULL;
1127
0
  }
1128
1129
0
  dir = VCWD_OPENDIR(path);
1130
1131
#ifdef PHP_WIN32
1132
  if (!dir) {
1133
    php_win32_docref1_from_error(GetLastError(), path);
1134
  }
1135
1136
  if (dir && dir->finished) {
1137
    closedir(dir);
1138
    dir = NULL;
1139
  }
1140
#endif
1141
0
  if (dir) {
1142
0
    stream = php_stream_alloc(&php_plain_files_dirstream_ops, dir, 0, mode);
1143
0
    if (stream == NULL)
1144
0
      closedir(dir);
1145
0
  }
1146
1147
0
  return stream;
1148
0
}
1149
/* }}} */
1150
1151
/* {{{ php_stream_fopen */
1152
PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zend_string **opened_path, int options STREAMS_DC)
1153
0
{
1154
0
  char realpath[MAXPATHLEN];
1155
0
  int open_flags;
1156
0
  int fd;
1157
0
  php_stream *ret;
1158
0
  int persistent = options & STREAM_OPEN_PERSISTENT;
1159
0
  char *persistent_id = NULL;
1160
1161
0
  if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) {
1162
0
    php_stream_wrapper_log_warn(&php_plain_files_wrapper, NULL, options,
1163
0
        InvalidMode, "`%s' is not a valid mode for fopen", mode);
1164
0
    return NULL;
1165
0
  }
1166
1167
0
  if (options & STREAM_ASSUME_REALPATH) {
1168
0
    strlcpy(realpath, filename, sizeof(realpath));
1169
0
  } else {
1170
0
    if (expand_filepath(filename, realpath) == NULL) {
1171
0
      return NULL;
1172
0
    }
1173
0
  }
1174
1175
0
  if (persistent) {
1176
0
    spprintf(&persistent_id, 0, "streams_stdio_%d_%s", open_flags, realpath);
1177
0
    switch (php_stream_from_persistent_id(persistent_id, &ret)) {
1178
0
      case PHP_STREAM_PERSISTENT_SUCCESS:
1179
0
        if (opened_path) {
1180
          //TODO: avoid reallocation???
1181
0
          *opened_path = zend_string_init(realpath, strlen(realpath), 0);
1182
0
        }
1183
0
        ZEND_FALLTHROUGH;
1184
1185
0
      case PHP_STREAM_PERSISTENT_FAILURE:
1186
0
        efree(persistent_id);
1187
0
        return ret;
1188
0
    }
1189
0
  }
1190
#ifdef PHP_WIN32
1191
  fd = php_win32_ioutil_open(realpath, open_flags, 0666);
1192
#else
1193
0
  fd = open(realpath, open_flags, 0666);
1194
0
#endif
1195
0
  if (fd != -1) {
1196
1197
0
    if (options & STREAM_OPEN_FOR_INCLUDE) {
1198
0
      ret = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);
1199
0
    } else {
1200
      /* skip the lseek(SEEK_CUR) system call to
1201
       * determine the current offset because we
1202
       * know newly opened files are at offset zero
1203
       * (unless the file has been opened in
1204
       * O_APPEND mode) */
1205
0
      ret = php_stream_fopen_from_fd_rel(fd, mode, persistent_id, (open_flags & O_APPEND) == 0);
1206
0
    }
1207
1208
0
    if (EG(active)) {
1209
      /* clear stat cache as mtime and ctime might got changed - phar can use stream before
1210
       * cache is initialized so we need to check if the execution is active. */
1211
0
      php_clear_stat_cache(0, NULL, 0);
1212
0
    }
1213
1214
0
    if (ret) {
1215
0
      if (opened_path) {
1216
0
        *opened_path = zend_string_init(realpath, strlen(realpath), 0);
1217
0
      }
1218
0
      if (persistent_id) {
1219
0
        efree(persistent_id);
1220
0
      }
1221
1222
      /* WIN32 always set ISREG flag */
1223
0
#ifndef PHP_WIN32
1224
      /* sanity checks for include/require.
1225
       * We check these after opening the stream, so that we save
1226
       * on fstat() syscalls */
1227
0
      if (options & STREAM_OPEN_FOR_INCLUDE) {
1228
0
        php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract;
1229
0
        int r;
1230
1231
0
        r = do_fstat(self, 0);
1232
0
        if ((r == 0 && !S_ISREG(self->sb.st_mode))) {
1233
0
          if (opened_path) {
1234
0
            zend_string_release_ex(*opened_path, 0);
1235
0
            *opened_path = NULL;
1236
0
          }
1237
0
          php_stream_close(ret);
1238
0
          return NULL;
1239
0
        }
1240
1241
        /* Make sure the fstat result is reused when we later try to get the
1242
         * file size. */
1243
0
        self->no_forced_fstat = 1;
1244
0
      }
1245
1246
0
      if (options & STREAM_USE_BLOCKING_PIPE) {
1247
0
        php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract;
1248
0
        self->is_pipe_blocking = 1;
1249
0
      }
1250
0
#endif
1251
1252
0
      return ret;
1253
0
    }
1254
0
    close(fd);
1255
0
  }
1256
0
  if (persistent_id) {
1257
0
    efree(persistent_id);
1258
0
  }
1259
0
  return NULL;
1260
0
}
1261
/* }}} */
1262
1263
1264
static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
1265
    int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
1266
0
{
1267
0
  if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {
1268
0
    return NULL;
1269
0
  }
1270
1271
0
  return php_stream_fopen_rel(path, mode, opened_path, options);
1272
0
}
1273
1274
static int php_plain_files_url_stater(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context)
1275
0
{
1276
0
  if (!(flags & PHP_STREAM_URL_STAT_IGNORE_OPEN_BASEDIR)) {
1277
0
    if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1278
0
      url += sizeof("file://") - 1;
1279
0
    }
1280
1281
0
    if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1)) {
1282
0
      return -1;
1283
0
    }
1284
0
  }
1285
1286
#ifdef PHP_WIN32
1287
  if (flags & PHP_STREAM_URL_STAT_LINK) {
1288
    return VCWD_LSTAT(url, &ssb->sb);
1289
  }
1290
#else
1291
0
# ifdef HAVE_SYMLINK
1292
0
  if (flags & PHP_STREAM_URL_STAT_LINK) {
1293
0
    return VCWD_LSTAT(url, &ssb->sb);
1294
0
  } else
1295
0
# endif
1296
0
#endif
1297
0
    return VCWD_STAT(url, &ssb->sb);
1298
0
}
1299
1300
static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1301
0
{
1302
0
  int ret;
1303
1304
0
  if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1305
0
    url += sizeof("file://") - 1;
1306
0
  }
1307
1308
0
  if (php_check_open_basedir(url)) {
1309
0
    return 0;
1310
0
  }
1311
1312
0
  ret = VCWD_UNLINK(url);
1313
0
  if (ret == -1) {
1314
0
    if (options & REPORT_ERRORS) {
1315
0
      char errstr[256];
1316
0
      php_stream_wrapper_warn_param(wrapper, context, options,
1317
0
          UnlinkFailed, url,
1318
0
          "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1319
0
    }
1320
0
    return 0;
1321
0
  }
1322
1323
  /* Clear stat cache (and realpath cache) */
1324
0
  php_clear_stat_cache(1, NULL, 0);
1325
1326
0
  return 1;
1327
0
}
1328
1329
static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context)
1330
0
{
1331
0
  int ret;
1332
1333
0
  if (!url_from || !url_to) {
1334
0
    return 0;
1335
0
  }
1336
1337
#ifdef PHP_WIN32
1338
  if (!php_win32_check_trailing_space(url_from, strlen(url_from))) {
1339
    php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);
1340
    return 0;
1341
  }
1342
  if (!php_win32_check_trailing_space(url_to, strlen(url_to))) {
1343
    php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);
1344
    return 0;
1345
  }
1346
#endif
1347
1348
0
  if (strncasecmp(url_from, "file://", sizeof("file://") - 1) == 0) {
1349
0
    url_from += sizeof("file://") - 1;
1350
0
  }
1351
1352
0
  if (strncasecmp(url_to, "file://", sizeof("file://") - 1) == 0) {
1353
0
    url_to += sizeof("file://") - 1;
1354
0
  }
1355
1356
0
  if (php_check_open_basedir(url_from) || php_check_open_basedir(url_to)) {
1357
0
    return 0;
1358
0
  }
1359
1360
0
  ret = VCWD_RENAME(url_from, url_to);
1361
1362
0
  if (ret == -1) {
1363
0
#ifndef PHP_WIN32
1364
0
    char errstr[256];
1365
0
# ifdef EXDEV
1366
0
    if (errno == EXDEV) {
1367
0
      zend_stat_t sb;
1368
0
# if !defined(ZTS) && !defined(TSRM_WIN32)
1369
      /* not sure what to do in ZTS case, umask is not thread-safe */
1370
0
      int oldmask = umask(077);
1371
0
# endif
1372
0
      int success = 0;
1373
0
      if (php_copy_file(url_from, url_to) == SUCCESS) {
1374
0
        if (VCWD_STAT(url_from, &sb) == 0) {
1375
0
          success = 1;
1376
0
#  ifndef TSRM_WIN32
1377
          /*
1378
           * Try to set user and permission info on the target.
1379
           * If we're not root, then some of these may fail.
1380
           * We try chown first, to set proper group info, relying
1381
           * on the system environment to have proper umask to not allow
1382
           * access to the file in the meantime.
1383
           */
1384
0
          if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) {
1385
0
            if (errno != EPERM) {
1386
0
              success = 0;
1387
0
            }
1388
0
            php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING,
1389
0
                !success, PHP_STREAM_EC(ChownFailed), url_from, url_to,
1390
0
                "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1391
0
          }
1392
1393
0
          if (success) {
1394
0
            if (VCWD_CHMOD(url_to, sb.st_mode)) {
1395
0
              if (errno != EPERM) {
1396
0
                success = 0;
1397
0
              }
1398
0
              php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING,
1399
0
                  !success, PHP_STREAM_EC(ChownFailed), url_from, url_to,
1400
0
                  "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1401
0
            }
1402
0
          }
1403
0
#  endif
1404
0
          if (success) {
1405
0
            VCWD_UNLINK(url_from);
1406
0
          }
1407
0
        } else {
1408
0
          php_stream_wrapper_warn_param2_nt(wrapper, context, options, StatFailed,
1409
0
              url_from, url_to,
1410
0
              "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1411
0
        }
1412
0
      } else {
1413
0
        php_stream_wrapper_warn_param2_nt(wrapper, context, options, CopyFailed,
1414
0
            url_from, url_to,
1415
0
            "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1416
0
      }
1417
0
#  if !defined(ZTS) && !defined(TSRM_WIN32)
1418
0
      umask(oldmask);
1419
0
#  endif
1420
0
      return success;
1421
0
    }
1422
0
# endif
1423
0
#endif
1424
1425
#ifdef PHP_WIN32
1426
    php_win32_docref2_from_error(GetLastError(), url_from, url_to);
1427
#else
1428
0
    php_stream_wrapper_warn_param2(wrapper, context, options,
1429
0
        RenameFailed, url_from, url_to,
1430
0
        "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1431
0
#endif
1432
0
    return 0;
1433
0
  }
1434
1435
  /* Clear stat cache (and realpath cache) */
1436
0
  php_clear_stat_cache(1, NULL, 0);
1437
1438
0
  return 1;
1439
0
}
1440
1441
static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, int mode, int options, php_stream_context *context)
1442
0
{
1443
0
  if (strncasecmp(dir, "file://", sizeof("file://") - 1) == 0) {
1444
0
    dir += sizeof("file://") - 1;
1445
0
  }
1446
1447
0
  if (!(options & PHP_STREAM_MKDIR_RECURSIVE)) {
1448
0
    if (php_check_open_basedir(dir)) {
1449
0
      return 0;
1450
0
    }
1451
1452
0
    int ret = VCWD_MKDIR(dir, (mode_t)mode);
1453
0
    if (ret < 0 && (options & REPORT_ERRORS)) {
1454
0
      php_stream_wrapper_warn(wrapper, context, options,
1455
0
          MkdirFailed, "%s", strerror(errno));
1456
0
      return 0;
1457
0
    }
1458
1459
0
    return 1;
1460
0
  }
1461
1462
0
  char buf[MAXPATHLEN];
1463
0
  if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND)) {
1464
0
    php_stream_wrapper_warn(wrapper, context, options,
1465
0
        InvalidPath, "Invalid path");
1466
0
    return 0;
1467
0
  }
1468
1469
0
  if (php_check_open_basedir(buf)) {
1470
0
    return 0;
1471
0
  }
1472
1473
  /* we look for directory separator from the end of string, thus hopefully reducing our work load */
1474
0
  char *p;
1475
0
  zend_stat_t sb;
1476
0
  size_t dir_len = strlen(dir), offset = 0;
1477
0
  char *e = buf +  strlen(buf);
1478
1479
0
  if ((p = memchr(buf, DEFAULT_SLASH, dir_len))) {
1480
0
    offset = p - buf + 1;
1481
0
  }
1482
1483
0
  if (p && dir_len == 1) {
1484
    /* buf == "DEFAULT_SLASH" */
1485
0
  }
1486
0
  else {
1487
    /* find a top level directory we need to create */
1488
0
    while ( (p = strrchr(buf + offset, DEFAULT_SLASH)) || (offset != 1 && (p = strrchr(buf, DEFAULT_SLASH))) ) {
1489
0
      int n = 0;
1490
1491
0
      *p = '\0';
1492
0
      while (p > buf && *(p-1) == DEFAULT_SLASH) {
1493
0
        ++n;
1494
0
        --p;
1495
0
        *p = '\0';
1496
0
      }
1497
0
      if (VCWD_STAT(buf, &sb) == 0) {
1498
0
        while (1) {
1499
0
          *p = DEFAULT_SLASH;
1500
0
          if (!n) break;
1501
0
          --n;
1502
0
          ++p;
1503
0
        }
1504
0
        break;
1505
0
      }
1506
0
    }
1507
0
  }
1508
1509
0
  if (!p) {
1510
0
    p = buf;
1511
0
  }
1512
0
  char errstr[256];
1513
0
  while (true) {
1514
0
    int ret = VCWD_MKDIR(buf, (mode_t) mode);
1515
0
    if (ret < 0 && errno != EEXIST) {
1516
0
      if (options & REPORT_ERRORS) {
1517
0
        php_stream_wrapper_warn(wrapper, context, options,
1518
0
            MkdirFailed,
1519
0
            "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1520
0
      }
1521
0
      return 0;
1522
0
    }
1523
1524
0
    bool replaced_slash = false;
1525
0
    while (++p != e) {
1526
0
      if (*p == '\0') {
1527
0
        replaced_slash = true;
1528
0
        *p = DEFAULT_SLASH;
1529
0
        if (*(p+1) != '\0') {
1530
0
          break;
1531
0
        }
1532
0
      }
1533
0
    }
1534
0
    if (p == e || !replaced_slash) {
1535
      /* No more directories to create */
1536
      /* issue a warning to client when the last directory was created failed */
1537
0
      if (ret < 0) {
1538
0
        if (options & REPORT_ERRORS) {
1539
0
          php_stream_wrapper_warn(wrapper, context, options,
1540
0
              MkdirFailed,
1541
0
              "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1542
0
        }
1543
0
        return 0;
1544
0
      }
1545
0
      return 1;
1546
0
    }
1547
0
  }
1548
0
}
1549
1550
static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1551
0
{
1552
0
  if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1553
0
    url += sizeof("file://") - 1;
1554
0
  }
1555
1556
0
  if (php_check_open_basedir(url)) {
1557
0
    return 0;
1558
0
  }
1559
1560
0
  char errstr[256];
1561
#ifdef PHP_WIN32
1562
  if (!php_win32_check_trailing_space(url, strlen(url))) {
1563
    php_stream_wrapper_warn_param(wrapper, context, options,
1564
        NotFound, url,
1565
        "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr)));
1566
    return 0;
1567
  }
1568
#endif
1569
1570
0
  if (VCWD_RMDIR(url) < 0) {
1571
0
    php_stream_wrapper_warn_param(wrapper, context, options,
1572
0
        RmdirFailed, url,
1573
0
        "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1574
0
    return 0;
1575
0
  }
1576
1577
  /* Clear stat cache (and realpath cache) */
1578
0
  php_clear_stat_cache(1, NULL, 0);
1579
1580
0
  return 1;
1581
0
}
1582
1583
static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url, int option, void *value, php_stream_context *context)
1584
0
{
1585
0
  struct utimbuf *newtime;
1586
0
#ifndef PHP_WIN32
1587
0
  uid_t uid;
1588
0
  gid_t gid;
1589
0
#endif
1590
0
  mode_t mode;
1591
0
  int ret = 0;
1592
0
  char errstr[256];
1593
1594
#ifdef PHP_WIN32
1595
  if (!php_win32_check_trailing_space(url, strlen(url))) {
1596
    php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS,
1597
        NotFound, url,
1598
        "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr)));
1599
    return 0;
1600
  }
1601
#endif
1602
1603
0
  if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
1604
0
    url += sizeof("file://") - 1;
1605
0
  }
1606
1607
0
  if (php_check_open_basedir(url)) {
1608
0
    return 0;
1609
0
  }
1610
1611
0
  switch(option) {
1612
0
    case PHP_STREAM_META_TOUCH:
1613
0
      newtime = (struct utimbuf *)value;
1614
0
      if (VCWD_ACCESS(url, F_OK) != 0) {
1615
0
        FILE *file = VCWD_FOPEN(url, "w");
1616
0
        if (file == NULL) {
1617
0
          php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS,
1618
0
              PermissionDenied, url,
1619
0
              "Unable to create file %s because %s", url,
1620
0
              php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1621
0
          return 0;
1622
0
        }
1623
0
        fclose(file);
1624
0
      }
1625
1626
0
      ret = VCWD_UTIME(url, newtime);
1627
0
      break;
1628
0
#ifndef PHP_WIN32
1629
0
    case PHP_STREAM_META_OWNER_NAME:
1630
0
    case PHP_STREAM_META_OWNER:
1631
0
      if(option == PHP_STREAM_META_OWNER_NAME) {
1632
0
        if(php_get_uid_by_name((char *)value, &uid) != SUCCESS) {
1633
0
          php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS,
1634
0
              MetaFailed, url,
1635
0
              "Unable to find uid for %s", (char *)value);
1636
0
          return 0;
1637
0
        }
1638
0
      } else {
1639
0
        uid = (uid_t)*(long *)value;
1640
0
      }
1641
0
      ret = VCWD_CHOWN(url, uid, -1);
1642
0
      break;
1643
0
    case PHP_STREAM_META_GROUP:
1644
0
    case PHP_STREAM_META_GROUP_NAME:
1645
0
      if(option == PHP_STREAM_META_GROUP_NAME) {
1646
0
        if(php_get_gid_by_name((char *)value, &gid) != SUCCESS) {
1647
0
          php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS,
1648
0
              MetaFailed, url,
1649
0
              "Unable to find gid for %s", (char *)value);
1650
0
          return 0;
1651
0
        }
1652
0
      } else {
1653
0
        gid = (gid_t)*(long *)value;
1654
0
      }
1655
0
      ret = VCWD_CHOWN(url, -1, gid);
1656
0
      break;
1657
0
#endif
1658
0
    case PHP_STREAM_META_ACCESS:
1659
0
      mode = (mode_t)*(zend_long *)value;
1660
0
      ret = VCWD_CHMOD(url, mode);
1661
0
      break;
1662
0
    default:
1663
0
      zend_value_error("Unknown option %d for stream_metadata", option);
1664
0
      return 0;
1665
0
  }
1666
0
  if (ret == -1) {
1667
0
    php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS,
1668
0
        MetaFailed, url,
1669
0
        "Operation failed: %s", php_socket_strerror_s(errno, errstr, sizeof(errstr)));
1670
0
    return 0;
1671
0
  }
1672
0
  php_clear_stat_cache(0, NULL, 0);
1673
0
  return 1;
1674
0
}
1675
1676
1677
static const php_stream_wrapper_ops php_plain_files_wrapper_ops = {
1678
  php_plain_files_stream_opener,
1679
  NULL,
1680
  NULL,
1681
  php_plain_files_url_stater,
1682
  php_plain_files_dir_opener,
1683
  "plainfile",
1684
  php_plain_files_unlink,
1685
  php_plain_files_rename,
1686
  php_plain_files_mkdir,
1687
  php_plain_files_rmdir,
1688
  php_plain_files_metadata
1689
};
1690
1691
/* TODO: We have to make php_plain_files_wrapper writable to support SWOOLE */
1692
PHPAPI /*const*/ php_stream_wrapper php_plain_files_wrapper = {
1693
  &php_plain_files_wrapper_ops,
1694
  NULL,
1695
  0
1696
};
1697
1698
/* {{{ php_stream_fopen_with_path */
1699
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)
1700
0
{
1701
  /* code ripped off from fopen_wrappers.c */
1702
0
  char *pathbuf, *end;
1703
0
  const char *ptr;
1704
0
  char trypath[MAXPATHLEN];
1705
0
  php_stream *stream;
1706
0
  size_t filename_length;
1707
0
  zend_string *exec_filename;
1708
1709
0
  if (opened_path) {
1710
0
    *opened_path = NULL;
1711
0
  }
1712
1713
0
  if(!filename) {
1714
0
    return NULL;
1715
0
  }
1716
1717
0
  filename_length = strlen(filename);
1718
0
#ifndef PHP_WIN32
1719
0
  (void) filename_length;
1720
0
#endif
1721
1722
  /* Relative path open */
1723
0
  if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) {
1724
    /* further checks, we could have ....... filenames */
1725
0
    ptr = filename + 1;
1726
0
    if (*ptr == '.') {
1727
0
      while (*(++ptr) == '.');
1728
0
      if (!IS_SLASH(*ptr)) { /* not a relative path after all */
1729
0
        goto not_relative_path;
1730
0
      }
1731
0
    }
1732
1733
1734
0
    if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename)) {
1735
0
      return NULL;
1736
0
    }
1737
1738
0
    return php_stream_fopen_rel(filename, mode, opened_path, options);
1739
0
  }
1740
1741
0
not_relative_path:
1742
1743
  /* Absolute path open */
1744
0
  if (IS_ABSOLUTE_PATH(filename, filename_length)) {
1745
1746
0
    if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename)) {
1747
0
      return NULL;
1748
0
    }
1749
1750
0
    return php_stream_fopen_rel(filename, mode, opened_path, options);
1751
0
  }
1752
1753
#ifdef PHP_WIN32
1754
  if (IS_SLASH(filename[0])) {
1755
    size_t cwd_len;
1756
    char *cwd;
1757
    cwd = virtual_getcwd_ex(&cwd_len);
1758
    /* getcwd() will return always return [DRIVE_LETTER]:/) on windows. */
1759
    *(cwd+3) = '\0';
1760
1761
    if (snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename) >= MAXPATHLEN) {
1762
      php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS,
1763
          PathTooLong,
1764
          "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN);
1765
    }
1766
1767
    efree(cwd);
1768
1769
    if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(trypath)) {
1770
      return NULL;
1771
    }
1772
1773
    return php_stream_fopen_rel(trypath, mode, opened_path, options);
1774
  }
1775
#endif
1776
1777
0
  if (!path || !*path) {
1778
0
    return php_stream_fopen_rel(filename, mode, opened_path, options);
1779
0
  }
1780
1781
  /* check in provided path */
1782
  /* append the calling scripts' current working directory
1783
   * as a fallback case
1784
   */
1785
0
  if (zend_is_executing() &&
1786
0
      (exec_filename = zend_get_executed_filename_ex()) != NULL) {
1787
0
    const char *exec_fname = ZSTR_VAL(exec_filename);
1788
0
    size_t exec_fname_length = ZSTR_LEN(exec_filename);
1789
1790
0
    while ((--exec_fname_length < SIZE_MAX) && !IS_SLASH(exec_fname[exec_fname_length]));
1791
0
    if (exec_fname_length<=0) {
1792
      /* no path */
1793
0
      pathbuf = estrdup(path);
1794
0
    } else {
1795
0
      size_t path_length = strlen(path);
1796
1797
0
      pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1);
1798
0
      memcpy(pathbuf, path, path_length);
1799
0
      pathbuf[path_length] = DEFAULT_DIR_SEPARATOR;
1800
0
      memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length);
1801
0
      pathbuf[path_length + exec_fname_length +1] = '\0';
1802
0
    }
1803
0
  } else {
1804
0
    pathbuf = estrdup(path);
1805
0
  }
1806
1807
0
  ptr = pathbuf;
1808
1809
0
  while (ptr && *ptr) {
1810
0
    end = (char *) strchr(ptr, DEFAULT_DIR_SEPARATOR);
1811
0
    if (end != NULL) {
1812
0
      *end = '\0';
1813
0
      end++;
1814
0
    }
1815
0
    if (*ptr == '\0') {
1816
0
      goto stream_skip;
1817
0
    }
1818
0
    if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) {
1819
0
      php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS,
1820
0
          PathTooLong,
1821
0
          "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN);
1822
0
    }
1823
1824
0
    if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0)) {
1825
0
      goto stream_skip;
1826
0
    }
1827
1828
0
    stream = php_stream_fopen_rel(trypath, mode, opened_path, options);
1829
0
    if (stream) {
1830
0
      efree(pathbuf);
1831
0
      return stream;
1832
0
    }
1833
0
stream_skip:
1834
0
    ptr = end;
1835
0
  } /* end provided path */
1836
1837
0
  efree(pathbuf);
1838
0
  return NULL;
1839
1840
0
}
1841
/* }}} */