| /* |
| * Copyright 2015 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 <folly/File.h> |
| |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/file.h> |
| |
| #include <folly/Exception.h> |
| #include <folly/FileUtil.h> |
| #include <folly/Format.h> |
| #include <folly/ScopeGuard.h> |
| |
| #include <system_error> |
| |
| #include <glog/logging.h> |
| |
| namespace folly { |
| |
| File::File() |
| : fd_(-1) |
| , ownsFd_(false) |
| {} |
| |
| File::File(int fd, bool ownsFd) |
| : 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) { |
| using std::swap; |
| swap(fd_, other.fd_); |
| swap(ownsFd_, other.ownsFd_); |
| } |
| |
| void swap(File& a, File& b) { |
| 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 |