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