FileBase.cpp revision c72c4ddfcd79c74f70713da91a69569451b5c19e
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
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 examining whether the inode referring 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}
280