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
28using namespace bcc;
29
30#ifdef _WIN32
31// TODO: Fix flock usage under windows
32#define LOCK_SH 0
33#define LOCK_EX 0
34#define LOCK_NB 0
35#define LOCK_UN 0
36
37int flock(int fd, int operation) {
38  return 0;
39}
40#endif  // _WIN32
41
42FileBase::FileBase(const std::string &pFilename,
43                   unsigned pOpenFlags,
44                   unsigned pFlags)
45  : mFD(-1),
46    mError(),
47    mName(pFilename), mOpenFlags(pOpenFlags),
48    mShouldUnlock(false),
49    mShouldDelete(false) {
50  // Process pFlags
51#ifdef O_BINARY
52  if (pFlags & kBinary) {
53    mOpenFlags |= O_BINARY;
54  }
55#endif
56  if (pFlags & kTruncate) {
57    mOpenFlags |= O_TRUNC;
58  }
59
60  if (pFlags & kAppend) {
61    mOpenFlags |= O_APPEND;
62  }
63
64  if (pFlags & kDeleteOnClose) {
65    mShouldDelete = true;
66  }
67
68  // Open the file.
69  open();
70
71  return;
72}
73
74FileBase::~FileBase() {
75  close();
76}
77
78bool FileBase::open() {
79  do {
80    // FIXME: Hard-coded permissions (0644) for newly created file should be
81    //        removed and provide a way to let the user configure the value.
82    mFD = ::open(mName.c_str(), mOpenFlags, 0644);
83    if (mFD > 0) {
84      return true;
85    }
86
87    // Some errors occurred ...
88    if (errno != EINTR) {
89      detectError();
90      return false;
91    }
92  } while (true);
93  // unreachable
94}
95
96
97bool FileBase::checkFileIntegrity() {
98  // Check the file integrity by examining whether the inode referring to the mFD
99  // and to the file mName are the same.
100  struct stat fd_stat, file_stat;
101
102  // Get the file status of file descriptor mFD.
103  do {
104    if (::fstat(mFD, &fd_stat) == 0) {
105      break;
106    } else if (errno != EINTR) {
107      detectError();
108      return false;
109    }
110  } while (true);
111
112  // Get the file status of file mName.
113  do {
114    if (::stat(mName.c_str(), &file_stat) == 0) {
115      break;
116    } else if (errno != EINTR) {
117      detectError();
118      return false;
119    }
120  } while (true);
121
122  return ((fd_stat.st_dev == file_stat.st_dev) &&
123          (fd_stat.st_ino == file_stat.st_ino));
124}
125
126void FileBase::detectError() {
127  // Read error from errno.
128  mError.assign(errno, std::generic_category());
129}
130
131bool FileBase::lock(enum LockModeEnum pMode,
132                    bool pNonblocking,
133                    unsigned pMaxRetry,
134                    useconds_t pRetryInterval) {
135  int lock_operation;
136  unsigned retry = 0;
137
138  // Check the state.
139  if ((mFD < 0) || hasError()) {
140    return false;
141  }
142
143  // Return immediately if it's already locked.
144  if (mShouldUnlock) {
145    return true;
146  }
147
148  // Determine the lock operation (2nd argument) to the flock().
149  if (pMode == kReadLock) {
150    lock_operation = LOCK_SH;
151  } else if (pMode == kWriteLock) {
152    lock_operation = LOCK_EX;
153  } else {
154    mError = std::make_error_code(std::errc::invalid_argument);
155    return false;
156  }
157
158  if (pNonblocking) {
159    lock_operation |= LOCK_NB;
160  }
161
162  do {
163    if (::flock(mFD, lock_operation) == 0) {
164      mShouldUnlock = true;
165      // Here we got a lock but we need to check whether the mFD still
166      // "represents" the filename (mName) we opened in the contructor. This
167      // check may failed when another process deleted the original file mFD
168      // mapped when we were trying to obtain the lock on the file.
169      if (!checkFileIntegrity()) {
170        if (hasError() || !reopen()) {
171          // Error occurred when check the file integrity or re-open the file.
172          return false;
173        } else {
174          // Wait a while before the next try.
175          ::usleep(pRetryInterval);
176          retry++;
177          continue;
178        }
179      }
180
181      return true;
182    }
183
184    // flock() was not performed successfully. Check the errno to see whether
185    // it's retry-able.
186    if (errno == EINTR) {
187      // flock() was interrupted by delivery of a signal. Restart without
188      // decrement the retry counter.
189      continue;
190    } else if (errno == EWOULDBLOCK) {
191      // The file descriptor was locked by others, wait for a while before next
192      // retry.
193      retry++;
194      ::usleep(pRetryInterval);
195    } else {
196      // There's a fatal error occurs when perform flock(). Return immediately
197      // without further retry.
198      detectError();
199      return false;
200    }
201  } while (retry <= pMaxRetry);
202
203  return false;
204}
205
206void FileBase::unlock() {
207  if (mFD < 0) {
208    return;
209  }
210
211  do {
212    if (::flock(mFD, LOCK_UN) == 0) {
213      mShouldUnlock = false;
214      return;
215    }
216  } while (errno == EINTR);
217
218  detectError();
219  return;
220}
221
222void FileBase::close() {
223  if (mShouldUnlock) {
224    unlock();
225    mShouldUnlock = false;
226  }
227  if (mFD > 0) {
228    ::close(mFD);
229    mFD = -1;
230  }
231  if (mShouldDelete) {
232    int res = ::remove(mName.c_str());
233    if (res != 0) {
234      ALOGE("Failed to remove file: %s - %s", mName.c_str(), ::strerror(res));
235    }
236  }
237  return;
238}
239