• 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 <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