1/*
2 * Copyright 2013-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <folly/File.h>
18
19#include <folly/Exception.h>
20#include <folly/FileUtil.h>
21#include <folly/Format.h>
22#include <folly/ScopeGuard.h>
23#include <folly/portability/Fcntl.h>
24#include <folly/portability/SysFile.h>
25#include <folly/portability/Unistd.h>
26
27#include <system_error>
28
29#include <glog/logging.h>
30
31namespace folly {
32
33File::File() noexcept : fd_(-1), ownsFd_(false) {}
34
35File::File(int fd, bool ownsFd) noexcept : fd_(fd), ownsFd_(ownsFd) {
36 CHECK_GE(fd, -1) << "fd must be -1 or non-negative";
37 CHECK(fd != -1 || !ownsFd) << "cannot own -1";
38}
39
40File::File(const char* name, int flags, mode_t mode)
41 : fd_(::open(name, flags, mode)), ownsFd_(false) {
42 if (fd_ == -1) {
43 throwSystemError(
44 folly::format("open(\"{}\", {:#o}, 0{:#o}) failed", name, flags, mode)
45 .fbstr());
46 }
47 ownsFd_ = true;
48}
49
50File::File(const std::string& name, int flags, mode_t mode)
51 : File(name.c_str(), flags, mode) {}
52
53File::File(StringPiece name, int flags, mode_t mode)
54 : File(name.str(), flags, mode) {}
55
56File::File(File&& other) noexcept : fd_(other.fd_), ownsFd_(other.ownsFd_) {
57 other.release();
58}
59
60File& File::operator=(File&& other) {
61 closeNoThrow();
62 swap(other);
63 return *this;
64}
65
66File::~File() {
67 auto fd = fd_;
68 if (!closeNoThrow()) { // ignore most errors
69 DCHECK_NE(errno, EBADF)
70 << "closing fd " << fd << ", it may already "
71 << "have been closed. Another time, this might close the wrong FD.";
72 }
73}
74
75/* static */ File File::temporary() {
76 // make a temp file with tmpfile(), dup the fd, then return it in a File.
77 FILE* tmpFile = tmpfile();
78 checkFopenError(tmpFile, "tmpfile() failed");
79 SCOPE_EXIT {
80 fclose(tmpFile);
81 };
82
83 int fd = ::dup(fileno(tmpFile));
84 checkUnixError(fd, "dup() failed");
85
86 return File(fd, true);
87}
88
89int File::release() noexcept {
90 int released = fd_;
91 fd_ = -1;
92 ownsFd_ = false;
93 return released;
94}
95
96void File::swap(File& other) noexcept {
97 using std::swap;
98 swap(fd_, other.fd_);
99 swap(ownsFd_, other.ownsFd_);
100}
101
102void swap(File& a, File& b) noexcept {
103 a.swap(b);
104}
105
106File File::dup() const {
107 if (fd_ != -1) {
108 int fd = ::dup(fd_);
109 checkUnixError(fd, "dup() failed");
110
111 return File(fd, true);
112 }
113
114 return File();
115}
116
117void File::close() {
118 if (!closeNoThrow()) {
119 throwSystemError("close() failed");
120 }
121}
122
123bool File::closeNoThrow() {
124 int r = ownsFd_ ? ::close(fd_) : 0;
125 release();
126 return r == 0;
127}
128
129void File::lock() {
130 doLock(LOCK_EX);
131}
132bool File::try_lock() {
133 return doTryLock(LOCK_EX);
134}
135void File::lock_shared() {
136 doLock(LOCK_SH);
137}
138bool File::try_lock_shared() {
139 return doTryLock(LOCK_SH);
140}
141
142void File::doLock(int op) {
143 checkUnixError(flockNoInt(fd_, op), "flock() failed (lock)");
144}
145
146bool File::doTryLock(int op) {
147 int r = flockNoInt(fd_, op | LOCK_NB);
148 // flock returns EWOULDBLOCK if already locked
149 if (r == -1 && errno == EWOULDBLOCK) {
150 return false;
151 }
152 checkUnixError(r, "flock() failed (try_lock)");
153 return true;
154}
155
156void File::unlock() {
157 checkUnixError(flockNoInt(fd_, LOCK_UN), "flock() failed (unlock)");
158}
159void File::unlock_shared() {
160 unlock();
161}
162
163} // namespace folly
164