Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / plain-file.cpp
blob50b572a8b9e0b4212337e810cf93cca13498dd4c
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
19 #include <sys/stat.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>
27 namespace HPHP {
29 const StaticString s_plainfile("plainfile");
30 const StaticString s_stdio("STDIO");
32 namespace {
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 tl_stdin = in;
42 tl_stdout = out;
43 tl_stderr = 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)
55 : File(nonblocking,
56 wrapper_type.isNull() ? s_plainfile : wrapper_type,
57 stream_type.isNull() ? s_stdio : stream_type),
58 m_stream(stream), m_buffer(nullptr) {
59 if (stream) {
60 setFd(fileno(stream));
61 m_buffer = (char *)malloc(BUFSIZ);
62 if (m_buffer)
63 setbuffer(stream, m_buffer, BUFSIZ);
65 setIsLocal(true);
68 PlainFile::PlainFile(int fd, bool nonblocking,
69 const String& wrapper_type, const String& stream_type)
70 : File(nonblocking,
71 wrapper_type.isNull() ? s_plainfile : wrapper_type,
72 stream_type.isNull() ? s_stdio : stream_type),
73 m_stream(nullptr), m_buffer(nullptr) {
74 setFd(fd);
77 PlainFile::~PlainFile() {
78 closeImpl();
81 void PlainFile::sweep() {
82 closeImpl();
83 File::sweep();
86 bool PlainFile::open(const String& filename, const String& mode) {
87 int fd;
88 FILE *f;
89 assert(m_stream == nullptr);
90 assert(getFd() == -1);
92 // For these definded in php fopen but C stream have different modes
93 switch (mode[0]) {
94 case 'x':
95 if (mode.find('+') == -1) {
96 fd = ::open(filename.data(), O_WRONLY|O_CREAT|O_EXCL, 0666);
97 if (fd < 0) return false;
98 f = fdopen(fd, "w");
99 } else {
100 fd = ::open(filename.data(), O_RDWR|O_CREAT|O_EXCL, 0666);
101 if (fd < 0) return false;
102 f = fdopen(fd, "w+");
104 break;
105 case 'c':
106 if (mode.find('+') == -1) {
107 fd = ::open(filename.data(), O_WRONLY|O_CREAT, 0666);
108 if (fd < 0) return false;
109 f = fdopen(fd, "w");
110 } else {
111 fd = ::open(filename.data(), O_RDWR|O_CREAT, 0666);
112 if (fd < 0) return false;
113 f = fdopen(fd, "w+");
115 break;
116 default:
117 f = fopen(filename.data(), mode.data());
119 if (!f) {
120 return false;
122 m_stream = f;
123 setFd(fileno(f));
124 m_buffer = (char *)malloc(BUFSIZ);
125 setName(filename.toCppString());
126 if (m_buffer)
127 setbuffer(f, m_buffer, BUFSIZ);
128 return true;
131 bool PlainFile::close() {
132 invokeFiltersOnClose();
133 return closeImpl();
136 bool PlainFile::closeImpl() {
137 bool ret = true;
138 s_pcloseRet = 0;
139 if (!isClosed()) {
140 if (m_stream) {
141 s_pcloseRet = fclose(m_stream);
142 m_stream = nullptr;
143 } else if (getFd() >= 0) {
144 s_pcloseRet = ::close(getFd());
146 if (m_buffer) {
147 free(m_buffer);
148 m_buffer = nullptr;
150 ret = (s_pcloseRet == 0);
151 setIsClosed(true);
152 setFd(-1);
154 File::closeImpl();
155 return ret;
158 ///////////////////////////////////////////////////////////////////////////////
159 // virtual functions
161 int64_t PlainFile::readImpl(char *buffer, int64_t length) {
162 assert(valid());
163 assert(length > 0);
164 // use read instead of fread to handle EOL in stdin
165 size_t ret = ::read(getFd(), buffer, length);
166 if (ret == 0
167 || (ret == (size_t)-1
168 && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF)) {
169 setEof(true);
171 return ret == (size_t)-1 ? 0 : ret;
174 int PlainFile::getc() {
175 assert(valid());
176 return File::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
181 // File.
182 String PlainFile::read() {
183 return File::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) {
192 assert(valid());
193 assert(length > 0);
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 */) {
202 assert(valid());
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);
212 return true;
214 offset += getPosition();
215 whence = SEEK_SET;
218 // invalidate the current buffer
219 setWritePosition(0);
220 setReadPosition(0);
221 // clear the eof flag
222 setEof(false);
223 flush();
224 // lseek instead of seek to be consistent with read
225 off_t result = lseek(getFd(), offset, whence);
226 setPosition(result);
227 return result != (off_t)-1;
230 int64_t PlainFile::tell() {
231 assert(valid());
232 return getPosition();
235 bool PlainFile::eof() {
236 assert(valid());
237 int64_t avail = bufferedLen();
238 if (avail > 0) {
239 return false;
241 return getEof();
244 bool PlainFile::rewind() {
245 assert(valid());
246 seek(0);
247 setWritePosition(0);
248 setReadPosition(0);
249 setPosition(0);
250 return true;
253 bool PlainFile::stat(struct stat *sb) {
254 assert(valid());
255 return ::fstat(getFd(), sb) == 0;
258 bool PlainFile::flush() {
259 if (m_stream) {
260 return fflush(m_stream) == 0;
262 assert(valid());
263 // No need to flush a file descriptor.
264 return true;
267 bool PlainFile::truncate(int64_t size) {
268 assert(valid());
269 return ftruncate(getFd(), size) == 0;
272 ///////////////////////////////////////////////////////////////////////////////
273 // BuiltinFiles
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() {
281 setIsClosed(true);
282 m_stream = nullptr;
283 setFd(-1);
286 bool BuiltinFile::close() {
287 invokeFiltersOnClose();
288 auto status = ::fclose(m_stream);
289 setIsClosed(true);
290 m_stream = nullptr;
291 setFd(-1);
292 File::closeImpl();
293 return status == 0;
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().
301 m_stream = nullptr;
302 setFd(-1);
303 setIsClosed(true);
304 File::sweep();
307 IMPLEMENT_REQUEST_LOCAL(BuiltinFiles, g_builtin_files);
309 void BuiltinFiles::requestInit() {
310 GetSTDIN();
311 GetSTDOUT();
312 GetSTDERR();
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;
325 f->setId(1);
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;
335 f->setId(2);
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;
345 f->setId(3);
346 assert(f->getId() == 3);
348 return g_builtin_files->m_stderr;
351 ///////////////////////////////////////////////////////////////////////////////