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 using 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 
flock(int fd,int operation)37 int flock(int fd, int operation) {
38   return 0;
39 }
40 #endif  // _WIN32
41 
FileBase(const std::string & pFilename,unsigned pOpenFlags,unsigned pFlags)42 FileBase::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 
~FileBase()74 FileBase::~FileBase() {
75   close();
76 }
77 
open()78 bool 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 
checkFileIntegrity()97 bool 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 
detectError()126 void FileBase::detectError() {
127   // Read error from errno.
128   mError.assign(errno, std::generic_category());
129 }
130 
lock(enum LockModeEnum pMode,bool pNonblocking,unsigned pMaxRetry,useconds_t pRetryInterval)131 bool 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 
unlock()206 void 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 
close()222 void 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