1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
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/FileUtil.h>
18
19#include <cerrno>
20#include <string>
21#include <system_error>
22#include <vector>
23
24#include <folly/detail/FileUtilDetail.h>
25#include <folly/net/NetOps.h>
26#include <folly/portability/Fcntl.h>
27#include <folly/portability/Sockets.h>
28#include <folly/portability/Stdlib.h>
29#include <folly/portability/SysFile.h>
30#include <folly/portability/SysStat.h>
31
32namespace folly {
33
34using namespace fileutil_detail;
35
36int openNoInt(const char* name, int flags, mode_t mode) {
37 return int(wrapNoInt(open, name, flags, mode));
38}
39
40static int filterCloseReturn(int r) {
41 // Ignore EINTR. On Linux, close() may only return EINTR after the file
42 // descriptor has been closed, so you must not retry close() on EINTR --
43 // in the best case, you'll get EBADF, and in the worst case, you'll end up
44 // closing a different file (one opened from another thread).
45 //
46 // Interestingly enough, the Single Unix Specification says that the state
47 // of the file descriptor is unspecified if close returns EINTR. In that
48 // case, the safe thing to do is also not to retry close() -- leaking a file
49 // descriptor is definitely better than closing the wrong file.
50 if (r == -1 && errno == EINTR) {
51 return 0;
52 }
53 return r;
54}
55
56int closeNoInt(int fd) {
57 return filterCloseReturn(close(fd));
58}
59
60int closeNoInt(NetworkSocket fd) {
61 return filterCloseReturn(netops::close(fd));
62}
63
64int fsyncNoInt(int fd) {
65 return int(wrapNoInt(fsync, fd));
66}
67
68int dupNoInt(int fd) {
69 return int(wrapNoInt(dup, fd));
70}
71
72int dup2NoInt(int oldfd, int newfd) {
73 return int(wrapNoInt(dup2, oldfd, newfd));
74}
75
76int fdatasyncNoInt(int fd) {
77#if defined(__APPLE__)
78 return int(wrapNoInt(fcntl, fd, F_FULLFSYNC));
79#elif defined(__FreeBSD__) || defined(_MSC_VER)
80 return int(wrapNoInt(fsync, fd));
81#else
82 return int(wrapNoInt(fdatasync, fd));
83#endif
84}
85
86int ftruncateNoInt(int fd, off_t len) {
87 return int(wrapNoInt(ftruncate, fd, len));
88}
89
90int truncateNoInt(const char* path, off_t len) {
91 return int(wrapNoInt(truncate, path, len));
92}
93
94int flockNoInt(int fd, int operation) {
95 return int(wrapNoInt(flock, fd, operation));
96}
97
98int shutdownNoInt(NetworkSocket fd, int how) {
99 return int(wrapNoInt(netops::shutdown, fd, how));
100}
101
102ssize_t readNoInt(int fd, void* buf, size_t count) {
103 return wrapNoInt(read, fd, buf, count);
104}
105
106ssize_t preadNoInt(int fd, void* buf, size_t count, off_t offset) {
107 return wrapNoInt(pread, fd, buf, count, offset);
108}
109
110ssize_t readvNoInt(int fd, const iovec* iov, int count) {
111 return wrapNoInt(readv, fd, iov, count);
112}
113
114ssize_t preadvNoInt(int fd, const iovec* iov, int count, off_t offset) {
115 return wrapNoInt(preadv, fd, iov, count, offset);
116}
117
118ssize_t writeNoInt(int fd, const void* buf, size_t count) {
119 return wrapNoInt(write, fd, buf, count);
120}
121
122ssize_t pwriteNoInt(int fd, const void* buf, size_t count, off_t offset) {
123 return wrapNoInt(pwrite, fd, buf, count, offset);
124}
125
126ssize_t writevNoInt(int fd, const iovec* iov, int count) {
127 return wrapNoInt(writev, fd, iov, count);
128}
129
130ssize_t pwritevNoInt(int fd, const iovec* iov, int count, off_t offset) {
131 return wrapNoInt(pwritev, fd, iov, count, offset);
132}
133
134ssize_t readFull(int fd, void* buf, size_t count) {
135 return wrapFull(read, fd, buf, count);
136}
137
138ssize_t preadFull(int fd, void* buf, size_t count, off_t offset) {
139 return wrapFull(pread, fd, buf, count, offset);
140}
141
142ssize_t writeFull(int fd, const void* buf, size_t count) {
143 return wrapFull(write, fd, const_cast<void*>(buf), count);
144}
145
146ssize_t pwriteFull(int fd, const void* buf, size_t count, off_t offset) {
147 return wrapFull(pwrite, fd, const_cast<void*>(buf), count, offset);
148}
149
150ssize_t readvFull(int fd, iovec* iov, int count) {
151 return wrapvFull(readv, fd, iov, count);
152}
153
154ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset) {
155 return wrapvFull(preadv, fd, iov, count, offset);
156}
157
158ssize_t writevFull(int fd, iovec* iov, int count) {
159 return wrapvFull(writev, fd, iov, count);
160}
161
162ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset) {
163 return wrapvFull(pwritev, fd, iov, count, offset);
164}
165
166int writeFileAtomicNoThrow(
167 StringPiece filename,
168 iovec* iov,
169 int count,
170 mode_t permissions) {
171 // We write the data to a temporary file name first, then atomically rename
172 // it into place. This ensures that the file contents will always be valid,
173 // even if we crash or are killed partway through writing out data.
174 //
175 // Create a buffer that will contain two things:
176 // - A nul-terminated version of the filename
177 // - The temporary file name
178 std::vector<char> pathBuffer;
179 // Note that we have to explicitly pass in the size here to make
180 // sure the nul byte gets included in the data.
181 constexpr folly::StringPiece suffix(".XXXXXX\0", 8);
182 pathBuffer.resize((2 * filename.size()) + 1 + suffix.size());
183 // Copy in the filename and then a nul terminator
184 memcpy(pathBuffer.data(), filename.data(), filename.size());
185 pathBuffer[filename.size()] = '\0';
186 const char* const filenameCStr = pathBuffer.data();
187 // Now prepare the temporary path template
188 char* const tempPath = pathBuffer.data() + filename.size() + 1;
189 memcpy(tempPath, filename.data(), filename.size());
190 memcpy(tempPath + filename.size(), suffix.data(), suffix.size());
191
192 auto tmpFD = mkstemp(tempPath);
193 if (tmpFD == -1) {
194 return errno;
195 }
196 bool success = false;
197 SCOPE_EXIT {
198 if (tmpFD != -1) {
199 close(tmpFD);
200 }
201 if (!success) {
202 unlink(tempPath);
203 }
204 };
205
206 auto rc = writevFull(tmpFD, iov, count);
207 if (rc == -1) {
208 return errno;
209 }
210
211 rc = fchmod(tmpFD, permissions);
212 if (rc == -1) {
213 return errno;
214 }
215
216 // Close the file before renaming to make sure all data has
217 // been successfully written.
218 rc = close(tmpFD);
219 tmpFD = -1;
220 if (rc == -1) {
221 return errno;
222 }
223
224 rc = rename(tempPath, filenameCStr);
225 if (rc == -1) {
226 return errno;
227 }
228 success = true;
229 return 0;
230}
231
232void writeFileAtomic(
233 StringPiece filename,
234 iovec* iov,
235 int count,
236 mode_t permissions) {
237 auto rc = writeFileAtomicNoThrow(filename, iov, count, permissions);
238 if (rc != 0) {
239 auto msg = std::string(__func__) + "() failed to update " + filename.str();
240 throw std::system_error(rc, std::generic_category(), msg);
241 }
242}
243
244void writeFileAtomic(StringPiece filename, ByteRange data, mode_t permissions) {
245 iovec iov;
246 iov.iov_base = const_cast<unsigned char*>(data.data());
247 iov.iov_len = data.size();
248 writeFileAtomic(filename, &iov, 1, permissions);
249}
250
251void writeFileAtomic(
252 StringPiece filename,
253 StringPiece data,
254 mode_t permissions) {
255 writeFileAtomic(filename, ByteRange(data), permissions);
256}
257
258} // namespace folly
259