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