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