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