blob: 7d8f8a62544e2b1745a9721f3a0cfd4783a9db19 [file] [log] [blame]
Zonr Chang4f94c522012-04-05 15:09:28 +08001/*
2 * Copyright 2012, The Android Open Source Project
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
Jean-Luc Brouilleta2dd52f2017-02-16 20:57:26 -080017#include "FileBase.h"
18#include "Log.h"
Stephen Hines331310e2012-10-26 19:27:55 -070019
Zonr Chang4f94c522012-04-05 15:09:28 +080020#include <sys/file.h>
21#include <sys/stat.h>
22#include <unistd.h>
23
24#include <cerrno>
Stephen Hines331310e2012-10-26 19:27:55 -070025#include <cstring>
Zonr Chang4f94c522012-04-05 15:09:28 +080026#include <new>
27
Zonr Chang4f94c522012-04-05 15:09:28 +080028using namespace bcc;
29
Stephen Hines48cd7452013-07-30 22:33:44 -070030#ifdef _WIN32
31// TODO: Fix flock usage under windows
32#define LOCK_SH 0
33#define LOCK_EX 0
34#define LOCK_NB 0
35#define LOCK_UN 0
36
37int flock(int fd, int operation) {
38 return 0;
39}
40#endif // _WIN32
41
Zonr Chang4f94c522012-04-05 15:09:28 +080042FileBase::FileBase(const std::string &pFilename,
43 unsigned pOpenFlags,
44 unsigned pFlags)
45 : mFD(-1),
46 mError(),
47 mName(pFilename), mOpenFlags(pOpenFlags),
Stephen Hines331310e2012-10-26 19:27:55 -070048 mShouldUnlock(false),
49 mShouldDelete(false) {
Zonr Chang4f94c522012-04-05 15:09:28 +080050 // Process pFlags
51#ifdef O_BINARY
52 if (pFlags & kBinary) {
53 mOpenFlags |= O_BINARY;
54 }
55#endif
Shih-wei Liaoc02eae62012-07-22 16:23:32 -070056 if (pFlags & kTruncate) {
57 mOpenFlags |= O_TRUNC;
58 }
Zonr Chang4f94c522012-04-05 15:09:28 +080059
Shih-wei Liaob2b8c642012-07-22 16:25:02 -070060 if (pFlags & kAppend) {
61 mOpenFlags |= O_APPEND;
62 }
63
Stephen Hines331310e2012-10-26 19:27:55 -070064 if (pFlags & kDeleteOnClose) {
65 mShouldDelete = true;
66 }
67
Zonr Chang4f94c522012-04-05 15:09:28 +080068 // Open the file.
69 open();
70
71 return;
72}
73
74FileBase::~FileBase() {
75 close();
76}
77
78bool FileBase::open() {
79 do {
80 // FIXME: Hard-coded permissions (0644) for newly created file should be
81 // removed and provide a way to let the user configure the value.
82 mFD = ::open(mName.c_str(), mOpenFlags, 0644);
83 if (mFD > 0) {
84 return true;
85 }
86
87 // Some errors occurred ...
88 if (errno != EINTR) {
89 detectError();
90 return false;
91 }
92 } while (true);
93 // unreachable
94}
95
96
97bool FileBase::checkFileIntegrity() {
98 // Check the file integrity by examining whether the inode referring to the mFD
99 // and to the file mName are the same.
100 struct stat fd_stat, file_stat;
101
102 // Get the file status of file descriptor mFD.
103 do {
104 if (::fstat(mFD, &fd_stat) == 0) {
105 break;
106 } else if (errno != EINTR) {
107 detectError();
108 return false;
109 }
110 } while (true);
111
112 // Get the file status of file mName.
113 do {
114 if (::stat(mName.c_str(), &file_stat) == 0) {
115 break;
116 } else if (errno != EINTR) {
117 detectError();
118 return false;
119 }
120 } while (true);
121
122 return ((fd_stat.st_dev == file_stat.st_dev) &&
123 (fd_stat.st_ino == file_stat.st_ino));
124}
125
126void FileBase::detectError() {
127 // Read error from errno.
Stephen Hinesd0993af2014-07-15 16:49:25 -0700128 mError.assign(errno, std::generic_category());
Zonr Chang4f94c522012-04-05 15:09:28 +0800129}
130
131bool FileBase::lock(enum LockModeEnum pMode,
132 bool pNonblocking,
133 unsigned pMaxRetry,
134 useconds_t pRetryInterval) {
135 int lock_operation;
136 unsigned retry = 0;
137
138 // Check the state.
139 if ((mFD < 0) || hasError()) {
140 return false;
141 }
142
143 // Return immediately if it's already locked.
144 if (mShouldUnlock) {
145 return true;
146 }
147
148 // Determine the lock operation (2nd argument) to the flock().
149 if (pMode == kReadLock) {
150 lock_operation = LOCK_SH;
151 } else if (pMode == kWriteLock) {
152 lock_operation = LOCK_EX;
153 } else {
Stephen Hinesd0993af2014-07-15 16:49:25 -0700154 mError = std::make_error_code(std::errc::invalid_argument);
Zonr Chang4f94c522012-04-05 15:09:28 +0800155 return false;
156 }
157
158 if (pNonblocking) {
159 lock_operation |= LOCK_NB;
160 }
161
162 do {
163 if (::flock(mFD, lock_operation) == 0) {
164 mShouldUnlock = true;
165 // Here we got a lock but we need to check whether the mFD still
166 // "represents" the filename (mName) we opened in the contructor. This
167 // check may failed when another process deleted the original file mFD
168 // mapped when we were trying to obtain the lock on the file.
169 if (!checkFileIntegrity()) {
170 if (hasError() || !reopen()) {
171 // Error occurred when check the file integrity or re-open the file.
172 return false;
173 } else {
174 // Wait a while before the next try.
175 ::usleep(pRetryInterval);
176 retry++;
177 continue;
178 }
179 }
180
181 return true;
182 }
183
184 // flock() was not performed successfully. Check the errno to see whether
185 // it's retry-able.
186 if (errno == EINTR) {
187 // flock() was interrupted by delivery of a signal. Restart without
188 // decrement the retry counter.
189 continue;
190 } else if (errno == EWOULDBLOCK) {
191 // The file descriptor was locked by others, wait for a while before next
192 // retry.
193 retry++;
194 ::usleep(pRetryInterval);
195 } else {
196 // There's a fatal error occurs when perform flock(). Return immediately
197 // without further retry.
198 detectError();
199 return false;
200 }
201 } while (retry <= pMaxRetry);
202
203 return false;
204}
205
206void FileBase::unlock() {
207 if (mFD < 0) {
208 return;
209 }
210
211 do {
212 if (::flock(mFD, LOCK_UN) == 0) {
213 mShouldUnlock = false;
214 return;
215 }
216 } while (errno == EINTR);
217
218 detectError();
219 return;
220}
221
Zonr Chang4f94c522012-04-05 15:09:28 +0800222void FileBase::close() {
223 if (mShouldUnlock) {
224 unlock();
225 mShouldUnlock = false;
226 }
227 if (mFD > 0) {
228 ::close(mFD);
229 mFD = -1;
230 }
Stephen Hines331310e2012-10-26 19:27:55 -0700231 if (mShouldDelete) {
232 int res = ::remove(mName.c_str());
233 if (res != 0) {
234 ALOGE("Failed to remove file: %s - %s", mName.c_str(), ::strerror(res));
235 }
236 }
Zonr Chang4f94c522012-04-05 15:09:28 +0800237 return;
238}