1 /*
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
17 #include "bcc/Support/FileBase.h"
18
19 #include "bcc/Support/Log.h"
20
21 #include <sys/file.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24
25 #include <cerrno>
26 #include <cstring>
27 #include <new>
28
29 #include <utils/FileMap.h>
30
31 using namespace bcc;
32
33 #ifdef _WIN32
34 // TODO: Fix flock usage under windows
35 #define LOCK_SH 0
36 #define LOCK_EX 0
37 #define LOCK_NB 0
38 #define LOCK_UN 0
39
flock(int fd,int operation)40 int flock(int fd, int operation) {
41 return 0;
42 }
43 #endif // _WIN32
44
FileBase(const std::string & pFilename,unsigned pOpenFlags,unsigned pFlags)45 FileBase::FileBase(const std::string &pFilename,
46 unsigned pOpenFlags,
47 unsigned pFlags)
48 : mFD(-1),
49 mError(),
50 mName(pFilename), mOpenFlags(pOpenFlags),
51 mShouldUnlock(false),
52 mShouldDelete(false) {
53 // Process pFlags
54 #ifdef O_BINARY
55 if (pFlags & kBinary) {
56 mOpenFlags |= O_BINARY;
57 }
58 #endif
59 if (pFlags & kTruncate) {
60 mOpenFlags |= O_TRUNC;
61 }
62
63 if (pFlags & kAppend) {
64 mOpenFlags |= O_APPEND;
65 }
66
67 if (pFlags & kDeleteOnClose) {
68 mShouldDelete = true;
69 }
70
71 // Open the file.
72 open();
73
74 return;
75 }
76
~FileBase()77 FileBase::~FileBase() {
78 close();
79 }
80
open()81 bool FileBase::open() {
82 do {
83 // FIXME: Hard-coded permissions (0644) for newly created file should be
84 // removed and provide a way to let the user configure the value.
85 mFD = ::open(mName.c_str(), mOpenFlags, 0644);
86 if (mFD > 0) {
87 return true;
88 }
89
90 // Some errors occurred ...
91 if (errno != EINTR) {
92 detectError();
93 return false;
94 }
95 } while (true);
96 // unreachable
97 }
98
99
checkFileIntegrity()100 bool FileBase::checkFileIntegrity() {
101 // Check the file integrity by examining whether the inode referring to the mFD
102 // and to the file mName are the same.
103 struct stat fd_stat, file_stat;
104
105 // Get the file status of file descriptor mFD.
106 do {
107 if (::fstat(mFD, &fd_stat) == 0) {
108 break;
109 } else if (errno != EINTR) {
110 detectError();
111 return false;
112 }
113 } while (true);
114
115 // Get the file status of file mName.
116 do {
117 if (::stat(mName.c_str(), &file_stat) == 0) {
118 break;
119 } else if (errno != EINTR) {
120 detectError();
121 return false;
122 }
123 } while (true);
124
125 return ((fd_stat.st_dev == file_stat.st_dev) &&
126 (fd_stat.st_ino == file_stat.st_ino));
127 }
128
detectError()129 void FileBase::detectError() {
130 // Read error from errno.
131 mError.assign(errno, std::generic_category());
132 }
133
lock(enum LockModeEnum pMode,bool pNonblocking,unsigned pMaxRetry,useconds_t pRetryInterval)134 bool FileBase::lock(enum LockModeEnum pMode,
135 bool pNonblocking,
136 unsigned pMaxRetry,
137 useconds_t pRetryInterval) {
138 int lock_operation;
139 unsigned retry = 0;
140
141 // Check the state.
142 if ((mFD < 0) || hasError()) {
143 return false;
144 }
145
146 // Return immediately if it's already locked.
147 if (mShouldUnlock) {
148 return true;
149 }
150
151 // Determine the lock operation (2nd argument) to the flock().
152 if (pMode == kReadLock) {
153 lock_operation = LOCK_SH;
154 } else if (pMode == kWriteLock) {
155 lock_operation = LOCK_EX;
156 } else {
157 mError = std::make_error_code(std::errc::invalid_argument);
158 return false;
159 }
160
161 if (pNonblocking) {
162 lock_operation |= LOCK_NB;
163 }
164
165 do {
166 if (::flock(mFD, lock_operation) == 0) {
167 mShouldUnlock = true;
168 // Here we got a lock but we need to check whether the mFD still
169 // "represents" the filename (mName) we opened in the contructor. This
170 // check may failed when another process deleted the original file mFD
171 // mapped when we were trying to obtain the lock on the file.
172 if (!checkFileIntegrity()) {
173 if (hasError() || !reopen()) {
174 // Error occurred when check the file integrity or re-open the file.
175 return false;
176 } else {
177 // Wait a while before the next try.
178 ::usleep(pRetryInterval);
179 retry++;
180 continue;
181 }
182 }
183
184 return true;
185 }
186
187 // flock() was not performed successfully. Check the errno to see whether
188 // it's retry-able.
189 if (errno == EINTR) {
190 // flock() was interrupted by delivery of a signal. Restart without
191 // decrement the retry counter.
192 continue;
193 } else if (errno == EWOULDBLOCK) {
194 // The file descriptor was locked by others, wait for a while before next
195 // retry.
196 retry++;
197 ::usleep(pRetryInterval);
198 } else {
199 // There's a fatal error occurs when perform flock(). Return immediately
200 // without further retry.
201 detectError();
202 return false;
203 }
204 } while (retry <= pMaxRetry);
205
206 return false;
207 }
208
unlock()209 void FileBase::unlock() {
210 if (mFD < 0) {
211 return;
212 }
213
214 do {
215 if (::flock(mFD, LOCK_UN) == 0) {
216 mShouldUnlock = false;
217 return;
218 }
219 } while (errno == EINTR);
220
221 detectError();
222 return;
223 }
224
createMap(off_t pOffset,size_t pLength,bool pIsReadOnly)225 android::FileMap *FileBase::createMap(off_t pOffset, size_t pLength,
226 bool pIsReadOnly) {
227 if (mFD < 0 || hasError()) {
228 return NULL;
229 }
230
231 android::FileMap *map = new (std::nothrow) android::FileMap();
232 if (map == NULL) {
233 mError = make_error_code(std::errc::not_enough_memory);
234 return NULL;
235 }
236
237 if (!map->create(NULL, mFD, pOffset, pLength, pIsReadOnly)) {
238 detectError();
239 map->release();
240 return NULL;
241 }
242
243 return map;
244 }
245
getSize()246 size_t FileBase::getSize() {
247 if (mFD < 0 || hasError()) {
248 return static_cast<size_t>(-1);
249 }
250
251 struct stat file_stat;
252 do {
253 if (::fstat(mFD, &file_stat) == 0) {
254 break;
255 } else if (errno != EINTR) {
256 detectError();
257 return static_cast<size_t>(-1);
258 }
259 } while (true);
260
261 return file_stat.st_size;
262 }
263
seek(off_t pOffset)264 off_t FileBase::seek(off_t pOffset) {
265 if ((mFD < 0) || hasError()) {
266 return static_cast<off_t>(-1);
267 }
268
269 do {
270 off_t result = ::lseek(mFD, pOffset, SEEK_SET);
271 if (result == pOffset) {
272 return result;
273 }
274 } while (errno == EINTR);
275
276 detectError();
277 return static_cast<off_t>(-1);
278 }
279
tell()280 off_t FileBase::tell() {
281 if ((mFD < 0) || hasError()) {
282 return static_cast<off_t>(-1);
283 }
284
285 do {
286 off_t result = ::lseek(mFD, 0, SEEK_CUR);
287 if (result != static_cast<off_t>(-1)) {
288 return result;
289 }
290 } while (errno == EINTR);
291
292 detectError();
293 return static_cast<off_t>(-1);
294 }
295
close()296 void FileBase::close() {
297 if (mShouldUnlock) {
298 unlock();
299 mShouldUnlock = false;
300 }
301 if (mFD > 0) {
302 ::close(mFD);
303 mFD = -1;
304 }
305 if (mShouldDelete) {
306 int res = ::remove(mName.c_str());
307 if (res != 0) {
308 ALOGE("Failed to remove file: %s - %s", mName.c_str(), ::strerror(res));
309 }
310 }
311 return;
312 }
313