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