2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/plain-file.h"
18 #include <sys/types.h>
21 #include "hphp/runtime/base/request-local.h"
23 #include <folly/portability/Fcntl.h>
24 #include <folly/portability/Stdio.h>
25 #include <folly/portability/Unistd.h>
29 const StaticString
s_plainfile("plainfile");
30 const StaticString
s_stdio("STDIO");
34 __thread
FILE* tl_stdin
{nullptr};
35 __thread
FILE* tl_stdout
{nullptr};
36 __thread
FILE* tl_stderr
{nullptr};
40 void setThreadLocalIO(FILE* in
, FILE* out
, FILE* err
) {
46 void clearThreadLocalIO() {
47 setThreadLocalIO(nullptr, nullptr, nullptr);
50 ///////////////////////////////////////////////////////////////////////////////
51 // constructor and destructor
53 PlainFile::PlainFile(FILE *stream
, bool nonblocking
,
54 const String
& wrapper_type
, const String
& stream_type
)
56 wrapper_type
.isNull() ? s_plainfile
: wrapper_type
,
57 stream_type
.isNull() ? s_stdio
: stream_type
),
58 m_stream(stream
), m_buffer(nullptr) {
60 setFd(fileno(stream
));
61 m_buffer
= (char *)malloc(BUFSIZ
);
63 setbuffer(stream
, m_buffer
, BUFSIZ
);
68 PlainFile::PlainFile(int fd
, bool nonblocking
,
69 const String
& wrapper_type
, const String
& stream_type
)
71 wrapper_type
.isNull() ? s_plainfile
: wrapper_type
,
72 stream_type
.isNull() ? s_stdio
: stream_type
),
73 m_stream(nullptr), m_buffer(nullptr) {
77 PlainFile::~PlainFile() {
81 void PlainFile::sweep() {
86 bool PlainFile::open(const String
& filename
, const String
& mode
) {
89 assert(m_stream
== nullptr);
90 assert(getFd() == -1);
92 // For these definded in php fopen but C stream have different modes
95 if (mode
.find('+') == -1) {
96 fd
= ::open(filename
.data(), O_WRONLY
|O_CREAT
|O_EXCL
, 0666);
97 if (fd
< 0) return false;
100 fd
= ::open(filename
.data(), O_RDWR
|O_CREAT
|O_EXCL
, 0666);
101 if (fd
< 0) return false;
102 f
= fdopen(fd
, "w+");
106 if (mode
.find('+') == -1) {
107 fd
= ::open(filename
.data(), O_WRONLY
|O_CREAT
, 0666);
108 if (fd
< 0) return false;
111 fd
= ::open(filename
.data(), O_RDWR
|O_CREAT
, 0666);
112 if (fd
< 0) return false;
113 f
= fdopen(fd
, "w+");
117 f
= fopen(filename
.data(), mode
.data());
124 m_buffer
= (char *)malloc(BUFSIZ
);
125 setName(filename
.toCppString());
127 setbuffer(f
, m_buffer
, BUFSIZ
);
131 bool PlainFile::close() {
132 invokeFiltersOnClose();
136 bool PlainFile::closeImpl() {
141 s_pcloseRet
= fclose(m_stream
);
143 } else if (getFd() >= 0) {
144 s_pcloseRet
= ::close(getFd());
150 ret
= (s_pcloseRet
== 0);
158 ///////////////////////////////////////////////////////////////////////////////
161 int64_t PlainFile::readImpl(char *buffer
, int64_t length
) {
164 // use read instead of fread to handle EOL in stdin
165 size_t ret
= ::read(getFd(), buffer
, length
);
167 || (ret
== (size_t)-1
168 && errno
!= EWOULDBLOCK
&& errno
!= EINTR
&& errno
!= EBADF
)) {
171 return ret
== (size_t)-1 ? 0 : ret
;
174 int PlainFile::getc() {
179 // This definition is needed to avoid triggering a gcc compiler error about
180 // an overloaded virtual when only overriding the one parameter version from
182 String
PlainFile::read() {
186 String
PlainFile::read(int64_t length
) {
187 if (length
) setEof(false);
188 return File::read(length
);
191 int64_t PlainFile::writeImpl(const char *buffer
, int64_t length
) {
195 // use write instead of fwrite to be consistent with read
196 // o.w., read-and-write files would not work
197 int64_t written
= ::write(getFd(), buffer
, length
);
198 return written
< 0 ? 0 : written
;
201 bool PlainFile::seek(int64_t offset
, int whence
/* = SEEK_SET */) {
204 if (whence
== SEEK_CUR
) {
205 off_t result
= lseek(getFd(), 0, SEEK_CUR
);
206 if (result
!= (off_t
)-1) {
207 offset
+= result
- (bufferedLen() + getPosition());
209 if (offset
> 0 && offset
< bufferedLen()) {
210 setReadPosition(getReadPosition() + offset
);
211 setPosition(getPosition() + offset
);
214 offset
+= getPosition();
218 // invalidate the current buffer
221 // clear the eof flag
224 // lseek instead of seek to be consistent with read
225 off_t result
= lseek(getFd(), offset
, whence
);
227 return result
!= (off_t
)-1;
230 int64_t PlainFile::tell() {
232 return getPosition();
235 bool PlainFile::eof() {
237 int64_t avail
= bufferedLen();
244 bool PlainFile::rewind() {
253 bool PlainFile::stat(struct stat
*sb
) {
255 return ::fstat(getFd(), sb
) == 0;
258 bool PlainFile::flush() {
260 return fflush(m_stream
) == 0;
263 // No need to flush a file descriptor.
267 bool PlainFile::truncate(int64_t size
) {
269 return ftruncate(getFd(), size
) == 0;
272 ///////////////////////////////////////////////////////////////////////////////
275 const StaticString
s_php("PHP");
277 BuiltinFile::BuiltinFile(FILE *stream
) : PlainFile(stream
, true, s_php
) {}
278 BuiltinFile::BuiltinFile(int fd
) : PlainFile(fd
, true, s_php
) {}
280 BuiltinFile::~BuiltinFile() {
286 bool BuiltinFile::close() {
287 invokeFiltersOnClose();
288 auto status
= ::fclose(m_stream
);
296 void BuiltinFile::sweep() {
297 invokeFiltersOnClose();
298 // This object was just a wrapper around a FILE* or fd owned by someone else,
299 // so don't close it except in explicit calls to close(). Beware this doesn't
300 // call PlainFile::sweep().
307 IMPLEMENT_REQUEST_LOCAL(BuiltinFiles
, g_builtin_files
);
309 void BuiltinFiles::requestInit() {
315 void BuiltinFiles::requestShutdown() {
316 m_stdin
.releaseForSweep();
317 m_stdout
.releaseForSweep();
318 m_stderr
.releaseForSweep();
321 const Variant
& BuiltinFiles::GetSTDIN() {
322 if (g_builtin_files
->m_stdin
.isNull()) {
323 auto f
= req::make
<BuiltinFile
>(tl_stdin
? tl_stdin
: stdin
);
324 g_builtin_files
->m_stdin
= f
;
326 assert(f
->getId() == 1);
328 return g_builtin_files
->m_stdin
;
331 const Variant
& BuiltinFiles::GetSTDOUT() {
332 if (g_builtin_files
->m_stdout
.isNull()) {
333 auto f
= req::make
<BuiltinFile
>(tl_stdout
? tl_stdout
: stdout
);
334 g_builtin_files
->m_stdout
= f
;
336 assert(f
->getId() == 2);
338 return g_builtin_files
->m_stdout
;
341 const Variant
& BuiltinFiles::GetSTDERR() {
342 if (g_builtin_files
->m_stderr
.isNull()) {
343 auto f
= req::make
<BuiltinFile
>(tl_stderr
? tl_stderr
: stderr
);
344 g_builtin_files
->m_stderr
= f
;
346 assert(f
->getId() == 3);
348 return g_builtin_files
->m_stderr
;
351 ///////////////////////////////////////////////////////////////////////////////