• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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