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 | |
31 | namespace folly { |
32 | |
33 | File::File() noexcept : fd_(-1), ownsFd_(false) {} |
34 | |
35 | File::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 | |
40 | File::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 | |
50 | File::File(const std::string& name, int flags, mode_t mode) |
51 | : File(name.c_str(), flags, mode) {} |
52 | |
53 | File::File(StringPiece name, int flags, mode_t mode) |
54 | : File(name.str(), flags, mode) {} |
55 | |
56 | File::File(File&& other) noexcept : fd_(other.fd_), ownsFd_(other.ownsFd_) { |
57 | other.release(); |
58 | } |
59 | |
60 | File& File::operator=(File&& other) { |
61 | closeNoThrow(); |
62 | swap(other); |
63 | return *this; |
64 | } |
65 | |
66 | File::~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 | |
89 | int File::release() noexcept { |
90 | int released = fd_; |
91 | fd_ = -1; |
92 | ownsFd_ = false; |
93 | return released; |
94 | } |
95 | |
96 | void File::swap(File& other) noexcept { |
97 | using std::swap; |
98 | swap(fd_, other.fd_); |
99 | swap(ownsFd_, other.ownsFd_); |
100 | } |
101 | |
102 | void swap(File& a, File& b) noexcept { |
103 | a.swap(b); |
104 | } |
105 | |
106 | File 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 | |
117 | void File::close() { |
118 | if (!closeNoThrow()) { |
119 | throwSystemError("close() failed" ); |
120 | } |
121 | } |
122 | |
123 | bool File::closeNoThrow() { |
124 | int r = ownsFd_ ? ::close(fd_) : 0; |
125 | release(); |
126 | return r == 0; |
127 | } |
128 | |
129 | void File::lock() { |
130 | doLock(LOCK_EX); |
131 | } |
132 | bool File::try_lock() { |
133 | return doTryLock(LOCK_EX); |
134 | } |
135 | void File::lock_shared() { |
136 | doLock(LOCK_SH); |
137 | } |
138 | bool File::try_lock_shared() { |
139 | return doTryLock(LOCK_SH); |
140 | } |
141 | |
142 | void File::doLock(int op) { |
143 | checkUnixError(flockNoInt(fd_, op), "flock() failed (lock)" ); |
144 | } |
145 | |
146 | bool 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 | |
156 | void File::unlock() { |
157 | checkUnixError(flockNoInt(fd_, LOCK_UN), "flock() failed (unlock)" ); |
158 | } |
159 | void File::unlock_shared() { |
160 | unlock(); |
161 | } |
162 | |
163 | } // namespace folly |
164 | |