PageRenderTime 55ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/base/plain-file.cpp

https://github.com/soitun/hiphop-php
C++ | 374 lines | 284 code | 52 blank | 38 comment | 59 complexity | 1754b7257637c9a0f47765cc79e58e04 MD5 | raw file
  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. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/runtime/base/plain-file.h"
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include "hphp/util/rds-local.h"
  20. #include <folly/portability/Fcntl.h>
  21. #include <folly/portability/Stdio.h>
  22. #include <folly/portability/Unistd.h>
  23. namespace HPHP {
  24. const StaticString s_plainfile("plainfile");
  25. const StaticString s_stdio("STDIO");
  26. const StaticString s_stdin("STDIN");
  27. const StaticString s_stdout("STDOUT");
  28. const StaticString s_stderr("STDERR");
  29. struct StdFiles {
  30. FILE* stdin{nullptr};
  31. FILE* stdout{nullptr};
  32. FILE* stderr{nullptr};
  33. };
  34. namespace {
  35. RDS_LOCAL(StdFiles, rl_stdfiles);
  36. }
  37. void setThreadLocalIO(FILE* in, FILE* out, FILE* err) {
  38. // Before setting new thread local IO structures the previous ones must be
  39. // cleared to ensure that they are closed appropriately.
  40. always_assert(!rl_stdfiles->stdin &&
  41. !rl_stdfiles->stdout &&
  42. !rl_stdfiles->stderr);
  43. rl_stdfiles->stdin = in;
  44. rl_stdfiles->stdout = out;
  45. rl_stdfiles->stderr = err;
  46. }
  47. void clearThreadLocalIO() {
  48. if (rl_stdfiles->stdin) fclose(rl_stdfiles->stdin);
  49. if (rl_stdfiles->stdout) fclose(rl_stdfiles->stdout);
  50. if (rl_stdfiles->stderr) fclose(rl_stdfiles->stderr);
  51. rl_stdfiles->stdin = rl_stdfiles->stdout = rl_stdfiles->stderr = nullptr;
  52. }
  53. ///////////////////////////////////////////////////////////////////////////////
  54. // constructor and destructor
  55. PlainFile::PlainFile(FILE *stream, bool nonblocking,
  56. const String& wrapper_type, const String& stream_type)
  57. : File(nonblocking,
  58. wrapper_type.isNull() ? s_plainfile : wrapper_type,
  59. stream_type.isNull() ? s_stdio : stream_type),
  60. m_stream(stream), m_buffer(nullptr) {
  61. if (stream) {
  62. setFd(fileno(stream));
  63. m_buffer = (char *)malloc(BUFSIZ);
  64. if (m_buffer)
  65. setbuffer(stream, m_buffer, BUFSIZ);
  66. }
  67. setIsLocal(true);
  68. }
  69. PlainFile::PlainFile(int fd, bool nonblocking,
  70. const String& wrapper_type, const String& stream_type)
  71. : File(nonblocking,
  72. wrapper_type.isNull() ? s_plainfile : wrapper_type,
  73. stream_type.isNull() ? s_stdio : stream_type),
  74. m_stream(nullptr), m_buffer(nullptr) {
  75. setFd(fd);
  76. }
  77. PlainFile::~PlainFile() {
  78. closeImpl();
  79. }
  80. void PlainFile::sweep() {
  81. closeImpl();
  82. File::sweep();
  83. }
  84. bool PlainFile::open(const String& filename, const String& mode) {
  85. int fd;
  86. FILE *f;
  87. assertx(m_stream == nullptr);
  88. assertx(getFd() == -1);
  89. // For these defined in php fopen but C stream have different modes
  90. switch (mode[0]) {
  91. case 'x':
  92. if (mode.find('+') == -1) {
  93. fd = ::open(filename.data(), O_WRONLY|O_CREAT|O_EXCL, 0666);
  94. if (fd < 0) return false;
  95. f = fdopen(fd, "w");
  96. } else {
  97. fd = ::open(filename.data(), O_RDWR|O_CREAT|O_EXCL, 0666);
  98. if (fd < 0) return false;
  99. f = fdopen(fd, "w+");
  100. }
  101. break;
  102. case 'c':
  103. if (mode.find('+') == -1) {
  104. fd = ::open(filename.data(), O_WRONLY|O_CREAT, 0666);
  105. if (fd < 0) return false;
  106. f = fdopen(fd, "w");
  107. } else {
  108. fd = ::open(filename.data(), O_RDWR|O_CREAT, 0666);
  109. if (fd < 0) return false;
  110. f = fdopen(fd, "w+");
  111. }
  112. break;
  113. default:
  114. f = fopen(filename.data(), mode.data());
  115. }
  116. if (!f) {
  117. return false;
  118. }
  119. m_stream = f;
  120. setFd(fileno(f));
  121. m_buffer = (char *)malloc(BUFSIZ);
  122. setName(filename.toCppString());
  123. if (m_buffer)
  124. setbuffer(f, m_buffer, BUFSIZ);
  125. return true;
  126. }
  127. bool PlainFile::close() {
  128. return closeImpl();
  129. }
  130. bool PlainFile::closeImpl() {
  131. bool ret = true;
  132. *s_pcloseRet = 0;
  133. if (!isClosed()) {
  134. if (m_stream) {
  135. *s_pcloseRet = fclose(m_stream);
  136. m_stream = nullptr;
  137. } else if (getFd() >= 0) {
  138. *s_pcloseRet = ::close(getFd());
  139. }
  140. if (m_buffer) {
  141. free(m_buffer);
  142. m_buffer = nullptr;
  143. }
  144. ret = (*s_pcloseRet == 0);
  145. setIsClosed(true);
  146. setFd(-1);
  147. }
  148. File::closeImpl();
  149. return ret;
  150. }
  151. ///////////////////////////////////////////////////////////////////////////////
  152. // virtual functions
  153. int64_t PlainFile::readImpl(char *buffer, int64_t length) {
  154. assertx(valid());
  155. assertx(length > 0);
  156. // use read instead of fread to handle EOL in stdin
  157. size_t ret = ::read(getFd(), buffer, length);
  158. if (ret == 0
  159. || (ret == (size_t)-1
  160. && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF)) {
  161. setEof(true);
  162. }
  163. return ret == (size_t)-1 ? 0 : ret;
  164. }
  165. int PlainFile::getc() {
  166. assertx(valid());
  167. return File::getc();
  168. }
  169. // This definition is needed to avoid triggering a gcc compiler error about
  170. // an overloaded virtual when only overriding the one parameter version from
  171. // File.
  172. String PlainFile::read() {
  173. return File::read();
  174. }
  175. String PlainFile::read(int64_t length) {
  176. if (length) setEof(false);
  177. return File::read(length);
  178. }
  179. int64_t PlainFile::writeImpl(const char *buffer, int64_t length) {
  180. assertx(valid());
  181. assertx(length > 0);
  182. // use write instead of fwrite to be consistent with read
  183. // o.w., read-and-write files would not work
  184. int64_t written = ::write(getFd(), buffer, length);
  185. return written < 0 ? 0 : written;
  186. }
  187. bool PlainFile::seek(int64_t offset, int whence /* = SEEK_SET */) {
  188. assertx(valid());
  189. if (whence == SEEK_CUR) {
  190. off_t result = lseek(getFd(), 0, SEEK_CUR);
  191. if (result != (off_t)-1) {
  192. offset += result - (bufferedLen() + getPosition());
  193. }
  194. if (offset > 0 && offset < bufferedLen()) {
  195. setReadPosition(getReadPosition() + offset);
  196. setPosition(getPosition() + offset);
  197. return true;
  198. }
  199. offset += getPosition();
  200. whence = SEEK_SET;
  201. }
  202. // invalidate the current buffer
  203. setWritePosition(0);
  204. setReadPosition(0);
  205. // clear the eof flag
  206. setEof(false);
  207. flush();
  208. // lseek instead of seek to be consistent with read
  209. off_t result = lseek(getFd(), offset, whence);
  210. setPosition(result);
  211. return result != (off_t)-1;
  212. }
  213. int64_t PlainFile::tell() {
  214. assertx(valid());
  215. return getPosition();
  216. }
  217. bool PlainFile::eof() {
  218. assertx(valid());
  219. int64_t avail = bufferedLen();
  220. if (avail > 0) {
  221. return false;
  222. }
  223. return getEof();
  224. }
  225. bool PlainFile::rewind() {
  226. assertx(valid());
  227. seek(0);
  228. setWritePosition(0);
  229. setReadPosition(0);
  230. setPosition(0);
  231. return true;
  232. }
  233. bool PlainFile::stat(struct stat *sb) {
  234. assertx(valid());
  235. return ::fstat(getFd(), sb) == 0;
  236. }
  237. bool PlainFile::flush() {
  238. if (m_stream) {
  239. return fflush(m_stream) == 0;
  240. }
  241. assertx(valid());
  242. // No need to flush a file descriptor.
  243. return true;
  244. }
  245. bool PlainFile::truncate(int64_t size) {
  246. assertx(valid());
  247. return ftruncate(getFd(), size) == 0;
  248. }
  249. ///////////////////////////////////////////////////////////////////////////////
  250. // BuiltinFiles
  251. const StaticString s_php("PHP");
  252. BuiltinFile::BuiltinFile(FILE *stream) : PlainFile(stream, true, s_php) {}
  253. BuiltinFile::BuiltinFile(int fd) : PlainFile(fd, true, s_php) {}
  254. BuiltinFile::~BuiltinFile() {
  255. setIsClosed(true);
  256. m_stream = nullptr;
  257. setFd(-1);
  258. }
  259. bool BuiltinFile::close() {
  260. if (m_stream == rl_stdfiles->stdin) rl_stdfiles->stdin = nullptr;
  261. if (m_stream == rl_stdfiles->stdout) rl_stdfiles->stdout = nullptr;
  262. if (m_stream == rl_stdfiles->stderr) rl_stdfiles->stderr = nullptr;
  263. auto status = ::fclose(m_stream);
  264. setIsClosed(true);
  265. m_stream = nullptr;
  266. setFd(-1);
  267. File::closeImpl();
  268. return status == 0;
  269. }
  270. void BuiltinFile::sweep() {
  271. // This object was just a wrapper around a FILE* or fd owned by someone else,
  272. // so don't close it except in explicit calls to close(). Beware this doesn't
  273. // call PlainFile::sweep().
  274. m_stream = nullptr;
  275. setFd(-1);
  276. setIsClosed(true);
  277. File::sweep();
  278. }
  279. IMPLEMENT_REQUEST_LOCAL(BuiltinFiles, g_builtin_files);
  280. void BuiltinFiles::requestInit() {
  281. getSTDIN();
  282. getSTDOUT();
  283. getSTDERR();
  284. }
  285. void BuiltinFiles::requestShutdown() {
  286. m_stdin.releaseForSweep();
  287. m_stdout.releaseForSweep();
  288. m_stderr.releaseForSweep();
  289. }
  290. static const Variant& getHelper(Variant& global_fd, FILE* rds_fd, FILE* fd,
  291. int fd_num) {
  292. if (global_fd.isNull()) {
  293. auto f = req::make<BuiltinFile>(rds_fd ? rds_fd : fd);
  294. global_fd = f;
  295. f->setId(fd_num);
  296. assertx(f->getId() == fd_num);
  297. }
  298. return global_fd;
  299. }
  300. Variant BuiltinFiles::getSTDIN(const StringData* name) {
  301. assertx(s_stdin.same(name));
  302. return getSTDIN();
  303. }
  304. Variant BuiltinFiles::getSTDOUT(const StringData* name) {
  305. assertx(s_stdout.same(name));
  306. return getSTDOUT();
  307. }
  308. Variant BuiltinFiles::getSTDERR(const StringData* name) {
  309. assertx(s_stderr.same(name));
  310. return getSTDERR();
  311. }
  312. const Variant& BuiltinFiles::getSTDIN() {
  313. return getHelper(g_builtin_files->m_stdin, rl_stdfiles->stdin, stdin, 1);
  314. }
  315. const Variant& BuiltinFiles::getSTDOUT() {
  316. return getHelper(g_builtin_files->m_stdout, rl_stdfiles->stdout, stdout, 2);
  317. }
  318. const Variant& BuiltinFiles::getSTDERR() {
  319. return getHelper(g_builtin_files->m_stderr, rl_stdfiles->stderr, stderr, 3);
  320. }
  321. ///////////////////////////////////////////////////////////////////////////////
  322. }