Codemod asserts to assertxs in the runtime
[hiphop-php.git] / hphp / runtime / base / plain-file.cpp
blobe9394e9b46eb9af5945f1940f8bc77cbd441ffde
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 // 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);
45 tl_stdin = in;
46 tl_stdout = out;
47 tl_stderr = err;
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)
62 : File(nonblocking,
63 wrapper_type.isNull() ? s_plainfile : wrapper_type,
64 stream_type.isNull() ? s_stdio : stream_type),
65 m_stream(stream), m_buffer(nullptr) {
66 if (stream) {
67 setFd(fileno(stream));
68 m_buffer = (char *)malloc(BUFSIZ);
69 if (m_buffer)
70 setbuffer(stream, m_buffer, BUFSIZ);
72 setIsLocal(true);
75 PlainFile::PlainFile(int fd, bool nonblocking,
76 const String& wrapper_type, const String& stream_type)
77 : File(nonblocking,
78 wrapper_type.isNull() ? s_plainfile : wrapper_type,
79 stream_type.isNull() ? s_stdio : stream_type),
80 m_stream(nullptr), m_buffer(nullptr) {
81 setFd(fd);
84 PlainFile::~PlainFile() {
85 closeImpl();
88 void PlainFile::sweep() {
89 closeImpl();
90 File::sweep();
93 bool PlainFile::open(const String& filename, const String& mode) {
94 int fd;
95 FILE *f;
96 assertx(m_stream == nullptr);
97 assertx(getFd() == -1);
99 // For these definded in php fopen but C stream have different modes
100 switch (mode[0]) {
101 case 'x':
102 if (mode.find('+') == -1) {
103 fd = ::open(filename.data(), O_WRONLY|O_CREAT|O_EXCL, 0666);
104 if (fd < 0) return false;
105 f = fdopen(fd, "w");
106 } else {
107 fd = ::open(filename.data(), O_RDWR|O_CREAT|O_EXCL, 0666);
108 if (fd < 0) return false;
109 f = fdopen(fd, "w+");
111 break;
112 case 'c':
113 if (mode.find('+') == -1) {
114 fd = ::open(filename.data(), O_WRONLY|O_CREAT, 0666);
115 if (fd < 0) return false;
116 f = fdopen(fd, "w");
117 } else {
118 fd = ::open(filename.data(), O_RDWR|O_CREAT, 0666);
119 if (fd < 0) return false;
120 f = fdopen(fd, "w+");
122 break;
123 default:
124 f = fopen(filename.data(), mode.data());
126 if (!f) {
127 return false;
129 m_stream = f;
130 setFd(fileno(f));
131 m_buffer = (char *)malloc(BUFSIZ);
132 setName(filename.toCppString());
133 if (m_buffer)
134 setbuffer(f, m_buffer, BUFSIZ);
135 return true;
138 bool PlainFile::close() {
139 invokeFiltersOnClose();
140 return closeImpl();
143 bool PlainFile::closeImpl() {
144 bool ret = true;
145 s_pcloseRet = 0;
146 if (!isClosed()) {
147 if (m_stream) {
148 s_pcloseRet = fclose(m_stream);
149 m_stream = nullptr;
150 } else if (getFd() >= 0) {
151 s_pcloseRet = ::close(getFd());
153 if (m_buffer) {
154 free(m_buffer);
155 m_buffer = nullptr;
157 ret = (s_pcloseRet == 0);
158 setIsClosed(true);
159 setFd(-1);
161 File::closeImpl();
162 return ret;
165 ///////////////////////////////////////////////////////////////////////////////
166 // virtual functions
168 int64_t PlainFile::readImpl(char *buffer, int64_t length) {
169 assertx(valid());
170 assertx(length > 0);
171 // use read instead of fread to handle EOL in stdin
172 size_t ret = ::read(getFd(), buffer, length);
173 if (ret == 0
174 || (ret == (size_t)-1
175 && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF)) {
176 setEof(true);
178 return ret == (size_t)-1 ? 0 : ret;
181 int PlainFile::getc() {
182 assertx(valid());
183 return File::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
188 // File.
189 String PlainFile::read() {
190 return File::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) {
199 assertx(valid());
200 assertx(length > 0);
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 */) {
209 assertx(valid());
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);
219 return true;
221 offset += getPosition();
222 whence = SEEK_SET;
225 // invalidate the current buffer
226 setWritePosition(0);
227 setReadPosition(0);
228 // clear the eof flag
229 setEof(false);
230 flush();
231 // lseek instead of seek to be consistent with read
232 off_t result = lseek(getFd(), offset, whence);
233 setPosition(result);
234 return result != (off_t)-1;
237 int64_t PlainFile::tell() {
238 assertx(valid());
239 return getPosition();
242 bool PlainFile::eof() {
243 assertx(valid());
244 int64_t avail = bufferedLen();
245 if (avail > 0) {
246 return false;
248 return getEof();
251 bool PlainFile::rewind() {
252 assertx(valid());
253 seek(0);
254 setWritePosition(0);
255 setReadPosition(0);
256 setPosition(0);
257 return true;
260 bool PlainFile::stat(struct stat *sb) {
261 assertx(valid());
262 return ::fstat(getFd(), sb) == 0;
265 bool PlainFile::flush() {
266 if (m_stream) {
267 return fflush(m_stream) == 0;
269 assertx(valid());
270 // No need to flush a file descriptor.
271 return true;
274 bool PlainFile::truncate(int64_t size) {
275 assertx(valid());
276 return ftruncate(getFd(), size) == 0;
279 ///////////////////////////////////////////////////////////////////////////////
280 // BuiltinFiles
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() {
288 setIsClosed(true);
289 m_stream = nullptr;
290 setFd(-1);
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);
299 setIsClosed(true);
300 m_stream = nullptr;
301 setFd(-1);
302 File::closeImpl();
303 return status == 0;
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().
311 m_stream = nullptr;
312 setFd(-1);
313 setIsClosed(true);
314 File::sweep();
317 IMPLEMENT_REQUEST_LOCAL(BuiltinFiles, g_builtin_files);
319 void BuiltinFiles::requestInit() {
320 GetSTDIN();
321 GetSTDOUT();
322 GetSTDERR();
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;
335 f->setId(1);
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;
345 f->setId(2);
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;
355 f->setId(3);
356 assertx(f->getId() == 3);
358 return g_builtin_files->m_stderr;
361 ///////////////////////////////////////////////////////////////////////////////