FileBase.cpp revision 8486498c5f6083b8ab0f7aebb1944eaed33bd290
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
224void FileBase::close() {
225  if (mShouldUnlock) {
226    unlock();
227    mShouldUnlock = false;
228  }
229  if (mFD > 0) {
230    ::close(mFD);
231    mFD = -1;
232  }
233  if (mShouldDelete) {
234    int res = ::remove(mName.c_str());
235    if (res != 0) {
236      ALOGE("Failed to remove file: %s - %s", mName.c_str(), ::strerror(res));
237    }
238  }
239  return;
240}
241