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