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
) {
41 // Before setting new thread local IO structures the previous ones must be
42 // cleared to ensure that they are closed appropriately.
43 always_assert(!tl_stdin
&& !tl_stdout
&& !tl_stderr
);
50 void clearThreadLocalIO() {
51 if (tl_stdin
) fclose(tl_stdin
);
52 if (tl_stdout
) fclose(tl_stdout
);
53 if (tl_stderr
) fclose(tl_stderr
);
54 tl_stdin
= tl_stdout
= tl_stderr
= nullptr;
57 ///////////////////////////////////////////////////////////////////////////////
58 // constructor and destructor
60 PlainFile::PlainFile(FILE *stream
, bool nonblocking
,
61 const String
& wrapper_type
, const String
& stream_type
)
63 wrapper_type
.isNull() ? s_plainfile
: wrapper_type
,
64 stream_type
.isNull() ? s_stdio
: stream_type
),
65 m_stream(stream
), m_buffer(nullptr) {
67 setFd(fileno(stream
));
68 m_buffer
= (char *)malloc(BUFSIZ
);
70 setbuffer(stream
, m_buffer
, BUFSIZ
);
75 PlainFile::PlainFile(int fd
, bool nonblocking
,
76 const String
& wrapper_type
, const String
& stream_type
)
78 wrapper_type
.isNull() ? s_plainfile
: wrapper_type
,
79 stream_type
.isNull() ? s_stdio
: stream_type
),
80 m_stream(nullptr), m_buffer(nullptr) {
84 PlainFile::~PlainFile() {
88 void PlainFile::sweep() {
93 bool PlainFile::open(const String
& filename
, const String
& mode
) {
96 assertx(m_stream
== nullptr);
97 assertx(getFd() == -1);
99 // For these definded in php fopen but C stream have different modes
102 if (mode
.find('+') == -1) {
103 fd
= ::open(filename
.data(), O_WRONLY
|O_CREAT
|O_EXCL
, 0666);
104 if (fd
< 0) return false;
107 fd
= ::open(filename
.data(), O_RDWR
|O_CREAT
|O_EXCL
, 0666);
108 if (fd
< 0) return false;
109 f
= fdopen(fd
, "w+");
113 if (mode
.find('+') == -1) {
114 fd
= ::open(filename
.data(), O_WRONLY
|O_CREAT
, 0666);
115 if (fd
< 0) return false;
118 fd
= ::open(filename
.data(), O_RDWR
|O_CREAT
, 0666);
119 if (fd
< 0) return false;
120 f
= fdopen(fd
, "w+");
124 f
= fopen(filename
.data(), mode
.data());
131 m_buffer
= (char *)malloc(BUFSIZ
);
132 setName(filename
.toCppString());
134 setbuffer(f
, m_buffer
, BUFSIZ
);
138 bool PlainFile::close() {
139 invokeFiltersOnClose();
143 bool PlainFile::closeImpl() {
148 s_pcloseRet
= fclose(m_stream
);
150 } else if (getFd() >= 0) {
151 s_pcloseRet
= ::close(getFd());
157 ret
= (s_pcloseRet
== 0);
165 ///////////////////////////////////////////////////////////////////////////////
168 int64_t PlainFile::readImpl(char *buffer
, int64_t length
) {
171 // use read instead of fread to handle EOL in stdin
172 size_t ret
= ::read(getFd(), buffer
, length
);
174 || (ret
== (size_t)-1
175 && errno
!= EWOULDBLOCK
&& errno
!= EINTR
&& errno
!= EBADF
)) {
178 return ret
== (size_t)-1 ? 0 : ret
;
181 int PlainFile::getc() {
186 // This definition is needed to avoid triggering a gcc compiler error about
187 // an overloaded virtual when only overriding the one parameter version from
189 String
PlainFile::read() {
193 String
PlainFile::read(int64_t length
) {
194 if (length
) setEof(false);
195 return File::read(length
);
198 int64_t PlainFile::writeImpl(const char *buffer
, int64_t length
) {
202 // use write instead of fwrite to be consistent with read
203 // o.w., read-and-write files would not work
204 int64_t written
= ::write(getFd(), buffer
, length
);
205 return written
< 0 ? 0 : written
;
208 bool PlainFile::seek(int64_t offset
, int whence
/* = SEEK_SET */) {
211 if (whence
== SEEK_CUR
) {
212 off_t result
= lseek(getFd(), 0, SEEK_CUR
);
213 if (result
!= (off_t
)-1) {
214 offset
+= result
- (bufferedLen() + getPosition());
216 if (offset
> 0 && offset
< bufferedLen()) {
217 setReadPosition(getReadPosition() + offset
);
218 setPosition(getPosition() + offset
);
221 offset
+= getPosition();
225 // invalidate the current buffer
228 // clear the eof flag
231 // lseek instead of seek to be consistent with read
232 off_t result
= lseek(getFd(), offset
, whence
);
234 return result
!= (off_t
)-1;
237 int64_t PlainFile::tell() {
239 return getPosition();
242 bool PlainFile::eof() {
244 int64_t avail
= bufferedLen();
251 bool PlainFile::rewind() {
260 bool PlainFile::stat(struct stat
*sb
) {
262 return ::fstat(getFd(), sb
) == 0;
265 bool PlainFile::flush() {
267 return fflush(m_stream
) == 0;
270 // No need to flush a file descriptor.
274 bool PlainFile::truncate(int64_t size
) {
276 return ftruncate(getFd(), size
) == 0;
279 ///////////////////////////////////////////////////////////////////////////////
282 const StaticString
s_php("PHP");
284 BuiltinFile::BuiltinFile(FILE *stream
) : PlainFile(stream
, true, s_php
) {}
285 BuiltinFile::BuiltinFile(int fd
) : PlainFile(fd
, true, s_php
) {}
287 BuiltinFile::~BuiltinFile() {
293 bool BuiltinFile::close() {
294 invokeFiltersOnClose();
295 if (m_stream
== tl_stdin
) tl_stdin
= nullptr;
296 if (m_stream
== tl_stdout
) tl_stdout
= nullptr;
297 if (m_stream
== tl_stderr
) tl_stderr
= nullptr;
298 auto status
= ::fclose(m_stream
);
306 void BuiltinFile::sweep() {
307 invokeFiltersOnClose();
308 // This object was just a wrapper around a FILE* or fd owned by someone else,
309 // so don't close it except in explicit calls to close(). Beware this doesn't
310 // call PlainFile::sweep().
317 IMPLEMENT_REQUEST_LOCAL(BuiltinFiles
, g_builtin_files
);
319 void BuiltinFiles::requestInit() {
325 void BuiltinFiles::requestShutdown() {
326 m_stdin
.releaseForSweep();
327 m_stdout
.releaseForSweep();
328 m_stderr
.releaseForSweep();
331 const Variant
& BuiltinFiles::GetSTDIN() {
332 if (g_builtin_files
->m_stdin
.isNull()) {
333 auto f
= req::make
<BuiltinFile
>(tl_stdin
? tl_stdin
: stdin
);
334 g_builtin_files
->m_stdin
= f
;
336 assertx(f
->getId() == 1);
338 return g_builtin_files
->m_stdin
;
341 const Variant
& BuiltinFiles::GetSTDOUT() {
342 if (g_builtin_files
->m_stdout
.isNull()) {
343 auto f
= req::make
<BuiltinFile
>(tl_stdout
? tl_stdout
: stdout
);
344 g_builtin_files
->m_stdout
= f
;
346 assertx(f
->getId() == 2);
348 return g_builtin_files
->m_stdout
;
351 const Variant
& BuiltinFiles::GetSTDERR() {
352 if (g_builtin_files
->m_stderr
.isNull()) {
353 auto f
= req::make
<BuiltinFile
>(tl_stderr
? tl_stderr
: stderr
);
354 g_builtin_files
->m_stderr
= f
;
356 assertx(f
->getId() == 3);
358 return g_builtin_files
->m_stderr
;
361 ///////////////////////////////////////////////////////////////////////////////