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