FileBase.cpp revision 331310e1f3f86a795f78e42b3f03558a43829f09
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
31using namespace bcc;
32
33FileBase::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
65FileBase::~FileBase() {
66  close();
67}
68
69bool 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
88bool 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
117void FileBase::detectError() {
118  // Read error from errno.
119  mError.assign(errno, llvm::posix_category());
120}
121
122bool 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
197void 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
213android::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
234size_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
252off_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
268off_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
284void 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