[security][CVE-2022-27809] Builtins should always take int64_t, not int
[hiphop-php.git] / hphp / runtime / ext / std / ext_std_file.cpp
blob83e1fdff4ff2937d31cc5a80c41523694a22545b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/std/ext_std_file.h"
20 #include "hphp/util/hphp-config.h"
21 #include "hphp/runtime/base/array-init.h"
22 #include "hphp/runtime/base/array-util.h"
23 #include "hphp/runtime/base/comparisons.h"
24 #include "hphp/runtime/base/directory.h"
25 #include "hphp/runtime/base/file-stream-wrapper.h"
26 #include "hphp/runtime/base/file-util.h"
27 #include "hphp/runtime/base/http-client.h"
28 #include "hphp/runtime/base/http-stream-wrapper.h"
29 #include "hphp/runtime/base/ini-setting.h"
30 #include "hphp/runtime/base/pipe.h"
31 #include "hphp/runtime/base/plain-file.h"
32 #include "hphp/runtime/base/temp-file.h"
33 #include "hphp/runtime/base/runtime-error.h"
34 #include "hphp/runtime/base/runtime-option.h"
35 #include "hphp/runtime/base/stat-cache.h"
36 #include "hphp/runtime/base/stream-wrapper-registry.h"
37 #include "hphp/runtime/base/string-util.h"
38 #include "hphp/runtime/base/request-info.h"
39 #include "hphp/runtime/base/zend-scanf.h"
40 #include "hphp/runtime/ext/hash/ext_hash.h"
41 #if ENABLE_EXTENSION_POSIX
42 #include "hphp/runtime/ext/posix/ext_posix.h"
43 #endif
44 #include "hphp/runtime/ext/std/ext_std_options.h"
45 #include "hphp/runtime/ext/string/ext_string.h"
46 #include "hphp/runtime/server/cli-server.h"
47 #include "hphp/runtime/server/static-content-cache.h"
48 #include "hphp/system/systemlib.h"
49 #include "hphp/util/logger.h"
50 #include "hphp/util/process.h"
51 #include "hphp/util/rds-local.h"
52 #include "hphp/util/user-info.h"
54 #include <folly/String.h>
55 #include <folly/portability/Dirent.h>
56 #include <folly/portability/SysFile.h>
58 #include <sys/types.h>
60 #ifndef _MSC_VER
61 #include <glob.h>
62 #if defined(__FreeBSD__) || defined(__APPLE__)
63 # include <sys/mount.h>
64 #else
65 # include <sys/vfs.h>
66 #endif
67 #include <utime.h>
68 #include <grp.h>
69 #include <pwd.h>
70 #include <fnmatch.h>
71 #endif
73 #include <boost/algorithm/string/predicate.hpp>
74 #include <boost/filesystem.hpp>
75 #include <vector>
77 namespace fs = boost::filesystem;
79 #define CHECK_HANDLE_BASE(handle, f, ret) \
80 auto f = dyn_cast_or_null<File>(handle); \
81 if (f == nullptr || f->isClosed()) { \
82 raise_warning("Not a valid stream resource"); \
83 return (ret); \
84 } \
86 #define CHECK_HANDLE(handle, f) \
87 CHECK_HANDLE_BASE(handle, f, false)
88 #define CHECK_HANDLE_RET_NULL(handle, f) \
89 CHECK_HANDLE_BASE(handle, f, uninit_variant)
91 #define CHECK_PATH_BASE(p, i, ret) \
92 if (p.size() != strlen(p.data())) { \
93 raise_warning( \
94 "%s() expects parameter %d to be a valid path, string given", \
95 __FUNCTION__ + 2, i \
96 ); \
97 return (ret); \
100 #define CHECK_PATH(p, i) \
101 CHECK_PATH_BASE(p, i, uninit_variant)
102 #define CHECK_PATH_FALSE(p, i) \
103 CHECK_PATH_BASE(p, i, false)
105 #define CHECK_SYSTEM(exp) \
106 if ((exp) != 0) { \
107 raise_warning( \
108 "%s(): %s", \
109 __FUNCTION__ + 2, \
110 folly::errnoStr(errno).c_str() \
111 ); \
112 return false; \
115 #define CHECK_SYSTEM_SILENT(exp) \
116 if ((exp) != 0) { \
117 Logger::Verbose("%s/%d: %s", __FUNCTION__, __LINE__, \
118 folly::errnoStr(errno).c_str()); \
119 return false; \
122 #define CHECK_BOOST(dest, func, path) \
123 boost::system::error_code ec; \
124 dest = func(path.toCppString(), ec); \
125 if (ec) { \
126 raise_warning( \
127 "%s(): %s", \
128 __FUNCTION__ + 2, \
129 ec.message().c_str() \
130 ); \
131 return false; \
134 #define CHECK_BOOST_SILENT(dest, func, path) \
135 boost::system::error_code ec; \
136 dest = func(path.toCppString(), ec); \
137 if (ec) { \
138 Logger::Verbose("%s/%d: %s", __FUNCTION__, __LINE__, \
139 ec.message().c_str() \
140 ); \
141 return false; \
144 #define CHECK_BOOST_ASSIGN(func, path, arg) \
145 boost::system::error_code ec; \
146 func(path.toCppString(), arg, ec); \
147 if (ec) { \
148 raise_warning( \
149 "%s(): %s", \
150 __FUNCTION__ + 2, \
151 ec.message().c_str() \
152 ); \
153 return false; \
156 // libxml/xpathInternals.h defines CHECK_ERROR,
157 // we need to undef it first
158 #ifdef CHECK_ERROR
159 #undef CHECK_ERROR
160 #endif
161 #define CHECK_ERROR(ret) \
162 check_error(__FUNCTION__, __LINE__, (ret))
164 namespace HPHP {
166 static const StaticString s_STREAM_URL_STAT_LINK("STREAM_URL_STAT_LINK");
167 static const StaticString s_STREAM_URL_STAT_QUIET("STREAM_URL_STAT_QUIET");
169 ///////////////////////////////////////////////////////////////////////////////
170 // helpers
172 static bool check_error(const char *function, int line, bool ret) {
173 if (!ret) {
174 Logger::Verbose("%s/%d: %s", function, line,
175 folly::errnoStr(errno).c_str());
177 return ret;
180 static int accessSyscall(
181 const String& uri_or_path,
182 int mode,
183 bool useFileCache = false) {
184 Stream::Wrapper* w = Stream::getWrapperFromURI(uri_or_path);
185 if (!w) return -1;
187 if (useFileCache && dynamic_cast<FileStreamWrapper*>(w)) {
188 String path(uri_or_path);
189 if (UNLIKELY(StringUtil::IsFileUrl(uri_or_path.data()))) {
190 path = StringUtil::DecodeFileUrl(uri_or_path);
191 if (path.empty()) {
192 return -1;
195 return ::access(File::TranslatePathWithFileCache(path).data(), mode);
197 return w->access(uri_or_path, mode);
200 static int statSyscall(
201 const String& path,
202 struct stat* buf,
203 bool useFileCache = false) {
204 bool isRelative = !FileUtil::isAbsolutePath(path.slice());
205 int pathIndex = 0;
206 Stream::Wrapper* w = Stream::getWrapperFromURI(path, &pathIndex);
207 if (!w) return -1;
208 bool isFileStream = dynamic_cast<FileStreamWrapper*>(w);
209 auto canUseFileCache = useFileCache && isFileStream;
210 if (isRelative && !pathIndex) {
211 auto fullpath = g_context->getCwd() + String::FromChar('/') + path;
212 if (!RID().hasSafeFileAccess() && !canUseFileCache) {
213 if (strlen(fullpath.data()) != fullpath.size()) return ENOENT;
214 if (!isFileStream && w->isNormalFileStream()) {
215 return w->stat(fullpath.data(), buf);
217 return ::stat(fullpath.data(), buf);
219 std::string realpath = StatCache::realpath(fullpath.data());
220 // realpath will return an empty string for nonexistent files
221 if (realpath.empty()) {
222 return ENOENT;
224 auto translatedPath = canUseFileCache ?
225 File::TranslatePathWithFileCache(realpath) :
226 File::TranslatePath(realpath);
227 if (!isFileStream && w->isNormalFileStream()) {
228 return w->stat(translatedPath.data(), buf);
230 return ::stat(translatedPath.data(), buf);
233 auto properPath = w->isNormalFileStream() ? path.substr(pathIndex) : path;
234 if (canUseFileCache) {
235 return ::stat(File::TranslatePathWithFileCache(properPath).data(), buf);
237 return w->stat(properPath, buf);
240 static int lstatSyscall(
241 const String& path,
242 struct stat* buf,
243 bool useFileCache = false) {
244 Stream::Wrapper* w = Stream::getWrapperFromURI(path);
245 if (!w) return -1;
246 if (useFileCache && dynamic_cast<FileStreamWrapper*>(w)) {
247 return ::lstat(File::TranslatePathWithFileCache(path).data(), buf);
249 return w->lstat(path, buf);
252 const StaticString
253 s_dev("dev"),
254 s_ino("ino"),
255 s_mode("mode"),
256 s_nlink("nlink"),
257 s_uid("uid"),
258 s_gid("gid"),
259 s_rdev("rdev"),
260 s_size("size"),
261 s_atime("atime"),
262 s_mtime("mtime"),
263 s_ctime("ctime"),
264 s_blksize("blksize"),
265 s_blocks("blocks");
267 Array stat_impl(struct stat *stat_sb) {
268 DictInit ret(26);
269 ret.append((int64_t)stat_sb->st_dev);
270 ret.append((int64_t)stat_sb->st_ino);
271 ret.append((int64_t)stat_sb->st_mode);
272 ret.append((int64_t)stat_sb->st_nlink);
273 ret.append((int64_t)stat_sb->st_uid);
274 ret.append((int64_t)stat_sb->st_gid);
275 ret.append((int64_t)stat_sb->st_rdev);
276 ret.append((int64_t)stat_sb->st_size);
277 ret.append((int64_t)stat_sb->st_atime);
278 ret.append((int64_t)stat_sb->st_mtime);
279 ret.append((int64_t)stat_sb->st_ctime);
280 #ifndef _MSC_VER
281 ret.append((int64_t)stat_sb->st_blksize);
282 ret.append((int64_t)stat_sb->st_blocks);
283 #endif
284 ret.set(s_dev, (int64_t)stat_sb->st_dev);
285 ret.set(s_ino, (int64_t)stat_sb->st_ino);
286 ret.set(s_mode, (int64_t)stat_sb->st_mode);
287 ret.set(s_nlink, (int64_t)stat_sb->st_nlink);
288 ret.set(s_uid, (int64_t)stat_sb->st_uid);
289 ret.set(s_gid, (int64_t)stat_sb->st_gid);
290 ret.set(s_rdev, (int64_t)stat_sb->st_rdev);
291 ret.set(s_size, (int64_t)stat_sb->st_size);
292 ret.set(s_atime, (int64_t)stat_sb->st_atime);
293 ret.set(s_mtime, (int64_t)stat_sb->st_mtime);
294 ret.set(s_ctime, (int64_t)stat_sb->st_ctime);
295 #ifndef _MSC_VER
296 ret.set(s_blksize, (int64_t)stat_sb->st_blksize);
297 ret.set(s_blocks, (int64_t)stat_sb->st_blocks);
298 #endif
299 return ret.toArray();
302 ///////////////////////////////////////////////////////////////////////////////
304 Variant HHVM_FUNCTION(fopen,
305 const String& filename,
306 const String& mode,
307 bool use_include_path /* = false */,
308 const Variant& context /* = null */) {
309 CHECK_PATH_FALSE(filename, 1);
310 if (!context.isNull() && dyn_cast<StreamContext>(context) == nullptr) {
311 raise_warning("$context must be a valid Stream Context or NULL");
312 return false;
314 if (filename.empty()) {
315 raise_warning("Filename cannot be empty");
316 return false;
319 auto file = File::Open(filename, mode,
320 use_include_path ? File::USE_INCLUDE_PATH : 0,
321 cast_or_null<StreamContext>(context));
322 if (!file) {
323 return false;
325 return Variant(file);
328 Variant HHVM_FUNCTION(popen,
329 const String& command,
330 const String& mode) {
331 CHECK_PATH_FALSE(command, 1);
332 auto file = req::make<Pipe>();
333 bool ret = CHECK_ERROR(file->open(File::TranslateCommand(command), mode));
334 if (!ret) {
335 raise_warning("popen(%s,%s): Invalid argument",
336 command.data(), mode.data());
337 return false;
339 return Variant(std::move(file));
342 bool HHVM_FUNCTION(fclose,
343 const Resource& handle) {
344 CHECK_HANDLE(handle, f);
345 return CHECK_ERROR(f->close());
348 Variant HHVM_FUNCTION(pclose,
349 const Variant& handle) {
350 CHECK_HANDLE(handle.toResource(), f);
351 CHECK_ERROR(f->close());
352 return *s_pcloseRet;
355 Variant HHVM_FUNCTION(fseek,
356 const Resource& handle,
357 int64_t offset,
358 int64_t whence /* = SEEK_SET */) {
359 CHECK_HANDLE(handle, f);
360 return CHECK_ERROR(f->seek(offset, whence)) ? 0 : -1;
363 bool HHVM_FUNCTION(rewind,
364 const Resource& handle) {
365 CHECK_HANDLE(handle, f);
366 return CHECK_ERROR(f->rewind());
369 Variant HHVM_FUNCTION(ftell,
370 const Resource& handle) {
371 CHECK_HANDLE(handle, f);
372 int64_t ret = f->tell();
373 if (!CHECK_ERROR(ret != -1)) {
374 return false;
376 return ret;
379 bool HHVM_FUNCTION(feof,
380 const Resource& handle) {
381 CHECK_HANDLE(handle, f);
382 return f->eof();
385 Variant HHVM_FUNCTION(fstat,
386 const Resource& handle) {
387 CHECK_HANDLE(handle, f);
388 struct stat sb;
389 if (!CHECK_ERROR(f->stat(&sb)))
390 return false;
391 return stat_impl(&sb);
394 Variant HHVM_FUNCTION(fread,
395 const Resource& handle,
396 int64_t length) {
397 CHECK_HANDLE(handle, f);
398 if (length < 1) {
399 raise_warning(
400 "fread(): Length parameter must be greater than 0"
402 return false;
404 return f->read(length);
407 Variant HHVM_FUNCTION(fgetc,
408 const Resource& handle) {
409 CHECK_HANDLE(handle, f);
410 int result = f->getc();
411 if (result == EOF) {
412 return false;
414 return String::FromChar(result);
417 Variant HHVM_FUNCTION(fgets,
418 const Resource& handle,
419 int64_t length /* = 0 */) {
420 if (length < 0) {
421 raise_invalid_argument_warning("length (negative): %" PRId64, length);
422 return false;
424 CHECK_HANDLE(handle, f);
425 String line = f->readLine(length - 1);
426 if (!line.isNull()) {
427 return line;
429 return false;
432 Variant HHVM_FUNCTION(fgetss,
433 const Resource& handle,
434 int64_t length /* = 0 */,
435 const String& allowable_tags /* = null_string */) {
436 Variant ret = HHVM_FN(fgets)(handle, length);
437 if (!same(ret, false)) {
438 return StringUtil::StripHTMLTags(ret.toString(), allowable_tags);
440 return ret;
443 Variant HHVM_FUNCTION(fscanf,
444 const Resource& handle,
445 const String& format) {
446 CHECK_HANDLE(handle, f);
447 String line = f->readLine();
448 if (line.length() == 0) {
449 return false;
451 return HHVM_FN(sscanf)(line, format);
454 Variant HHVM_FUNCTION(fpassthru,
455 const Resource& handle) {
456 CHECK_HANDLE(handle, f);
457 return f->print();
460 Variant HHVM_FUNCTION(fwrite,
461 const Resource& handle,
462 const String& data,
463 int64_t length /* = 0 */) {
464 CHECK_HANDLE(handle, f);
465 int64_t ret = f->write(data, length);
466 if (ret < 0) {
467 raise_notice("fwrite(): send of %ld bytes failed with errno=%d %s",
468 data.size(), errno, folly::errnoStr(errno).c_str());
469 ret = 0;
471 return ret;
474 Variant HHVM_FUNCTION(fputs,
475 const Resource& handle,
476 const String& data,
477 int64_t length /* = 0 */) {
478 CHECK_HANDLE(handle, f);
479 int64_t ret = f->write(data, length);
480 if (ret < 0) ret = 0;
481 return ret;
484 Variant HHVM_FUNCTION(fprintf,
485 const Variant& handle,
486 const String& format,
487 const Array& args /* = null_array */) {
488 if (!handle.isResource()) {
489 raise_param_type_warning("fprintf", 1, "resource", *handle.asTypedValue());
490 return false;
492 const Resource res = handle.toResource();
493 CHECK_HANDLE(res, f);
494 return f->printf(format, args);
497 Variant HHVM_FUNCTION(vfprintf,
498 const Variant& handle,
499 const Variant& format,
500 const Variant& args) {
501 CHECK_HANDLE(handle.toResource(), f);
502 return f->printf(format.toString(), args.toArray());
505 bool HHVM_FUNCTION(fflush,
506 const Resource& handle) {
507 CHECK_HANDLE(handle, f);
508 return CHECK_ERROR(f->flush());
511 bool HHVM_FUNCTION(ftruncate,
512 const Resource& handle,
513 int64_t size) {
514 CHECK_HANDLE(handle, f);
515 return CHECK_ERROR(f->truncate(size));
518 static int flock_values[] = { LOCK_SH, LOCK_EX, LOCK_UN };
520 bool HHVM_FUNCTION(flock,
521 const Resource& handle,
522 int64_t operation,
523 bool& wouldblock) {
524 CHECK_HANDLE(handle, f);
525 int act;
526 wouldblock = false;
528 act = operation & 3;
529 if (act < 1 || act > 3) {
530 raise_invalid_argument_warning("operation: %ld", operation);
531 return false;
533 act = flock_values[act - 1] | (operation & 4 ? LOCK_NB : 0);
534 bool ret = f->lock(act, wouldblock);
535 return ret;
538 // match the behavior of PHP5
539 #define FCSV_CHECK_ARG(NAME) \
540 if (NAME.size() == 0) { \
541 raise_invalid_argument_warning(#NAME ": %s", NAME.data()); \
542 return false; \
543 } else if (NAME.size() > 1) { \
544 raise_notice(#NAME " must be a single character"); \
546 char NAME ## _char = NAME.charAt(0); \
548 Variant HHVM_FUNCTION(fputcsv,
549 const Resource& handle,
550 const Array& fields,
551 const String& delimiter /* = "," */,
552 const String& enclosure /* = "\"" */,
553 const String& escape /* = "\\" */) {
554 FCSV_CHECK_ARG(delimiter);
555 FCSV_CHECK_ARG(enclosure);
556 FCSV_CHECK_ARG(escape);
558 CHECK_HANDLE_RET_NULL(handle, f);
559 return f->writeCSV(fields, delimiter_char, enclosure_char, escape_char);
562 Variant HHVM_FUNCTION(fgetcsv,
563 const Resource& handle,
564 int64_t length /* = 0 */,
565 const String& delimiter /* = "," */,
566 const String& enclosure /* = "\"" */,
567 const String& escape /* = "\\" */) {
568 if (length < 0) {
569 raise_invalid_argument_warning("Length parameter may not be negative");
570 return false;
573 FCSV_CHECK_ARG(delimiter);
574 FCSV_CHECK_ARG(enclosure);
575 FCSV_CHECK_ARG(escape);
577 CHECK_HANDLE_RET_NULL(handle, f);
578 Array ret = f->readCSV(length, delimiter_char, enclosure_char, escape_char);
579 if (!ret.isNull()) {
580 return ret;
582 return false;
585 ///////////////////////////////////////////////////////////////////////////////
587 Variant HHVM_FUNCTION(file_get_contents,
588 const String& filename,
589 bool use_include_path /* = false */,
590 const Variant& context /* = null */,
591 int64_t offset /* = -1 */,
592 int64_t maxlen /* = -1 */) {
593 CHECK_PATH(filename, 1);
594 Variant stream = HHVM_FN(fopen)(filename, "rb", use_include_path, context);
595 if (same(stream, false)) return false;
596 return HHVM_FN(stream_get_contents)(stream.toResource(), maxlen, offset);
599 Variant HHVM_FUNCTION(file_put_contents,
600 const String& filename,
601 const Variant& data,
602 int64_t flags /* = 0 */,
603 const Variant& context /* = null */) {
604 CHECK_PATH(filename, 1);
606 char mode[3] = "wb";
607 if (flags & PHP_FILE_APPEND) {
608 mode[0] = 'a';
609 } else if (flags & LOCK_EX) {
610 // Open in "create" mode (writing only, create if needed, no truncate)
611 // so that the file is not modified before we attempt to aquire the
612 // requested lock.
613 mode[0] = 'c';
615 mode[2] = '\0';
617 auto file = File::Open(
618 filename,
619 mode,
620 flags,
621 dyn_cast_or_null<StreamContext>(context)
624 if (!file) {
625 return false;
628 if (flags & LOCK_EX) {
629 // Check to make sure we are dealing with a regular file
630 if (!isa<PlainFile>(file)) {
631 raise_warning(
632 "%s(): Exclusive locks may only be set for regular files",
633 __FUNCTION__ + 2);
634 return false;
637 if (!file->lock(LOCK_EX)) {
638 return false;
642 if (mode[0] == 'c') {
643 file->truncate(0);
646 int numbytes = 0;
648 switch (data.getType()) {
649 case KindOfResource: {
650 auto fsrc = dyn_cast_or_null<File>(data);
651 if (!fsrc) {
652 raise_warning("Not a valid stream resource");
653 return false;
655 while (true) {
656 char buffer[1024];
657 int len = fsrc->readImpl(buffer, sizeof(buffer));
658 if (len == 0) break;
659 numbytes += len;
660 int written = file->writeImpl(buffer, len);
661 if (written != len) {
662 numbytes = -1;
663 break;
666 break;
669 case KindOfPersistentVec:
670 case KindOfVec:
671 case KindOfPersistentDict:
672 case KindOfDict:
673 case KindOfPersistentKeyset:
674 case KindOfKeyset:
675 case KindOfClsMeth: {
676 Array arr = data.toArray();
677 for (ArrayIter iter(arr); iter; ++iter) {
678 auto const value = iter.second().toString();
679 if (!value.empty()) {
680 numbytes += value.size();
681 int written = file->writeImpl(value.data(), value.size());
682 if (written != value.size()) {
683 numbytes = -1;
684 break;
688 break;
691 case KindOfObject:
692 if (!data.getObjectData()->hasToString()) {
693 raise_warning("Not a valid stream resource");
694 return false;
696 // fallthrough
697 case KindOfUninit:
698 case KindOfNull:
699 case KindOfBoolean:
700 case KindOfInt64:
701 case KindOfDouble:
702 case KindOfPersistentString:
703 case KindOfString:
704 case KindOfFunc:
705 case KindOfClass:
706 case KindOfLazyClass: {
707 String value = data.toString();
708 if (!value.empty()) {
709 numbytes += value.size();
710 int written = file->writeImpl(value.data(), value.size());
711 if (written != value.size()) {
712 numbytes = -1;
715 break;
717 case KindOfRFunc:
718 case KindOfRClsMeth:
719 raise_warning("Not a valid stream resource");
720 return false;
723 // like fwrite(), fclose() can error when fflush()ing
724 if (numbytes < 0 || !file->close()) {
725 return false;
728 return numbytes;
731 Variant HHVM_FUNCTION(file,
732 const String& filename,
733 int64_t flags /* = 0 */,
734 const Variant& context /* = null */) {
735 CHECK_PATH(filename, 1);
736 Variant contents = HHVM_FN(file_get_contents)(filename,
737 flags & PHP_FILE_USE_INCLUDE_PATH,
738 context);
739 if (same(contents, false)) {
740 return false;
742 String content = contents.toString();
743 if (content.empty()) {
744 return empty_vec_array();
746 auto ret = Array::CreateVec();
748 char eol_marker = '\n';
749 bool include_new_line = !(flags & PHP_FILE_IGNORE_NEW_LINES);
750 bool skip_blank_lines = flags & PHP_FILE_SKIP_EMPTY_LINES;
751 const char *s = content.data();
752 const char *e = s + content.size();
753 const char *p = (const char *)memchr(s, '\n', content.size());
754 if (!p) {
755 p = e;
756 goto parse_eol;
759 if (include_new_line) {
760 do {
761 p++;
762 parse_eol:
763 ret.append(String(s, p-s, CopyString));
764 s = p;
765 } while ((p = (const char *)memchr(p, eol_marker, (e-p))));
766 } else {
767 do {
768 int windows_eol = 0;
769 if (p != content.data() && eol_marker == '\n' && *(p - 1) == '\r') {
770 windows_eol++;
773 if (skip_blank_lines && !(p-s-windows_eol)) {
774 s = ++p;
775 continue;
777 ret.append(String(s, p-s-windows_eol, CopyString));
778 s = ++p;
779 } while ((p = (const char *)memchr(p, eol_marker, (e-p))));
782 /* handle any left overs of files without new lines */
783 if (s != e) {
784 p = e;
785 goto parse_eol;
787 return ret;
790 Variant HHVM_FUNCTION(readfile,
791 const String& filename,
792 bool use_include_path /* = false */,
793 const Variant& context /* = null */) {
794 CHECK_PATH_FALSE(filename, 1);
795 Variant f = HHVM_FN(fopen)(filename, "rb", use_include_path, context);
796 if (same(f, false)) {
797 Logger::Verbose("%s/%d: %s", __FUNCTION__, __LINE__,
798 folly::errnoStr(errno).c_str());
799 return false;
801 Variant ret = HHVM_FN(fpassthru)(f.toResource());
802 return ret;
805 bool HHVM_FUNCTION(move_uploaded_file,
806 const String& filename,
807 const String& destination) {
808 Transport *transport = g_context->getTransport();
809 if (!transport || !transport->isUploadedFile(filename)) {
810 return false;
813 CHECK_PATH_FALSE(destination, 2);
815 if (HHVM_FN(rename)(filename, destination)) {
816 return true;
819 // If rename didn't work, fall back to copy followed by unlink
820 if (!HHVM_FN(copy)(filename, destination)) {
821 return false;
823 HHVM_FN(unlink)(filename);
825 return true;
828 namespace {
830 String resolve_parse_ini_filename(const String& filename) {
831 // Try cleaning up the path. Use file_exists to avoid a warning
832 // that get_contents generates
833 String resolved = File::TranslatePath(filename);
834 if (!resolved.empty() && HHVM_FN(file_exists)(resolved)) {
835 return resolved;
838 if (FileUtil::isAbsolutePath(filename.data())) {
839 return null_string;
842 // Still no go, and not an absolute path, try to resolve based
843 // on containing file name.
844 auto const cfd = String::attach(g_context->getContainingFileName());
845 if (!cfd.empty()) {
846 int npos = cfd.rfind('/');
847 if (npos >= 0) {
848 resolved = cfd.substr(0, npos + 1) + filename;
849 if (!resolved.empty() && HHVM_FN(file_exists)(resolved)) {
850 return resolved;
855 // Next, see if include path was set in the ini settings.
856 auto const& includePaths = RID().getIncludePaths();
858 for (auto const& path : includePaths) {
859 resolved = path + FileUtil::getDirSeparator() + filename;
860 if (HHVM_FN(file_exists)(resolved)) {
861 return resolved;
865 return null_string;
869 Variant HHVM_FUNCTION(parse_ini_file,
870 const String& filename,
871 bool process_sections /* = false */,
872 int64_t scanner_mode /* = k_INI_SCANNER_NORMAL */) {
873 CHECK_PATH_FALSE(filename, 1);
874 if (filename.empty()) {
875 raise_invalid_argument_warning("Filename cannot be empty!");
876 return false;
879 // Block ability to load ini files via http (eg., remotely)
880 int oFilename = 0;
881 Stream::Wrapper* w = Stream::getWrapperFromURI(filename, &oFilename);
882 if (nullptr != dynamic_cast<HttpStreamWrapper*>(w)) {
883 raise_warning("remote access to ini files is not allowed");
884 return false;
887 // Extract the (local) filename
888 String wrapperPrefix = filename.substr(0, oFilename);
889 String path = resolve_parse_ini_filename(filename.substr(oFilename));
891 if (path.empty()) {
892 // At this point, we were unable to find the file.
893 raise_warning("No such file or directory");
894 return false;
897 Variant content = HHVM_FN(file_get_contents)(wrapperPrefix + path);
898 if (same(content, false)) {
899 // Don't generate a warning, as they have already been generated.
900 return false;
903 return IniSetting::FromString(content.toString(), filename, process_sections,
904 scanner_mode);
907 Variant HHVM_FUNCTION(parse_ini_string,
908 const String& ini,
909 bool process_sections /* = false */,
910 int64_t scanner_mode /* = k_INI_SCANNER_NORMAL */) {
911 return IniSetting::FromString(ini, "", process_sections, scanner_mode);
914 Variant HHVM_FUNCTION(md5_file,
915 const String& filename,
916 bool raw_output /* = false */) {
917 CHECK_PATH(filename, 1);
918 return HHVM_FN(hash_file)("md5", filename, raw_output);
921 Variant HHVM_FUNCTION(sha1_file,
922 const String& filename,
923 bool raw_output /* = false */) {
924 CHECK_PATH(filename, 1);
925 return HHVM_FN(hash_file)("sha1", filename, raw_output);
928 ///////////////////////////////////////////////////////////////////////////////
929 // stats functions
931 Variant HHVM_FUNCTION(fileperms,
932 const String& filename) {
933 CHECK_PATH(filename, 1);
934 struct stat sb;
935 CHECK_SYSTEM(statSyscall(filename, &sb, true));
936 return (int64_t)sb.st_mode;
939 Variant HHVM_FUNCTION(fileinode,
940 const String& filename) {
941 CHECK_PATH(filename, 1);
942 struct stat sb;
943 CHECK_SYSTEM(statSyscall(filename, &sb));
944 return (int64_t)sb.st_ino;
947 Variant HHVM_FUNCTION(filesize,
948 const String& filename) {
949 CHECK_PATH(filename, 1);
950 if (filename.empty()) {
951 return false;
953 if (StaticContentCache::TheFileCache) {
954 int64_t size =
955 StaticContentCache::TheFileCache->fileSize(filename.data(),
956 filename.data()[0] != '/');
957 if (size >= 0) return size;
959 struct stat sb;
960 CHECK_SYSTEM(statSyscall(filename, &sb, true));
961 return (int64_t)sb.st_size;
964 Variant HHVM_FUNCTION(fileowner,
965 const String& filename) {
966 CHECK_PATH(filename, 1);
967 struct stat sb;
968 CHECK_SYSTEM(statSyscall(filename, &sb, true));
969 return (int64_t)sb.st_uid;
972 Variant HHVM_FUNCTION(filegroup,
973 const String& filename) {
974 CHECK_PATH(filename, 1);
975 struct stat sb;
976 CHECK_SYSTEM(statSyscall(filename, &sb, true));
977 return (int64_t)sb.st_gid;
980 Variant HHVM_FUNCTION(fileatime,
981 const String& filename) {
982 CHECK_PATH(filename, 1);
983 struct stat sb;
984 CHECK_SYSTEM(statSyscall(filename, &sb, true));
985 return (int64_t)sb.st_atime;
988 Variant HHVM_FUNCTION(filemtime,
989 const String& filename) {
990 CHECK_PATH(filename, 1);
991 struct stat sb;
992 CHECK_SYSTEM(statSyscall(filename, &sb, true));
993 return (int64_t)sb.st_mtime;
996 Variant HHVM_FUNCTION(filectime,
997 const String& filename) {
998 CHECK_PATH(filename, 1);
999 struct stat sb;
1000 CHECK_SYSTEM(statSyscall(filename, &sb, true));
1001 return (int64_t)sb.st_ctime;
1004 Variant HHVM_FUNCTION(filetype,
1005 const String& filename) {
1006 CHECK_PATH(filename, 1);
1007 struct stat sb;
1008 CHECK_SYSTEM(lstatSyscall(filename, &sb));
1010 switch (sb.st_mode & S_IFMT) {
1011 case S_IFLNK: return "link";
1012 case S_IFIFO: return "fifo";
1013 case S_IFCHR: return "char";
1014 case S_IFDIR: return "dir";
1015 case S_IFBLK: return "block";
1016 case S_IFREG: return "file";
1017 case S_IFSOCK: return "socket";
1019 return "unknown";
1022 Variant HHVM_FUNCTION(linkinfo,
1023 const String& filename) {
1024 CHECK_PATH(filename, 1);
1025 struct stat sb;
1026 CHECK_SYSTEM(statSyscall(filename, &sb));
1027 return (int64_t)sb.st_dev;
1030 bool HHVM_FUNCTION(is_writable,
1031 const String& filename) {
1032 CHECK_PATH_FALSE(filename, 1);
1033 if (filename.empty()) {
1034 return false;
1036 CHECK_SYSTEM_SILENT(accessSyscall(filename, W_OK));
1037 return true;
1039 int mask = S_IWOTH;
1040 if (sb.st_uid == getuid()) {
1041 mask = S_IWUSR;
1042 } else if (sb.st_gid == getgid()) {
1043 mask = S_IWGRP;
1044 } else {
1045 int groups = getgroups(0, NULL);
1046 if (groups > 0) {
1047 gid_t *gids = (gid_t *)malloc(groups * sizeof(gid_t));
1048 int n = getgroups(groups, gids);
1049 for (int i = 0; i < n; i++) {
1050 if (sb.st_gid == gids[i]) {
1051 mask = S_IWGRP;
1052 break;
1055 free(gids);
1058 return sb.st_mode & mask;
1062 bool HHVM_FUNCTION(is_writeable,
1063 const String& filename) {
1064 CHECK_PATH_FALSE(filename, 1);
1065 return HHVM_FN(is_writable)(filename);
1068 bool HHVM_FUNCTION(is_readable,
1069 const String& filename) {
1070 CHECK_PATH_FALSE(filename, 1);
1071 if (filename.empty()) {
1072 return false;
1074 CHECK_SYSTEM_SILENT(accessSyscall(filename, R_OK, true));
1075 return true;
1077 int mask = S_IROTH;
1078 if (sb.st_uid == getuid()) {
1079 mask = S_IRUSR;
1080 } else if (sb.st_gid == getgid()) {
1081 mask = S_IRGRP;
1082 } else {
1083 int groups = getgroups(0, NULL);
1084 if (groups > 0) {
1085 gid_t *gids = (gid_t *)malloc(groups * sizeof(gid_t));
1086 int n = getgroups(groups, gids);
1087 for (int i = 0; i < n; i++) {
1088 if (sb.st_gid == gids[i]) {
1089 mask = S_IRGRP;
1090 break;
1093 free(gids);
1096 return sb.st_mode & mask;
1100 bool HHVM_FUNCTION(is_executable,
1101 const String& filename) {
1102 CHECK_PATH_FALSE(filename, 1);
1103 if (filename.empty()) {
1104 return false;
1106 CHECK_SYSTEM_SILENT(accessSyscall(filename, X_OK));
1107 return true;
1109 int mask = S_IXOTH;
1110 if (sb.st_uid == getuid()) {
1111 mask = S_IXUSR;
1112 } else if (sb.st_gid == getgid()) {
1113 mask = S_IXGRP;
1114 } else {
1115 int groups = getgroups(0, NULL);
1116 if (groups > 0) {
1117 gid_t *gids = (gid_t *)malloc(groups * sizeof(gid_t));
1118 int n = getgroups(groups, gids);
1119 for (int i = 0; i < n; i++) {
1120 if (sb.st_gid == gids[i]) {
1121 mask = S_IXGRP;
1122 break;
1125 free(gids);
1128 return (sb.st_mode & mask) && (sb.st_mode & S_IFMT) != S_IFDIR;
1132 static VFileType lookupVirtualFile(const String& filename) {
1133 if (filename.empty() || !StaticContentCache::TheFileCache) {
1134 return VFileType::NotFound;
1137 String cwd;
1138 std::string root;
1139 bool isRelative = !FileUtil::isAbsolutePath(filename.slice());
1140 if (isRelative) {
1141 cwd = g_context->getCwd();
1142 root = RuntimeOption::SourceRoot;
1143 if (cwd.empty() || FileUtil::isDirSeparator(cwd[cwd.size() - 1])) {
1144 root.pop_back();
1148 if (!isRelative || !root.compare(cwd.data())) {
1149 return StaticContentCache::TheFileCache->getFileType(filename.data());
1152 return VFileType::NotFound;
1155 bool HHVM_FUNCTION(is_file,
1156 const String& filename) {
1157 CHECK_PATH_FALSE(filename, 1);
1158 if (filename.empty()) {
1159 return false;
1161 auto vtype = lookupVirtualFile(filename);
1162 if (vtype != VFileType::NotFound) {
1163 return vtype == VFileType::PlainFile;
1166 struct stat sb;
1167 CHECK_SYSTEM_SILENT(statSyscall(filename, &sb, true));
1168 return (sb.st_mode & S_IFMT) == S_IFREG;
1171 bool HHVM_FUNCTION(is_dir,
1172 const String& filename) {
1173 CHECK_PATH_FALSE(filename, 1);
1174 if (filename.empty()) {
1175 return false;
1177 auto vtype = lookupVirtualFile(filename);
1178 if (vtype != VFileType::NotFound) {
1179 return vtype == VFileType::Directory;
1182 struct stat sb;
1183 CHECK_SYSTEM_SILENT(statSyscall(filename, &sb, false));
1184 return (sb.st_mode & S_IFMT) == S_IFDIR;
1187 bool HHVM_FUNCTION(is_link,
1188 const String& filename) {
1189 CHECK_PATH_FALSE(filename, 1);
1190 struct stat sb;
1191 CHECK_SYSTEM_SILENT(lstatSyscall(filename, &sb));
1192 return (sb.st_mode & S_IFMT) == S_IFLNK;
1195 bool HHVM_FUNCTION(is_uploaded_file,
1196 const String& filename) {
1197 Transport *transport = g_context->getTransport();
1198 if (transport) {
1199 return transport->isUploadedFile(filename);
1201 return false;
1204 bool HHVM_FUNCTION(file_exists,
1205 const String& filename) {
1206 CHECK_PATH_FALSE(filename, 1);
1207 auto vtype = lookupVirtualFile(filename);
1208 if (vtype != VFileType::NotFound) {
1209 return true;
1212 if (filename.empty() ||
1213 (accessSyscall(filename, F_OK, true)) < 0) {
1214 return false;
1216 return true;
1219 Variant HHVM_FUNCTION(stat,
1220 const String& filename) {
1221 CHECK_PATH(filename, 1);
1222 if (filename.empty()) {
1223 return false;
1226 struct stat sb;
1227 if (statSyscall(filename, &sb, true) != 0) {
1228 raise_warning(
1229 "stat(): stat failed for %s",
1230 filename.c_str()
1232 return false;
1234 return stat_impl(&sb);
1237 Variant HHVM_FUNCTION(lstat,
1238 const String& filename) {
1239 CHECK_PATH(filename, 1);
1240 if (filename.empty()) {
1241 return false;
1244 struct stat sb;
1245 CHECK_SYSTEM(lstatSyscall(filename, &sb, true));
1246 return stat_impl(&sb);
1249 void HHVM_FUNCTION(clearstatcache, bool /*clear_realpath_cache*/ /* = false */,
1250 const Variant& /*filename*/ /* = uninit_variant */) {
1251 // we are not having a cache for file stats, so do nothing here
1254 Variant HHVM_FUNCTION(readlink_internal,
1255 const String& path,
1256 bool warning_compliance) {
1257 char buff[PATH_MAX];
1258 int ret = readlink(File::TranslatePath(path).data(), buff, PATH_MAX-1);
1259 if (ret < 0) {
1260 Logger::Verbose("%s/%d: %s", __FUNCTION__, __LINE__,
1261 folly::errnoStr(errno).c_str());
1262 if (warning_compliance) {
1263 raise_warning("readlink(): No such file or directory %s",path.c_str());
1265 return false;
1267 buff[ret] = '\0';
1268 return String(buff, ret, CopyString);
1271 Variant HHVM_FUNCTION(readlink,
1272 const String& path) {
1273 CHECK_PATH(path, 1);
1274 return HHVM_FN(readlink_internal)(path, true);
1277 Variant HHVM_FUNCTION(realpath,
1278 const String& path) {
1279 CHECK_PATH(path, 1);
1281 String translated;
1282 if (path.empty()) {
1283 translated = File::TranslatePath(g_context->getCwd());
1284 } else {
1285 translated = File::TranslatePath(path);
1288 if (translated.empty()) {
1289 return false;
1291 if (StaticContentCache::TheFileCache &&
1292 StaticContentCache::TheFileCache->exists(translated.data(), false)) {
1293 return translated;
1295 // Zend doesn't support streams in realpath
1296 Stream::Wrapper* w = Stream::getWrapperFromURI(path);
1297 if (!w || !w->isNormalFileStream()) {
1298 return false;
1300 if (!dynamic_cast<FileStreamWrapper*>(w)) {
1301 auto str = w->realpath(translated);
1302 if (str.isNull()) return false;
1303 return str;
1305 char resolved_path[PATH_MAX];
1306 if (!realpath(translated.c_str(), resolved_path)) {
1307 return false;
1309 return String(resolved_path, CopyString);
1312 #define PHP_PATHINFO_DIRNAME 1
1313 #define PHP_PATHINFO_BASENAME 2
1314 #define PHP_PATHINFO_EXTENSION 4
1315 #define PHP_PATHINFO_FILENAME 8
1317 const StaticString
1318 s_dirname("dirname"),
1319 s_basename("basename"),
1320 s_extension("extension"),
1321 s_filename("filename");
1323 Variant HHVM_FUNCTION(pathinfo,
1324 const String& path,
1325 int64_t opt /* = 15 */) {
1326 DictInit ret{4};
1328 if (opt == 0) {
1329 return empty_string_variant();
1332 if ((opt & PHP_PATHINFO_DIRNAME) == PHP_PATHINFO_DIRNAME) {
1333 String dirname = HHVM_FN(dirname)(path);
1334 if (opt == PHP_PATHINFO_DIRNAME) {
1335 return dirname;
1337 if (!dirname.equal(staticEmptyString())) {
1338 ret.set(s_dirname, dirname);
1342 String basename = HHVM_FN(basename)(path);
1343 if ((opt & PHP_PATHINFO_BASENAME) == PHP_PATHINFO_BASENAME) {
1344 if (opt == PHP_PATHINFO_BASENAME) {
1345 return basename;
1347 ret.set(s_basename, basename);
1350 if ((opt & PHP_PATHINFO_EXTENSION) == PHP_PATHINFO_EXTENSION) {
1351 int pos = basename.rfind('.');
1352 String extension(empty_string());
1353 if (pos >= 0) {
1354 extension = basename.substr(pos + 1);
1355 ret.set(s_extension, extension);
1357 if (opt == PHP_PATHINFO_EXTENSION) {
1358 return extension;
1362 if ((opt & PHP_PATHINFO_FILENAME) == PHP_PATHINFO_FILENAME) {
1363 int pos = basename.rfind('.');
1364 String filename(empty_string());
1365 if (pos >= 0) {
1366 filename = basename.substr(0, pos);
1367 } else {
1368 filename = basename;
1370 if (opt == PHP_PATHINFO_FILENAME) {
1371 return filename;
1373 ret.set(s_filename, filename);
1376 return ret.toVariant();
1379 Variant HHVM_FUNCTION(disk_free_space,
1380 const String& directory) {
1381 CHECK_PATH(directory, 1);
1382 fs::space_info sb;
1383 String translated = File::TranslatePath(directory);
1384 CHECK_BOOST(sb, fs::space, translated);
1385 return (double)sb.free;
1388 Variant HHVM_FUNCTION(diskfreespace,
1389 const String& directory) {
1390 CHECK_PATH(directory, 1);
1391 return HHVM_FN(disk_free_space)(directory);
1394 Variant HHVM_FUNCTION(disk_total_space,
1395 const String& directory) {
1396 CHECK_PATH(directory, 1);
1397 fs::space_info sb;
1398 String translated = File::TranslatePath(directory);
1399 CHECK_BOOST(sb, fs::space, translated);
1400 return (double)sb.capacity;
1403 ///////////////////////////////////////////////////////////////////////////////
1404 // system wrappers
1406 bool HHVM_FUNCTION(chmod,
1407 const String& filename,
1408 int64_t mode) {
1409 CHECK_PATH_FALSE(filename, 1);
1410 String translated = File::TranslatePath(filename);
1412 // If filename points to a user file, invoke ExtendedWrapper::chmod(..)
1413 Stream::Wrapper* w = Stream::getWrapperFromURI(filename);
1414 auto usw = dynamic_cast<Stream::ExtendedWrapper*>(w);
1415 if (usw != nullptr) {
1416 return usw->chmod(filename, mode);
1419 CHECK_SYSTEM(chmod(translated.c_str(), mode));
1420 return true;
1423 static bool do_chown(const String& filename,
1424 const Variant& user,
1425 bool islChown,
1426 const char* funcName) {
1427 // If filename points to a user file, invoke ExtendedWrapper::chown(..)
1428 Stream::Wrapper* w = Stream::getWrapperFromURI(filename);
1429 auto usw = dynamic_cast<Stream::ExtendedWrapper*>(w);
1430 if (usw != nullptr) {
1431 if (user.isInteger()) {
1432 return usw->chown(filename, user.toInt64());
1433 } else if (user.isString()) {
1434 return usw->chown(filename, user.toString());
1436 raise_warning("%s(): parameter 2 should be string or integer, %s given",
1437 funcName, getDataTypeString(user.getType()).c_str());
1438 return false;
1441 #ifdef WIN32
1442 return false;
1443 #else
1444 int uid;
1445 if (user.isString()) {
1446 String suser = user.toString();
1447 auto buf = PasswdBuffer{};
1448 struct passwd *pw;
1449 if (getpwnam_r(suser.data(), &buf.ent, buf.data.get(), buf.size, &pw)) {
1450 // failed to read user info
1451 return false;
1453 if (!pw) {
1454 Logger::Verbose("%s/%d: Unable to find uid for %s",
1455 __FUNCTION__, __LINE__, suser.data());
1456 raise_warning("%s(): Unable to find uid for %s", funcName,
1457 user.toString().c_str());
1458 return false;
1460 uid = pw->pw_uid;
1461 } else {
1462 uid = user.toInt32();
1465 if (islChown) {
1466 CHECK_SYSTEM(lchown(File::TranslatePath(filename).data(), uid, (gid_t)-1));
1467 } else {
1468 CHECK_SYSTEM(chown(File::TranslatePath(filename).data(), uid, (gid_t)-1));
1470 return true;
1471 #endif
1474 bool HHVM_FUNCTION(chown,
1475 const String& filename,
1476 const Variant& user) {
1477 CHECK_PATH_FALSE(filename, 1);
1478 return do_chown(filename, user, false, "chown");
1481 bool HHVM_FUNCTION(lchown,
1482 const String& filename,
1483 const Variant& user) {
1484 #ifdef WIN32
1485 return false;
1486 #else
1487 CHECK_PATH_FALSE(filename, 1);
1488 return do_chown(filename, user, true, "lchown");
1489 #endif
1492 static bool do_chgrp(const String& filename,
1493 const Variant& group,
1494 bool islChgrp,
1495 const char* funcName) {
1496 // If filename points to a user file, invoke ExtendedWrapper::chgrp(..)
1497 Stream::Wrapper* w = Stream::getWrapperFromURI(filename);
1498 auto usw = dynamic_cast<Stream::ExtendedWrapper*>(w);
1499 if (usw != nullptr) {
1500 if (group.isInteger()) {
1501 return usw->chgrp(filename, group.toInt64());
1502 } else if (group.isString()) {
1503 return usw->chgrp(filename, group.toString());
1505 raise_warning("%s(): parameter 2 should be string or integer, %s given",
1506 funcName, getDataTypeString(group.getType()).c_str());
1507 return false;
1510 #ifdef WIN32
1511 return false;
1512 #else
1513 int gid;
1514 if (group.isString()) {
1515 String sgroup = group.toString();
1516 auto buf = GroupBuffer{};
1517 struct group *gr;
1519 while (true) {
1520 if (getgrnam_r(sgroup.data(), &buf.ent, buf.data.get(), buf.size, &gr)) {
1521 if (errno == ERANGE) {
1522 buf.resize();
1523 continue;
1524 } else if (errno == ENOENT || errno == ESRCH) {
1525 gr = nullptr;
1526 break;
1528 // failed to read group info
1529 return false;
1531 break;
1534 if (!gr) {
1535 Logger::Verbose("%s/%d: Unable to find gid for %s",
1536 __FUNCTION__, __LINE__, sgroup.data());
1537 raise_warning("%s(): Unable to find gid for %s", funcName,
1538 group.toString().c_str());
1539 return false;
1541 gid = gr->gr_gid;
1542 } else {
1543 gid = group.toInt32();
1546 if (islChgrp) {
1547 CHECK_SYSTEM(lchown(File::TranslatePath(filename).data(), (uid_t)-1, gid));
1548 } else {
1549 CHECK_SYSTEM(chown(File::TranslatePath(filename).data(), (uid_t)-1, gid));
1551 return true;
1552 #endif
1555 bool HHVM_FUNCTION(chgrp,
1556 const String& filename,
1557 const Variant& group) {
1558 CHECK_PATH_FALSE(filename, 1);
1559 return do_chgrp(filename, group, false, "chgrp");
1562 bool HHVM_FUNCTION(lchgrp,
1563 const String& filename,
1564 const Variant& group) {
1565 #ifdef WIN32
1566 return false;
1567 #else
1568 CHECK_PATH_FALSE(filename, 1);
1569 return do_chgrp(filename, group, true, "lchgrp");
1570 #endif
1573 bool HHVM_FUNCTION(touch,
1574 const String& filename,
1575 int64_t mtime /* = 0 */,
1576 int64_t atime /* = 0 */) {
1577 CHECK_PATH_FALSE(filename, 1);
1579 if (filename.empty()) {
1580 return false;
1583 // If filename points to a user file, invoke ExtendedWrapper::touch(..)
1584 Stream::Wrapper* w = Stream::getWrapperFromURI(filename);
1585 auto usw = dynamic_cast<Stream::ExtendedWrapper*>(w);
1586 if (usw != nullptr) {
1587 return usw->touch(filename, mtime, atime);
1590 String translated = File::TranslatePath(filename);
1592 /* create the file if it doesn't exist already */
1593 if (accessSyscall(translated, F_OK)) {
1594 FILE *f = fopen(translated.data(), "w");
1595 if (!f) {
1596 raise_warning(
1597 "touch(): Unable to create file %s because %s",
1598 translated.data(), folly::errnoStr(errno).c_str()
1600 return false;
1602 fclose(f);
1605 #ifdef _MSC_VER
1606 CHECK_BOOST_ASSIGN(fs::last_write_time, translated, mtime);
1607 // Windows doesn't have an access time to set.
1608 #else
1609 if (mtime == 0 && atime == 0) {
1610 // It is important to pass nullptr so that the OS sets mtime and atime
1611 // to the current time with maximum precision (more precise then seconds)
1612 CHECK_SYSTEM(utime(translated.data(), nullptr));
1613 } else {
1614 struct utimbuf newtime;
1615 newtime.actime = atime ? atime : mtime;
1616 newtime.modtime = mtime;
1617 CHECK_SYSTEM(utime(translated.data(), &newtime));
1619 #endif
1621 return true;
1624 bool HHVM_FUNCTION(copy,
1625 const String& source,
1626 const String& dest,
1627 const Variant& context /* = null */) {
1628 CHECK_PATH_FALSE(source, 1);
1629 CHECK_PATH_FALSE(dest, 2);
1630 if (!context.isNull() || !File::IsPlainFilePath(source) ||
1631 !File::IsPlainFilePath(dest) || is_cli_server_mode()) {
1632 Variant sfile = HHVM_FN(fopen)(source, "r", false, context);
1633 if (same(sfile, false)) {
1634 return false;
1636 Variant dfile = HHVM_FN(fopen)(dest, "w", false, context);
1637 if (same(dfile, false)) {
1638 return false;
1641 if (!HHVM_FN(stream_copy_to_stream)(sfile.toResource(),
1642 dfile.toResource()).toBoolean() &&
1643 HHVM_FN(filesize)(source).toBoolean()) {
1644 return false;
1647 return HHVM_FN(fclose)(dfile.toResource());
1648 } else {
1649 int ret =
1650 RuntimeOption::UseDirectCopy ?
1651 FileUtil::directCopy(File::TranslatePath(source).data(),
1652 File::TranslatePath(dest).data())
1654 FileUtil::copy(File::TranslatePath(source).data(),
1655 File::TranslatePath(dest).data());
1656 return (ret == 0);
1660 bool HHVM_FUNCTION(rename, const String& oldname, const String& newname,
1661 const Variant& /*context*/ /* = null */) {
1662 CHECK_PATH_FALSE(oldname, 1);
1663 CHECK_PATH_FALSE(newname, 2);
1664 Stream::Wrapper* w = Stream::getWrapperFromURI(oldname);
1665 if (!w) return false;
1666 if (w != Stream::getWrapperFromURI(newname)) {
1667 raise_warning("Can't rename a file on different streams");
1668 return false;
1670 CHECK_SYSTEM(w->rename(oldname, newname));
1671 return true;
1674 int64_t HHVM_FUNCTION(umask,
1675 const Variant& mask /* = uninit_variant */) {
1676 int oldumask = umask(077);
1677 if (mask.isNull()) {
1678 umask(oldumask);
1679 } else {
1680 umask(mask.toInt32());
1682 return oldumask;
1685 bool HHVM_FUNCTION(unlink, const String& filename,
1686 const Variant& /*context*/ /* = null */) {
1687 CHECK_PATH_FALSE(filename, 1);
1688 Stream::Wrapper* w = Stream::getWrapperFromURI(filename);
1689 if (!w) return false;
1690 CHECK_SYSTEM_SILENT(w->unlink(filename));
1691 return true;
1694 bool HHVM_FUNCTION(link,
1695 const String& target,
1696 const String& link) {
1697 CHECK_PATH_FALSE(target, 1);
1698 CHECK_PATH_FALSE(link, 2);
1699 CHECK_BOOST_ASSIGN(fs::create_hard_link, File::TranslatePath(target),
1700 File::TranslatePath(link).toCppString());
1701 return true;
1704 bool HHVM_FUNCTION(symlink,
1705 const String& target,
1706 const String& link) {
1707 CHECK_PATH_FALSE(target, 1);
1708 CHECK_PATH_FALSE(link, 2);
1709 if (HHVM_FN(is_dir)(target)) {
1710 CHECK_BOOST_ASSIGN(fs::create_directory_symlink,
1711 File::TranslatePathKeepRelative(target),
1712 File::TranslatePath(link).toCppString());
1713 } else {
1714 CHECK_BOOST_ASSIGN(fs::create_symlink,
1715 File::TranslatePathKeepRelative(target),
1716 File::TranslatePath(link).toCppString());
1718 return true;
1721 String HHVM_FUNCTION(basename,
1722 const String& path,
1723 const String& suffix /* = null_string */) {
1724 int state = 0;
1725 const char *c = path.data();
1726 const char *comp, *cend;
1727 comp = cend = c;
1728 for (int cnt = path.size(); cnt > 0; --cnt, ++c) {
1729 if (FileUtil::isDirSeparator(*c)) {
1730 if (state == 1) {
1731 state = 0;
1732 cend = c;
1734 } else if (state == 0) {
1735 comp = c;
1736 state = 1;
1740 if (state == 1) {
1741 cend = c;
1743 int sufflen = suffix.size();
1744 if (!suffix.empty() && sufflen < (int)(cend - comp) &&
1745 memcmp(cend - sufflen, suffix.data(), sufflen) == 0) {
1746 cend -= sufflen;
1748 return String(comp, cend - comp, CopyString);
1751 bool HHVM_FUNCTION(fnmatch,
1752 const String& pattern,
1753 const String& filename, int64_t flags /* = 0 */) {
1754 CHECK_PATH_FALSE(pattern, 1);
1755 CHECK_PATH_FALSE(filename, 2);
1756 if (filename.size() >= PATH_MAX) {
1757 raise_warning(
1758 "Filename exceeds the maximum allowed length of %d characters",
1759 PATH_MAX);
1760 return false;
1762 if (pattern.size() >= PATH_MAX) {
1763 raise_warning("Path exceeds the maximum allowed length of %d characters",
1764 PATH_MAX);
1765 return false;
1768 return fnmatch(pattern.data(), filename.data(), flags) == 0;
1771 Variant HHVM_FUNCTION(glob,
1772 const String& pattern,
1773 int64_t flags /* = 0 */) {
1774 CHECK_PATH(pattern, 1);
1775 glob_t globbuf;
1776 int cwd_skip = 0;
1777 memset(&globbuf, 0, sizeof(glob_t));
1778 globbuf.gl_offs = 0;
1779 String work_pattern;
1781 if (pattern.size() >= PATH_MAX) {
1782 raise_warning("Pattern exceeds the maximum allowed length of %d characters",
1783 PATH_MAX);
1784 return false;
1787 if (pattern.charAt(0) == '/') {
1788 work_pattern = pattern;
1789 } else {
1790 String cwd = g_context->getCwd();
1791 if (!cwd.empty() && cwd[cwd.length() - 1] == '/') {
1792 work_pattern = cwd + pattern;
1793 cwd_skip = cwd.length();
1794 } else {
1795 work_pattern = cwd + "/" + pattern;
1796 cwd_skip = cwd.length() + 1;
1799 int nret = glob(work_pattern.data(),
1800 flags & PHP_GLOB_FLAGMASK,
1801 nullptr,
1802 &globbuf);
1803 if (nret == GLOB_NOMATCH) {
1804 globfree(&globbuf);
1805 return empty_vec_array();
1808 if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
1809 if (RID().hasSafeFileAccess()) {
1810 if (!HHVM_FN(is_dir)(work_pattern)) {
1811 globfree(&globbuf);
1812 return false;
1815 globfree(&globbuf);
1816 return empty_vec_array();
1819 if (nret) {
1820 globfree(&globbuf);
1821 return false;
1824 auto ret = Array::CreateVec();
1825 bool basedir_limit = false;
1826 for (int n = 0; n < (int)globbuf.gl_pathc; n++) {
1827 String translated = File::TranslatePath(globbuf.gl_pathv[n]);
1828 if (translated.empty()) {
1829 basedir_limit = true;
1830 continue;
1832 /* we need to do this everytime since GLOB_ONLYDIR does not guarantee that
1833 * all directories will be filtered. GNU libc documentation states the
1834 * following:
1835 * If the information about the type of the file is easily available
1836 * non-directories will be rejected but no extra work will be done to
1837 * determine the information for each file. I.e., the caller must still be
1838 * able to filter directories out.
1840 if ((flags & GLOB_ONLYDIR) && !HHVM_FN(is_dir)(globbuf.gl_pathv[n])) {
1841 continue;
1843 ret.append(String(globbuf.gl_pathv[n] + cwd_skip, CopyString));
1846 globfree(&globbuf);
1848 if (basedir_limit && ret.empty()) {
1849 return false;
1851 // php's glob always produces an array, but Variant::Variant(CArrRef)
1852 // will produce KindOfNull if given a req::ptr wrapped around null.
1853 if (ret.isNull()) {
1854 return empty_vec_array();
1856 return ret;
1859 Variant HHVM_FUNCTION(tempnam,
1860 const String& dir,
1861 const String& prefix) {
1862 CHECK_PATH(dir, 1);
1863 CHECK_PATH(prefix, 2);
1864 String tmpdir = dir, trailing_slash = "/";
1865 if (tmpdir.empty() || !HHVM_FN(is_dir)(tmpdir) ||
1866 !HHVM_FN(is_writable)(tmpdir)) {
1867 tmpdir = HHVM_FN(sys_get_temp_dir)();
1869 tmpdir = File::TranslatePath(tmpdir);
1870 String pbase = HHVM_FN(basename)(prefix);
1871 if (pbase.size() > 64) pbase = pbase.substr(0, 63);
1872 if ((tmpdir.length() > 0) && (tmpdir[tmpdir.length() - 1] == '/')) {
1873 trailing_slash = "";
1875 String templ = tmpdir + trailing_slash + pbase + "XXXXXX";
1876 auto buf = templ.get()->mutableData();
1877 if (UNLIKELY(is_cli_server_mode())) {
1878 if (!cli_mkstemp(buf)) return false;
1879 } else {
1880 int fd = mkstemp(buf);
1881 if (fd < 0) {
1882 Logger::Verbose("%s/%d: %s", __FUNCTION__, __LINE__,
1883 folly::errnoStr(errno).c_str());
1884 return false;
1887 close(fd);
1889 return templ.setSize(strlen(buf));
1892 Variant HHVM_FUNCTION(tmpfile) {
1893 auto file = req::make<TempFile>(true);
1894 if (!file->valid()) {
1895 return false;
1897 return Variant(file);
1900 ///////////////////////////////////////////////////////////////////////////////
1901 // directory functions
1903 bool HHVM_FUNCTION(mkdir, const String& pathname, int64_t mode /* = 0777 */,
1904 bool recursive /* = false */,
1905 const Variant& /*context*/ /* = null */) {
1906 CHECK_PATH_FALSE(pathname, 1);
1907 Stream::Wrapper* w = Stream::getWrapperFromURI(pathname);
1908 if (!w) return false;
1909 int options = recursive ? k_STREAM_MKDIR_RECURSIVE : 0;
1910 CHECK_SYSTEM_SILENT(w->mkdir(pathname, mode, options));
1911 return true;
1914 bool HHVM_FUNCTION(rmdir, const String& dirname,
1915 const Variant& /*context*/ /* = null */) {
1916 CHECK_PATH_FALSE(dirname, 1);
1917 Stream::Wrapper* w = Stream::getWrapperFromURI(dirname);
1918 if (!w) return false;
1919 int options = 0;
1920 CHECK_SYSTEM_SILENT(w->rmdir(dirname, options));
1921 return true;
1924 String HHVM_FUNCTION(dirname,
1925 const String& path) {
1926 return FileUtil::dirname(path);
1929 Variant HHVM_FUNCTION(getcwd) {
1930 return g_context->getCwd();
1933 bool HHVM_FUNCTION(chdir,
1934 const String& directory) {
1935 CHECK_PATH_FALSE(directory, 1);
1936 if (!HHVM_FN(is_dir)(directory)) {
1937 raise_warning("chdir(): No such file or directory (errno 2)");
1938 return false;
1941 #if ENABLE_EXTENSION_POSIX
1942 if (!HHVM_FN(posix_access)(directory, X_OK)) {
1943 raise_warning("chdir(): Permission denied (errno 13)");
1944 return false;
1946 #endif
1948 g_context->setCwd(HHVM_FN(realpath)(directory).toString());
1949 return true;
1952 #ifndef WIN32
1953 bool HHVM_FUNCTION(chroot,
1954 const String& directory) {
1955 CHECK_PATH_FALSE(directory, 1);
1956 CHECK_SYSTEM(chroot(File::TranslatePath(directory).data()));
1957 CHECK_SYSTEM(chdir("/"));
1958 return true;
1960 #endif
1962 ///////////////////////////////////////////////////////////////////////////////
1965 * A stack maintains the states of nested structures.
1968 struct DirectoryData final : RequestEventHandler {
1969 void requestInit() override {
1970 assertx(!defaultDirectory);
1972 void requestShutdown() override {
1973 defaultDirectory = nullptr;
1975 req::ptr<Directory> defaultDirectory;
1978 namespace {
1980 IMPLEMENT_STATIC_REQUEST_LOCAL(DirectoryData, s_directory_data);
1982 const StaticString
1983 s_handle("handle"),
1984 s_path("path");
1986 req::ptr<Directory> get_dir(const Resource& dir_handle) {
1987 if (dir_handle.isNull()) {
1988 auto defaultDir = s_directory_data->defaultDirectory;
1989 if (!defaultDir) {
1990 raise_warning("no Directory resource supplied");
1991 return nullptr;
1993 return defaultDir;
1996 auto d = dyn_cast_or_null<Directory>(dir_handle);
1997 if (!d) {
1998 raise_warning("Not a valid directory resource");
1999 return nullptr;
2001 return d;
2004 bool StringDescending(const String& s1, const String& s2) {
2005 return s1.more(s2);
2008 bool StringAscending(const String& s1, const String& s2) {
2009 return s1.less(s2);
2014 Variant HHVM_FUNCTION(dir,
2015 const String& directory) {
2016 CHECK_PATH(directory, 1);
2017 Variant dir = HHVM_FN(opendir)(directory);
2018 if (same(dir, false)) {
2019 return false;
2021 auto d = SystemLib::AllocDirectoryObject();
2022 d->setProp(nullptr, s_path.get(), directory.asTypedValue());
2023 d->setProp(nullptr, s_handle.get(), *dir.asTypedValue());
2024 return d;
2027 Variant HHVM_FUNCTION(opendir, const String& path,
2028 const Variant& /*context*/ /* = null */) {
2029 CHECK_PATH(path, 1);
2030 Stream::Wrapper* w = Stream::getWrapperFromURI(path);
2031 if (!w) return false;
2032 auto p = w->opendir(path);
2033 if (!p) {
2034 return false;
2036 s_directory_data->defaultDirectory = p;
2037 return Variant(p);
2040 Variant HHVM_FUNCTION(readdir,
2041 const Variant& dir_handle /* = null */) {
2042 const Resource& res_dir_handle = dir_handle.isNull()
2043 ? null_resource
2044 : dir_handle.toResource();
2045 auto dir = get_dir(res_dir_handle);
2046 if (!dir) {
2047 return false;
2049 return dir->read();
2052 void HHVM_FUNCTION(rewinddir,
2053 const Variant& dir_handle /* = null */) {
2054 const Resource& res_dir_handle = dir_handle.isNull()
2055 ? null_resource
2056 : dir_handle.toResource();
2057 auto dir = get_dir(res_dir_handle);
2058 if (!dir) {
2059 return;
2061 dir->rewind();
2064 Variant
2065 HHVM_FUNCTION(scandir, const String& directory, bool descending /* = false */,
2066 const Variant& /*context*/ /* = null */) {
2067 if (directory.empty()) {
2068 raise_warning("scandir(): Directory name cannot be empty");
2069 return false;
2072 CHECK_PATH(directory, 1);
2073 Stream::Wrapper* w = Stream::getWrapperFromURI(directory);
2074 if (!w) return false;
2075 auto dir = w->opendir(directory);
2076 if (!dir) {
2077 return false;
2080 std::vector<String> names;
2081 while (true) {
2082 auto name = dir->read();
2083 if (same(name, false)) {
2084 break;
2086 names.push_back(name.toString());
2088 dir->close();
2090 sort(
2091 names.begin(),
2092 names.end(),
2093 descending ? StringDescending : StringAscending
2096 VecInit ret{names.size()};
2097 for (auto& name : names) {
2098 ret.append(name);
2100 return ret.toVariant();
2103 void HHVM_FUNCTION(closedir,
2104 const Variant& dir_handle /* = null */) {
2105 const Resource& res_dir_handle = dir_handle.isNull()
2106 ? null_resource
2107 : dir_handle.toResource();
2108 auto d = get_dir(res_dir_handle);
2109 if (!d) {
2110 return;
2112 if (s_directory_data->defaultDirectory == d) {
2113 s_directory_data->defaultDirectory = nullptr;
2115 d->close();
2118 ///////////////////////////////////////////////////////////////////////////////
2120 const StaticString
2121 s_STDIN("STDIN"),
2122 s_STDOUT("STDOUT"),
2123 s_STDERR("STDERR");
2125 void StandardExtension::initFile() {
2126 HHVM_RC_STR(DIRECTORY_SEPARATOR, s_DIRECTORY_SEPARATOR);
2127 HHVM_RC_STR(PATH_SEPARATOR, s_PATH_SEPARATOR);
2129 HHVM_RC_INT(FILE_USE_INCLUDE_PATH, PHP_FILE_USE_INCLUDE_PATH);
2130 HHVM_RC_INT(FILE_IGNORE_NEW_LINES, PHP_FILE_IGNORE_NEW_LINES);
2131 HHVM_RC_INT(FILE_SKIP_EMPTY_LINES, PHP_FILE_SKIP_EMPTY_LINES);
2132 HHVM_RC_INT(FILE_APPEND, PHP_FILE_APPEND);
2133 HHVM_RC_INT(FILE_NO_DEFAULT_CONTEXT, PHP_FILE_NO_DEFAULT_CONTEXT);
2134 HHVM_RC_INT(FILE_TEXT, 0);
2135 HHVM_RC_INT(FILE_BINARY, 0);
2136 HHVM_RC_INT_SAME(FNM_NOESCAPE);
2137 HHVM_RC_INT_SAME(FNM_CASEFOLD);
2138 HHVM_RC_INT_SAME(FNM_PERIOD);
2139 HHVM_RC_INT_SAME(FNM_PATHNAME);
2140 HHVM_RC_INT(GLOB_AVAILABLE_FLAGS, PHP_GLOB_FLAGS);
2141 HHVM_RC_INT_SAME(GLOB_BRACE);
2142 HHVM_RC_INT_SAME(GLOB_ERR);
2143 HHVM_RC_INT_SAME(GLOB_MARK);
2144 HHVM_RC_INT_SAME(GLOB_NOCHECK);
2145 HHVM_RC_INT_SAME(GLOB_NOESCAPE);
2146 HHVM_RC_INT_SAME(GLOB_NOSORT);
2147 HHVM_RC_INT_SAME(GLOB_ONLYDIR);
2148 HHVM_RC_INT(LOCK_SH, k_LOCK_SH);
2149 HHVM_RC_INT(LOCK_EX, k_LOCK_EX);
2150 HHVM_RC_INT(LOCK_UN, k_LOCK_UN);
2151 HHVM_RC_INT(LOCK_NB, k_LOCK_NB);
2153 HHVM_RC_INT(SCANDIR_SORT_ASCENDING, k_SCANDIR_SORT_ASCENDING);
2154 HHVM_RC_INT(SCANDIR_SORT_DESCENDING, k_SCANDIR_SORT_DESCENDING);
2155 HHVM_RC_INT(SCANDIR_SORT_NONE, k_SCANDIR_SORT_NONE);
2156 HHVM_RC_INT_SAME(SEEK_SET);
2157 HHVM_RC_INT_SAME(SEEK_CUR);
2158 HHVM_RC_INT_SAME(SEEK_END);
2160 Native::registerConstant(s_STDIN.get(), BuiltinFiles::getSTDIN);
2161 Native::registerConstant(s_STDOUT.get(), BuiltinFiles::getSTDOUT);
2162 Native::registerConstant(s_STDERR.get(), BuiltinFiles::getSTDERR);
2164 HHVM_RC_INT(INI_SCANNER_NORMAL, k_INI_SCANNER_NORMAL);
2165 HHVM_RC_INT(INI_SCANNER_RAW, k_INI_SCANNER_RAW);
2167 HHVM_RC_INT(PATHINFO_BASENAME, PHP_PATHINFO_BASENAME);
2168 HHVM_RC_INT(PATHINFO_DIRNAME, PHP_PATHINFO_DIRNAME);
2169 HHVM_RC_INT(PATHINFO_EXTENSION, PHP_PATHINFO_EXTENSION);
2170 HHVM_RC_INT(PATHINFO_FILENAME, PHP_PATHINFO_FILENAME);
2172 HHVM_FE(fopen);
2173 HHVM_FE(popen);
2174 HHVM_FE(fclose);
2175 HHVM_FE(pclose);
2176 HHVM_FE(fseek);
2177 HHVM_FE(rewind);
2178 HHVM_FE(ftell);
2179 HHVM_FE(feof);
2180 HHVM_FE(fstat);
2181 HHVM_FE(fread);
2182 HHVM_FE(fgetc);
2183 HHVM_FE(fgets);
2184 HHVM_FE(fgetss);
2185 HHVM_FE(fscanf);
2186 HHVM_FE(fpassthru);
2187 HHVM_FE(fwrite);
2188 HHVM_FE(fputs);
2189 HHVM_FE(fprintf);
2190 HHVM_FE(vfprintf);
2191 HHVM_FE(fflush);
2192 HHVM_FE(ftruncate);
2193 HHVM_FE(flock);
2194 HHVM_FE(fputcsv);
2195 HHVM_FE(fgetcsv);
2196 HHVM_FE(file_get_contents);
2197 HHVM_FE(file_put_contents);
2198 HHVM_FE(file);
2199 HHVM_FE(readfile);
2200 HHVM_FE(move_uploaded_file);
2201 HHVM_FE(parse_ini_file);
2202 HHVM_FE(parse_ini_string);
2203 HHVM_FE(md5_file);
2204 HHVM_FE(sha1_file);
2205 HHVM_FE(chmod);
2206 HHVM_FE(chown);
2207 HHVM_FE(lchown);
2208 HHVM_FE(chgrp);
2209 HHVM_FE(lchgrp);
2210 HHVM_FE(touch);
2211 HHVM_FE(copy);
2212 HHVM_FE(rename);
2213 HHVM_FE(umask);
2214 HHVM_FE(unlink);
2215 HHVM_FE(link);
2216 HHVM_FE(symlink);
2217 HHVM_FE(basename);
2218 HHVM_FE(fnmatch);
2219 HHVM_FE(glob);
2220 HHVM_FE(tempnam);
2221 HHVM_FE(tmpfile);
2222 HHVM_FE(fileperms);
2223 HHVM_FE(fileinode);
2224 HHVM_FE(filesize);
2225 HHVM_FE(fileowner);
2226 HHVM_FE(filegroup);
2227 HHVM_FE(fileatime);
2228 HHVM_FE(filemtime);
2229 HHVM_FE(filectime);
2230 HHVM_FE(filetype);
2231 HHVM_FE(linkinfo);
2232 HHVM_FE(is_writable);
2233 HHVM_FE(is_writeable);
2234 HHVM_FE(is_readable);
2235 HHVM_FE(is_executable);
2236 HHVM_FE(is_file);
2237 HHVM_FE(is_dir);
2238 HHVM_FE(is_link);
2239 HHVM_FE(is_uploaded_file);
2240 HHVM_FE(file_exists);
2241 HHVM_FE(stat);
2242 HHVM_FE(lstat);
2243 HHVM_FE(clearstatcache);
2244 HHVM_FE(readlink_internal);
2245 HHVM_FE(readlink);
2246 HHVM_FE(realpath);
2247 HHVM_FE(pathinfo);
2248 HHVM_FE(disk_free_space);
2249 HHVM_FE(diskfreespace);
2250 HHVM_FE(disk_total_space);
2251 HHVM_FE(mkdir);
2252 HHVM_FE(rmdir);
2253 HHVM_FE(dirname);
2254 HHVM_FE(getcwd);
2255 HHVM_FE(chdir);
2256 #ifndef WIN32
2257 HHVM_FE(chroot);
2258 #endif
2259 HHVM_FE(dir);
2260 HHVM_FE(opendir);
2261 HHVM_FE(readdir);
2262 HHVM_FE(rewinddir);
2263 HHVM_FE(scandir);
2264 HHVM_FE(closedir);
2266 loadSystemlib("std_file");
2269 ///////////////////////////////////////////////////////////////////////////////