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/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 writeNoInt(int fd, const void* buf, size_t count) {
115 return wrapNoInt(write, fd, buf, count);
116}
117
118ssize_t pwriteNoInt(int fd, const void* buf, size_t count, off_t offset) {
119 return wrapNoInt(pwrite, fd, buf, count, offset);
120}
121
122ssize_t writevNoInt(int fd, const iovec* iov, int count) {
123 return wrapNoInt(writev, fd, iov, count);
124}
125
126ssize_t readFull(int fd, void* buf, size_t count) {
127 return wrapFull(read, fd, buf, count);
128}
129
130ssize_t preadFull(int fd, void* buf, size_t count, off_t offset) {
131 return wrapFull(pread, fd, buf, count, offset);
132}
133
134ssize_t writeFull(int fd, const void* buf, size_t count) {
135 return wrapFull(write, fd, const_cast<void*>(buf), count);
136}
137
138ssize_t pwriteFull(int fd, const void* buf, size_t count, off_t offset) {
139 return wrapFull(pwrite, fd, const_cast<void*>(buf), count, offset);
140}
141
142ssize_t readvFull(int fd, iovec* iov, int count) {
143 return wrapvFull(readv, fd, iov, count);
144}
145
146ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset) {
147 return wrapvFull(preadv, fd, iov, count, offset);
148}
149
150ssize_t writevFull(int fd, iovec* iov, int count) {
151 return wrapvFull(writev, fd, iov, count);
152}
153
154ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset) {
155 return wrapvFull(pwritev, fd, iov, count, offset);
156}
157
158int writeFileAtomicNoThrow(
159 StringPiece filename,
160 iovec* iov,
161 int count,
162 mode_t permissions) {
163 // We write the data to a temporary file name first, then atomically rename
164 // it into place. This ensures that the file contents will always be valid,
165 // even if we crash or are killed partway through writing out data.
166 //
167 // Create a buffer that will contain two things:
168 // - A nul-terminated version of the filename
169 // - The temporary file name
170 std::vector<char> pathBuffer;
171 // Note that we have to explicitly pass in the size here to make
172 // sure the nul byte gets included in the data.
173 constexpr folly::StringPiece suffix(".XXXXXX\0", 8);
174 pathBuffer.resize((2 * filename.size()) + 1 + suffix.size());
175 // Copy in the filename and then a nul terminator
176 memcpy(pathBuffer.data(), filename.data(), filename.size());
177 pathBuffer[filename.size()] = '\0';
178 const char* const filenameCStr = pathBuffer.data();
179 // Now prepare the temporary path template
180 char* const tempPath = pathBuffer.data() + filename.size() + 1;
181 memcpy(tempPath, filename.data(), filename.size());
182 memcpy(tempPath + filename.size(), suffix.data(), suffix.size());
183
184 auto tmpFD = mkstemp(tempPath);
185 if (tmpFD == -1) {
186 return errno;
187 }
188 bool success = false;
189 SCOPE_EXIT {
190 if (tmpFD != -1) {
191 close(tmpFD);
192 }
193 if (!success) {
194 unlink(tempPath);
195 }
196 };
197
198 auto rc = writevFull(tmpFD, iov, count);
199 if (rc == -1) {
200 return errno;
201 }
202
203 rc = fchmod(tmpFD, permissions);
204 if (rc == -1) {
205 return errno;
206 }
207
208 // Close the file before renaming to make sure all data has
209 // been successfully written.
210 rc = close(tmpFD);
211 tmpFD = -1;
212 if (rc == -1) {
213 return errno;
214 }
215
216 rc = rename(tempPath, filenameCStr);
217 if (rc == -1) {
218 return errno;
219 }
220 success = true;
221 return 0;
222}
223
224void writeFileAtomic(
225 StringPiece filename,
226 iovec* iov,
227 int count,
228 mode_t permissions) {
229 auto rc = writeFileAtomicNoThrow(filename, iov, count, permissions);
230 if (rc != 0) {
231 auto msg = std::string(__func__) + "() failed to update " + filename.str();
232 throw std::system_error(rc, std::generic_category(), msg);
233 }
234}
235
236void writeFileAtomic(StringPiece filename, ByteRange data, mode_t permissions) {
237 iovec iov;
238 iov.iov_base = const_cast<unsigned char*>(data.data());
239 iov.iov_len = data.size();
240 writeFileAtomic(filename, &iov, 1, permissions);
241}
242
243void writeFileAtomic(
244 StringPiece filename,
245 StringPiece data,
246 mode_t permissions) {
247 writeFileAtomic(filename, ByteRange(data), permissions);
248}
249
250} // namespace folly
251