2 +----------------------------------------------------------------------+
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"
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>
62 #if defined(__FreeBSD__) || defined(__APPLE__)
63 # include <sys/mount.h>
73 #include <boost/algorithm/string/predicate.hpp>
74 #include <boost/filesystem.hpp>
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"); \
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())) { \
94 "%s() expects parameter %d to be a valid path, string given", \
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) \
110 folly::errnoStr(errno).c_str() \
115 #define CHECK_SYSTEM_SILENT(exp) \
117 Logger::Verbose("%s/%d: %s", __FUNCTION__, __LINE__, \
118 folly::errnoStr(errno).c_str()); \
122 #define CHECK_BOOST(dest, func, path) \
123 boost::system::error_code ec; \
124 dest = func(path.toCppString(), ec); \
129 ec.message().c_str() \
134 #define CHECK_BOOST_SILENT(dest, func, path) \
135 boost::system::error_code ec; \
136 dest = func(path.toCppString(), ec); \
138 Logger::Verbose("%s/%d: %s", __FUNCTION__, __LINE__, \
139 ec.message().c_str() \
144 #define CHECK_BOOST_ASSIGN(func, path, arg) \
145 boost::system::error_code ec; \
146 func(path.toCppString(), arg, ec); \
151 ec.message().c_str() \
156 // libxml/xpathInternals.h defines CHECK_ERROR,
157 // we need to undef it first
161 #define CHECK_ERROR(ret) \
162 check_error(__FUNCTION__, __LINE__, (ret))
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 ///////////////////////////////////////////////////////////////////////////////
172 static bool check_error(const char *function
, int line
, bool ret
) {
174 Logger::Verbose("%s/%d: %s", function
, line
,
175 folly::errnoStr(errno
).c_str());
180 static int accessSyscall(
181 const String
& uri_or_path
,
183 bool useFileCache
= false) {
184 Stream::Wrapper
* w
= Stream::getWrapperFromURI(uri_or_path
);
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
);
195 return ::access(File::TranslatePathWithFileCache(path
).data(), mode
);
197 return w
->access(uri_or_path
, mode
);
200 static int statSyscall(
203 bool useFileCache
= false) {
204 bool isRelative
= !FileUtil::isAbsolutePath(path
.slice());
206 Stream::Wrapper
* w
= Stream::getWrapperFromURI(path
, &pathIndex
);
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()) {
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(
243 bool useFileCache
= false) {
244 Stream::Wrapper
* w
= Stream::getWrapperFromURI(path
);
246 if (useFileCache
&& dynamic_cast<FileStreamWrapper
*>(w
)) {
247 return ::lstat(File::TranslatePathWithFileCache(path
).data(), buf
);
249 return w
->lstat(path
, buf
);
264 s_blksize("blksize"),
267 Array
stat_impl(struct stat
*stat_sb
) {
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
);
281 ret
.append((int64_t)stat_sb
->st_blksize
);
282 ret
.append((int64_t)stat_sb
->st_blocks
);
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
);
296 ret
.set(s_blksize
, (int64_t)stat_sb
->st_blksize
);
297 ret
.set(s_blocks
, (int64_t)stat_sb
->st_blocks
);
299 return ret
.toArray();
302 ///////////////////////////////////////////////////////////////////////////////
304 Variant
HHVM_FUNCTION(fopen
,
305 const String
& filename
,
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");
314 if (filename
.empty()) {
315 raise_warning("Filename cannot be empty");
319 auto file
= File::Open(filename
, mode
,
320 use_include_path
? File::USE_INCLUDE_PATH
: 0,
321 cast_or_null
<StreamContext
>(context
));
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
));
335 raise_warning("popen(%s,%s): Invalid argument",
336 command
.data(), mode
.data());
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());
355 Variant
HHVM_FUNCTION(fseek
,
356 const Resource
& handle
,
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)) {
379 bool HHVM_FUNCTION(feof
,
380 const Resource
& handle
) {
381 CHECK_HANDLE(handle
, f
);
385 Variant
HHVM_FUNCTION(fstat
,
386 const Resource
& handle
) {
387 CHECK_HANDLE(handle
, f
);
389 if (!CHECK_ERROR(f
->stat(&sb
)))
391 return stat_impl(&sb
);
394 Variant
HHVM_FUNCTION(fread
,
395 const Resource
& handle
,
397 CHECK_HANDLE(handle
, f
);
400 "fread(): Length parameter must be greater than 0"
404 return f
->read(length
);
407 Variant
HHVM_FUNCTION(fgetc
,
408 const Resource
& handle
) {
409 CHECK_HANDLE(handle
, f
);
410 int result
= f
->getc();
414 return String::FromChar(result
);
417 Variant
HHVM_FUNCTION(fgets
,
418 const Resource
& handle
,
419 int64_t length
/* = 0 */) {
421 raise_invalid_argument_warning("length (negative): %" PRId64
, length
);
424 CHECK_HANDLE(handle
, f
);
425 String line
= f
->readLine(length
- 1);
426 if (!line
.isNull()) {
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
);
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) {
451 return HHVM_FN(sscanf
)(line
, format
);
454 Variant
HHVM_FUNCTION(fpassthru
,
455 const Resource
& handle
) {
456 CHECK_HANDLE(handle
, f
);
460 Variant
HHVM_FUNCTION(fwrite
,
461 const Resource
& handle
,
463 int64_t length
/* = 0 */) {
464 CHECK_HANDLE(handle
, f
);
465 int64_t ret
= f
->write(data
, length
);
467 raise_notice("fwrite(): send of %ld bytes failed with errno=%d %s",
468 data
.size(), errno
, folly::errnoStr(errno
).c_str());
474 Variant
HHVM_FUNCTION(fputs
,
475 const Resource
& handle
,
477 int64_t length
/* = 0 */) {
478 CHECK_HANDLE(handle
, f
);
479 int64_t ret
= f
->write(data
, length
);
480 if (ret
< 0) ret
= 0;
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());
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
,
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
,
524 CHECK_HANDLE(handle
, f
);
529 if (act
< 1 || act
> 3) {
530 raise_invalid_argument_warning("operation: %ld", operation
);
533 act
= flock_values
[act
- 1] | (operation
& 4 ? LOCK_NB
: 0);
534 bool ret
= f
->lock(act
, wouldblock
);
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()); \
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
,
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
/* = "\\" */) {
569 raise_invalid_argument_warning("Length parameter may not be negative");
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
);
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
,
602 int64_t flags
/* = 0 */,
603 const Variant
& context
/* = null */) {
604 CHECK_PATH(filename
, 1);
607 if (flags
& PHP_FILE_APPEND
) {
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
617 auto file
= File::Open(
621 dyn_cast_or_null
<StreamContext
>(context
)
628 if (flags
& LOCK_EX
) {
629 // Check to make sure we are dealing with a regular file
630 if (!isa
<PlainFile
>(file
)) {
632 "%s(): Exclusive locks may only be set for regular files",
637 if (!file
->lock(LOCK_EX
)) {
642 if (mode
[0] == 'c') {
648 switch (data
.getType()) {
649 case KindOfResource
: {
650 auto fsrc
= dyn_cast_or_null
<File
>(data
);
652 raise_warning("Not a valid stream resource");
657 int len
= fsrc
->readImpl(buffer
, sizeof(buffer
));
660 int written
= file
->writeImpl(buffer
, len
);
661 if (written
!= len
) {
669 case KindOfPersistentVec
:
671 case KindOfPersistentDict
:
673 case KindOfPersistentKeyset
:
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()) {
692 if (!data
.getObjectData()->hasToString()) {
693 raise_warning("Not a valid stream resource");
702 case KindOfPersistentString
:
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()) {
719 raise_warning("Not a valid stream resource");
723 // like fwrite(), fclose() can error when fflush()ing
724 if (numbytes
< 0 || !file
->close()) {
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
,
739 if (same(contents
, 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());
759 if (include_new_line
) {
763 ret
.append(String(s
, p
-s
, CopyString
));
765 } while ((p
= (const char *)memchr(p
, eol_marker
, (e
-p
))));
769 if (p
!= content
.data() && eol_marker
== '\n' && *(p
- 1) == '\r') {
773 if (skip_blank_lines
&& !(p
-s
-windows_eol
)) {
777 ret
.append(String(s
, p
-s
-windows_eol
, CopyString
));
779 } while ((p
= (const char *)memchr(p
, eol_marker
, (e
-p
))));
782 /* handle any left overs of files without new lines */
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());
801 Variant ret
= HHVM_FN(fpassthru
)(f
.toResource());
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
)) {
813 CHECK_PATH_FALSE(destination
, 2);
815 if (HHVM_FN(rename
)(filename
, destination
)) {
819 // If rename didn't work, fall back to copy followed by unlink
820 if (!HHVM_FN(copy
)(filename
, destination
)) {
823 HHVM_FN(unlink
)(filename
);
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
)) {
838 if (FileUtil::isAbsolutePath(filename
.data())) {
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());
846 int npos
= cfd
.rfind('/');
848 resolved
= cfd
.substr(0, npos
+ 1) + filename
;
849 if (!resolved
.empty() && HHVM_FN(file_exists
)(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
)) {
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!");
879 // Block ability to load ini files via http (eg., remotely)
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");
887 // Extract the (local) filename
888 String wrapperPrefix
= filename
.substr(0, oFilename
);
889 String path
= resolve_parse_ini_filename(filename
.substr(oFilename
));
892 // At this point, we were unable to find the file.
893 raise_warning("No such file or directory");
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.
903 return IniSetting::FromString(content
.toString(), filename
, process_sections
,
907 Variant
HHVM_FUNCTION(parse_ini_string
,
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 ///////////////////////////////////////////////////////////////////////////////
931 Variant
HHVM_FUNCTION(fileperms
,
932 const String
& filename
) {
933 CHECK_PATH(filename
, 1);
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);
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()) {
953 if (StaticContentCache::TheFileCache
) {
955 StaticContentCache::TheFileCache
->fileSize(filename
.data(),
956 filename
.data()[0] != '/');
957 if (size
>= 0) return size
;
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);
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);
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);
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);
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);
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);
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";
1022 Variant
HHVM_FUNCTION(linkinfo
,
1023 const String
& filename
) {
1024 CHECK_PATH(filename
, 1);
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()) {
1036 CHECK_SYSTEM_SILENT(accessSyscall(filename
, W_OK
));
1040 if (sb.st_uid == getuid()) {
1042 } else if (sb.st_gid == getgid()) {
1045 int groups = getgroups(0, NULL);
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]) {
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()) {
1074 CHECK_SYSTEM_SILENT(accessSyscall(filename
, R_OK
, true));
1078 if (sb.st_uid == getuid()) {
1080 } else if (sb.st_gid == getgid()) {
1083 int groups = getgroups(0, NULL);
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]) {
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()) {
1106 CHECK_SYSTEM_SILENT(accessSyscall(filename
, X_OK
));
1110 if (sb.st_uid == getuid()) {
1112 } else if (sb.st_gid == getgid()) {
1115 int groups = getgroups(0, NULL);
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]) {
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
;
1139 bool isRelative
= !FileUtil::isAbsolutePath(filename
.slice());
1141 cwd
= g_context
->getCwd();
1142 root
= RuntimeOption::SourceRoot
;
1143 if (cwd
.empty() || FileUtil::isDirSeparator(cwd
[cwd
.size() - 1])) {
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()) {
1161 auto vtype
= lookupVirtualFile(filename
);
1162 if (vtype
!= VFileType::NotFound
) {
1163 return vtype
== VFileType::PlainFile
;
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()) {
1177 auto vtype
= lookupVirtualFile(filename
);
1178 if (vtype
!= VFileType::NotFound
) {
1179 return vtype
== VFileType::Directory
;
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);
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();
1199 return transport
->isUploadedFile(filename
);
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
) {
1212 if (filename
.empty() ||
1213 (accessSyscall(filename
, F_OK
, true)) < 0) {
1219 Variant
HHVM_FUNCTION(stat
,
1220 const String
& filename
) {
1221 CHECK_PATH(filename
, 1);
1222 if (filename
.empty()) {
1227 if (statSyscall(filename
, &sb
, true) != 0) {
1229 "stat(): stat failed for %s",
1234 return stat_impl(&sb
);
1237 Variant
HHVM_FUNCTION(lstat
,
1238 const String
& filename
) {
1239 CHECK_PATH(filename
, 1);
1240 if (filename
.empty()) {
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
,
1256 bool warning_compliance
) {
1257 char buff
[PATH_MAX
];
1258 int ret
= readlink(File::TranslatePath(path
).data(), buff
, PATH_MAX
-1);
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());
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);
1283 translated
= File::TranslatePath(g_context
->getCwd());
1285 translated
= File::TranslatePath(path
);
1288 if (translated
.empty()) {
1291 if (StaticContentCache::TheFileCache
&&
1292 StaticContentCache::TheFileCache
->exists(translated
.data(), false)) {
1295 // Zend doesn't support streams in realpath
1296 Stream::Wrapper
* w
= Stream::getWrapperFromURI(path
);
1297 if (!w
|| !w
->isNormalFileStream()) {
1300 if (!dynamic_cast<FileStreamWrapper
*>(w
)) {
1301 auto str
= w
->realpath(translated
);
1302 if (str
.isNull()) return false;
1305 char resolved_path
[PATH_MAX
];
1306 if (!realpath(translated
.c_str(), resolved_path
)) {
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
1318 s_dirname("dirname"),
1319 s_basename("basename"),
1320 s_extension("extension"),
1321 s_filename("filename");
1323 Variant
HHVM_FUNCTION(pathinfo
,
1325 int64_t opt
/* = 15 */) {
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
) {
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
) {
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());
1354 extension
= basename
.substr(pos
+ 1);
1355 ret
.set(s_extension
, extension
);
1357 if (opt
== PHP_PATHINFO_EXTENSION
) {
1362 if ((opt
& PHP_PATHINFO_FILENAME
) == PHP_PATHINFO_FILENAME
) {
1363 int pos
= basename
.rfind('.');
1364 String
filename(empty_string());
1366 filename
= basename
.substr(0, pos
);
1368 filename
= basename
;
1370 if (opt
== PHP_PATHINFO_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);
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);
1398 String translated
= File::TranslatePath(directory
);
1399 CHECK_BOOST(sb
, fs::space
, translated
);
1400 return (double)sb
.capacity
;
1403 ///////////////////////////////////////////////////////////////////////////////
1406 bool HHVM_FUNCTION(chmod
,
1407 const String
& filename
,
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
));
1423 static bool do_chown(const String
& filename
,
1424 const Variant
& user
,
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());
1445 if (user
.isString()) {
1446 String suser
= user
.toString();
1447 auto buf
= PasswdBuffer
{};
1449 if (getpwnam_r(suser
.data(), &buf
.ent
, buf
.data
.get(), buf
.size
, &pw
)) {
1450 // failed to read user info
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());
1462 uid
= user
.toInt32();
1466 CHECK_SYSTEM(lchown(File::TranslatePath(filename
).data(), uid
, (gid_t
)-1));
1468 CHECK_SYSTEM(chown(File::TranslatePath(filename
).data(), uid
, (gid_t
)-1));
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
) {
1487 CHECK_PATH_FALSE(filename
, 1);
1488 return do_chown(filename
, user
, true, "lchown");
1492 static bool do_chgrp(const String
& filename
,
1493 const Variant
& group
,
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());
1514 if (group
.isString()) {
1515 String sgroup
= group
.toString();
1516 auto buf
= GroupBuffer
{};
1520 if (getgrnam_r(sgroup
.data(), &buf
.ent
, buf
.data
.get(), buf
.size
, &gr
)) {
1521 if (errno
== ERANGE
) {
1524 } else if (errno
== ENOENT
|| errno
== ESRCH
) {
1528 // failed to read group info
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());
1543 gid
= group
.toInt32();
1547 CHECK_SYSTEM(lchown(File::TranslatePath(filename
).data(), (uid_t
)-1, gid
));
1549 CHECK_SYSTEM(chown(File::TranslatePath(filename
).data(), (uid_t
)-1, gid
));
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
) {
1568 CHECK_PATH_FALSE(filename
, 1);
1569 return do_chgrp(filename
, group
, true, "lchgrp");
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()) {
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");
1597 "touch(): Unable to create file %s because %s",
1598 translated
.data(), folly::errnoStr(errno
).c_str()
1606 CHECK_BOOST_ASSIGN(fs::last_write_time
, translated
, mtime
);
1607 // Windows doesn't have an access time to set.
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));
1614 struct utimbuf newtime
;
1615 newtime
.actime
= atime
? atime
: mtime
;
1616 newtime
.modtime
= mtime
;
1617 CHECK_SYSTEM(utime(translated
.data(), &newtime
));
1624 bool HHVM_FUNCTION(copy
,
1625 const String
& source
,
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)) {
1636 Variant dfile
= HHVM_FN(fopen
)(dest
, "w", false, context
);
1637 if (same(dfile
, false)) {
1641 if (!HHVM_FN(stream_copy_to_stream
)(sfile
.toResource(),
1642 dfile
.toResource()).toBoolean() &&
1643 HHVM_FN(filesize
)(source
).toBoolean()) {
1647 return HHVM_FN(fclose
)(dfile
.toResource());
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());
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");
1670 CHECK_SYSTEM(w
->rename(oldname
, newname
));
1674 int64_t HHVM_FUNCTION(umask
,
1675 const Variant
& mask
/* = uninit_variant */) {
1676 int oldumask
= umask(077);
1677 if (mask
.isNull()) {
1680 umask(mask
.toInt32());
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
));
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());
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());
1714 CHECK_BOOST_ASSIGN(fs::create_symlink
,
1715 File::TranslatePathKeepRelative(target
),
1716 File::TranslatePath(link
).toCppString());
1721 String
HHVM_FUNCTION(basename
,
1723 const String
& suffix
/* = null_string */) {
1725 const char *c
= path
.data();
1726 const char *comp
, *cend
;
1728 for (int cnt
= path
.size(); cnt
> 0; --cnt
, ++c
) {
1729 if (FileUtil::isDirSeparator(*c
)) {
1734 } else if (state
== 0) {
1743 int sufflen
= suffix
.size();
1744 if (!suffix
.empty() && sufflen
< (int)(cend
- comp
) &&
1745 memcmp(cend
- sufflen
, suffix
.data(), sufflen
) == 0) {
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
) {
1758 "Filename exceeds the maximum allowed length of %d characters",
1762 if (pattern
.size() >= PATH_MAX
) {
1763 raise_warning("Path exceeds the maximum allowed length of %d characters",
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);
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",
1787 if (pattern
.charAt(0) == '/') {
1788 work_pattern
= pattern
;
1790 String cwd
= g_context
->getCwd();
1791 if (!cwd
.empty() && cwd
[cwd
.length() - 1] == '/') {
1792 work_pattern
= cwd
+ pattern
;
1793 cwd_skip
= cwd
.length();
1795 work_pattern
= cwd
+ "/" + pattern
;
1796 cwd_skip
= cwd
.length() + 1;
1799 int nret
= glob(work_pattern
.data(),
1800 flags
& PHP_GLOB_FLAGMASK
,
1803 if (nret
== GLOB_NOMATCH
) {
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
)) {
1816 return empty_vec_array();
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;
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
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
])) {
1843 ret
.append(String(globbuf
.gl_pathv
[n
] + cwd_skip
, CopyString
));
1848 if (basedir_limit
&& ret
.empty()) {
1851 // php's glob always produces an array, but Variant::Variant(CArrRef)
1852 // will produce KindOfNull if given a req::ptr wrapped around null.
1854 return empty_vec_array();
1859 Variant
HHVM_FUNCTION(tempnam
,
1861 const String
& prefix
) {
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;
1880 int fd
= mkstemp(buf
);
1882 Logger::Verbose("%s/%d: %s", __FUNCTION__
, __LINE__
,
1883 folly::errnoStr(errno
).c_str());
1889 return templ
.setSize(strlen(buf
));
1892 Variant
HHVM_FUNCTION(tmpfile
) {
1893 auto file
= req::make
<TempFile
>(true);
1894 if (!file
->valid()) {
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
));
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;
1920 CHECK_SYSTEM_SILENT(w
->rmdir(dirname
, options
));
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)");
1941 #if ENABLE_EXTENSION_POSIX
1942 if (!HHVM_FN(posix_access
)(directory
, X_OK
)) {
1943 raise_warning("chdir(): Permission denied (errno 13)");
1948 g_context
->setCwd(HHVM_FN(realpath
)(directory
).toString());
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("/"));
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
;
1980 IMPLEMENT_STATIC_REQUEST_LOCAL(DirectoryData
, s_directory_data
);
1986 req::ptr
<Directory
> get_dir(const Resource
& dir_handle
) {
1987 if (dir_handle
.isNull()) {
1988 auto defaultDir
= s_directory_data
->defaultDirectory
;
1990 raise_warning("no Directory resource supplied");
1996 auto d
= dyn_cast_or_null
<Directory
>(dir_handle
);
1998 raise_warning("Not a valid directory resource");
2004 bool StringDescending(const String
& s1
, const String
& s2
) {
2008 bool StringAscending(const String
& s1
, const String
& 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)) {
2021 auto d
= SystemLib::AllocDirectoryObject();
2022 d
->setProp(nullptr, s_path
.get(), directory
.asTypedValue());
2023 d
->setProp(nullptr, s_handle
.get(), *dir
.asTypedValue());
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
);
2036 s_directory_data
->defaultDirectory
= p
;
2040 Variant
HHVM_FUNCTION(readdir
,
2041 const Variant
& dir_handle
/* = null */) {
2042 const Resource
& res_dir_handle
= dir_handle
.isNull()
2044 : dir_handle
.toResource();
2045 auto dir
= get_dir(res_dir_handle
);
2052 void HHVM_FUNCTION(rewinddir
,
2053 const Variant
& dir_handle
/* = null */) {
2054 const Resource
& res_dir_handle
= dir_handle
.isNull()
2056 : dir_handle
.toResource();
2057 auto dir
= get_dir(res_dir_handle
);
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");
2072 CHECK_PATH(directory
, 1);
2073 Stream::Wrapper
* w
= Stream::getWrapperFromURI(directory
);
2074 if (!w
) return false;
2075 auto dir
= w
->opendir(directory
);
2080 std::vector
<String
> names
;
2082 auto name
= dir
->read();
2083 if (same(name
, false)) {
2086 names
.push_back(name
.toString());
2093 descending
? StringDescending
: StringAscending
2096 VecInit ret
{names
.size()};
2097 for (auto& name
: names
) {
2100 return ret
.toVariant();
2103 void HHVM_FUNCTION(closedir
,
2104 const Variant
& dir_handle
/* = null */) {
2105 const Resource
& res_dir_handle
= dir_handle
.isNull()
2107 : dir_handle
.toResource();
2108 auto d
= get_dir(res_dir_handle
);
2112 if (s_directory_data
->defaultDirectory
== d
) {
2113 s_directory_data
->defaultDirectory
= nullptr;
2118 ///////////////////////////////////////////////////////////////////////////////
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
);
2196 HHVM_FE(file_get_contents
);
2197 HHVM_FE(file_put_contents
);
2200 HHVM_FE(move_uploaded_file
);
2201 HHVM_FE(parse_ini_file
);
2202 HHVM_FE(parse_ini_string
);
2232 HHVM_FE(is_writable
);
2233 HHVM_FE(is_writeable
);
2234 HHVM_FE(is_readable
);
2235 HHVM_FE(is_executable
);
2239 HHVM_FE(is_uploaded_file
);
2240 HHVM_FE(file_exists
);
2243 HHVM_FE(clearstatcache
);
2244 HHVM_FE(readlink_internal
);
2248 HHVM_FE(disk_free_space
);
2249 HHVM_FE(diskfreespace
);
2250 HHVM_FE(disk_total_space
);
2266 loadSystemlib("std_file");
2269 ///////////////////////////////////////////////////////////////////////////////