/* * Copyright 2013-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include namespace folly { File::File() noexcept : fd_(-1), ownsFd_(false) {} File::File(int fd, bool ownsFd) noexcept : fd_(fd), ownsFd_(ownsFd) { CHECK_GE(fd, -1) << "fd must be -1 or non-negative"; CHECK(fd != -1 || !ownsFd) << "cannot own -1"; } File::File(const char* name, int flags, mode_t mode) : fd_(::open(name, flags, mode)), ownsFd_(false) { if (fd_ == -1) { throwSystemError( folly::format("open(\"{}\", {:#o}, 0{:#o}) failed", name, flags, mode) .fbstr()); } ownsFd_ = true; } File::File(const std::string& name, int flags, mode_t mode) : File(name.c_str(), flags, mode) {} File::File(StringPiece name, int flags, mode_t mode) : File(name.str(), flags, mode) {} File::File(File&& other) noexcept : fd_(other.fd_), ownsFd_(other.ownsFd_) { other.release(); } File& File::operator=(File&& other) { closeNoThrow(); swap(other); return *this; } File::~File() { auto fd = fd_; if (!closeNoThrow()) { // ignore most errors DCHECK_NE(errno, EBADF) << "closing fd " << fd << ", it may already " << "have been closed. Another time, this might close the wrong FD."; } } /* static */ File File::temporary() { // make a temp file with tmpfile(), dup the fd, then return it in a File. FILE* tmpFile = tmpfile(); checkFopenError(tmpFile, "tmpfile() failed"); SCOPE_EXIT { fclose(tmpFile); }; int fd = ::dup(fileno(tmpFile)); checkUnixError(fd, "dup() failed"); return File(fd, true); } int File::release() noexcept { int released = fd_; fd_ = -1; ownsFd_ = false; return released; } void File::swap(File& other) noexcept { using std::swap; swap(fd_, other.fd_); swap(ownsFd_, other.ownsFd_); } void swap(File& a, File& b) noexcept { a.swap(b); } File File::dup() const { if (fd_ != -1) { int fd = ::dup(fd_); checkUnixError(fd, "dup() failed"); return File(fd, true); } return File(); } void File::close() { if (!closeNoThrow()) { throwSystemError("close() failed"); } } bool File::closeNoThrow() { int r = ownsFd_ ? ::close(fd_) : 0; release(); return r == 0; } void File::lock() { doLock(LOCK_EX); } bool File::try_lock() { return doTryLock(LOCK_EX); } void File::lock_shared() { doLock(LOCK_SH); } bool File::try_lock_shared() { return doTryLock(LOCK_SH); } void File::doLock(int op) { checkUnixError(flockNoInt(fd_, op), "flock() failed (lock)"); } bool File::doTryLock(int op) { int r = flockNoInt(fd_, op | LOCK_NB); // flock returns EWOULDBLOCK if already locked if (r == -1 && errno == EWOULDBLOCK) { return false; } checkUnixError(r, "flock() failed (try_lock)"); return true; } void File::unlock() { checkUnixError(flockNoInt(fd_, LOCK_UN), "flock() failed (unlock)"); } void File::unlock_shared() { unlock(); } } // namespace folly