OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. See the AUTHORS file for names of contributors. | |
4 | |
5 #include <errno.h> | |
6 | |
7 #include "base/debug/trace_event.h" | |
8 #include "base/files/file_path.h" | |
9 #include "base/posix/eintr_wrapper.h" | |
10 #include "base/strings/utf_string_conversions.h" | |
11 #include "chromium_logger.h" | |
12 #include "env_chromium_posix.h" | |
13 | |
14 #if defined(OS_WIN) | |
15 #include <io.h> | |
16 #include "base/win/win_util.h" | |
17 #endif | |
18 | |
19 #if defined(OS_POSIX) | |
20 #include <dirent.h> | |
21 #include <fcntl.h> | |
22 #include <sys/resource.h> | |
23 #endif | |
24 | |
25 using namespace leveldb; | |
26 | |
27 namespace leveldb_env { | |
28 | |
29 namespace { | |
30 | |
31 #if (defined(OS_POSIX) && !defined(OS_LINUX)) || defined(OS_WIN) | |
32 // The following are glibc-specific | |
33 | |
34 size_t fread_unlocked(void *ptr, size_t size, size_t n, FILE *file) { | |
35 return fread(ptr, size, n, file); | |
36 } | |
37 | |
38 size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *file) { | |
39 return fwrite(ptr, size, n, file); | |
40 } | |
41 | |
42 int fflush_unlocked(FILE *file) { | |
43 return fflush(file); | |
44 } | |
45 | |
46 #if !defined(OS_ANDROID) | |
47 int fdatasync(int fildes) { | |
48 #if defined(OS_WIN) | |
49 return _commit(fildes); | |
50 #else | |
51 return HANDLE_EINTR(fsync(fildes)); | |
52 #endif | |
53 } | |
54 #endif | |
55 | |
56 #endif | |
57 | |
58 // Wide-char safe fopen wrapper. | |
59 FILE* fopen_internal(const char* fname, const char* mode) { | |
60 #if defined(OS_WIN) | |
61 return _wfopen(UTF8ToUTF16(fname).c_str(), ASCIIToUTF16(mode).c_str()); | |
62 #else | |
63 return fopen(fname, mode); | |
64 #endif | |
65 } | |
66 | |
67 class ChromiumSequentialFile: public SequentialFile { | |
68 private: | |
69 std::string filename_; | |
70 FILE* file_; | |
71 const UMALogger* uma_logger_; | |
72 | |
73 public: | |
74 ChromiumSequentialFile(const std::string& fname, FILE* f, | |
75 const UMALogger* uma_logger) | |
76 : filename_(fname), file_(f), uma_logger_(uma_logger) { } | |
77 virtual ~ChromiumSequentialFile() { fclose(file_); } | |
78 | |
79 virtual Status Read(size_t n, Slice* result, char* scratch) { | |
80 Status s; | |
81 size_t r = fread_unlocked(scratch, 1, n, file_); | |
82 *result = Slice(scratch, r); | |
83 if (r < n) { | |
84 if (feof(file_)) { | |
85 // We leave status as ok if we hit the end of the file | |
86 } else { | |
87 // A partial read with an error: return a non-ok status | |
88 s = MakeIOError(filename_, strerror(errno), kSequentialFileRead, errno); | |
89 uma_logger_->RecordErrorAt(kSequentialFileRead); | |
90 } | |
91 } | |
92 return s; | |
93 } | |
94 | |
95 virtual Status Skip(uint64_t n) { | |
96 if (fseek(file_, n, SEEK_CUR)) { | |
97 int saved_errno = errno; | |
98 uma_logger_->RecordErrorAt(kSequentialFileSkip); | |
99 return MakeIOError( | |
100 filename_, strerror(saved_errno), kSequentialFileSkip, saved_errno); | |
101 } | |
102 return Status::OK(); | |
103 } | |
104 }; | |
105 | |
106 class ChromiumRandomAccessFile: public RandomAccessFile { | |
107 private: | |
108 std::string filename_; | |
109 ::base::PlatformFile file_; | |
110 const UMALogger* uma_logger_; | |
111 | |
112 public: | |
113 ChromiumRandomAccessFile(const std::string& fname, ::base::PlatformFile file, | |
114 const UMALogger* uma_logger) | |
115 : filename_(fname), file_(file), uma_logger_(uma_logger) { } | |
116 virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_); } | |
117 | |
118 virtual Status Read(uint64_t offset, size_t n, Slice* result, | |
119 char* scratch) const { | |
120 Status s; | |
121 int r = ::base::ReadPlatformFile(file_, offset, scratch, n); | |
122 *result = Slice(scratch, (r < 0) ? 0 : r); | |
123 if (r < 0) { | |
124 // An error: return a non-ok status | |
125 s = MakeIOError( | |
126 filename_, "Could not perform read", kRandomAccessFileRead); | |
127 uma_logger_->RecordErrorAt(kRandomAccessFileRead); | |
128 } | |
129 return s; | |
130 } | |
131 }; | |
132 | |
133 } // unnamed namespace | |
134 | |
135 Status MakeIOError(Slice filename, | |
136 const char* message, | |
137 MethodID method, | |
138 int saved_errno) { | |
139 char buf[512]; | |
140 snprintf(buf, | |
141 sizeof(buf), | |
142 "%s (ChromeMethodErrno: %d::%s::%d)", | |
143 message, | |
144 method, | |
145 MethodIDToString(method), | |
146 saved_errno); | |
147 return Status::IOError(filename, buf); | |
148 } | |
149 | |
150 ChromiumWritableFile::ChromiumWritableFile(const std::string& fname, | |
151 FILE* f, | |
152 const UMALogger* uma_logger, | |
153 WriteTracker* tracker, | |
154 bool make_backup) | |
155 : filename_(fname), | |
156 file_(f), | |
157 uma_logger_(uma_logger), | |
158 tracker_(tracker), | |
159 file_type_(kOther), | |
160 make_backup_(make_backup) { | |
161 base::FilePath path = base::FilePath::FromUTF8Unsafe(fname); | |
162 if (FilePathToString(path.BaseName()).find("MANIFEST") == 0) | |
163 file_type_ = kManifest; | |
164 else if (ChromiumEnv::HasTableExtension(path)) | |
165 file_type_ = kTable; | |
166 if (file_type_ != kManifest) | |
167 tracker_->DidCreateNewFile(filename_); | |
168 parent_dir_ = FilePathToString(ChromiumEnv::CreateFilePath(fname).DirName()); | |
169 } | |
170 | |
171 ChromiumWritableFile::~ChromiumWritableFile() { | |
172 if (file_ != NULL) { | |
173 // Ignoring any potential errors | |
174 fclose(file_); | |
175 } | |
176 } | |
177 | |
178 Status ChromiumWritableFile::SyncParent() { | |
179 Status s; | |
180 #if !defined(OS_WIN) | |
181 TRACE_EVENT0("leveldb", "SyncParent"); | |
182 | |
183 int parent_fd = | |
184 HANDLE_EINTR(open(parent_dir_.c_str(), O_RDONLY)); | |
185 if (parent_fd < 0) { | |
186 int saved_errno = errno; | |
187 return MakeIOError( | |
188 parent_dir_, strerror(saved_errno), kSyncParent, saved_errno); | |
189 } | |
190 if (HANDLE_EINTR(fsync(parent_fd)) != 0) { | |
191 int saved_errno = errno; | |
192 s = MakeIOError( | |
193 parent_dir_, strerror(saved_errno), kSyncParent, saved_errno); | |
194 }; | |
195 close(parent_fd); | |
196 #endif | |
197 return s; | |
198 } | |
199 | |
200 Status ChromiumWritableFile::Append(const Slice& data) { | |
201 if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) { | |
202 Status s = SyncParent(); | |
203 if (!s.ok()) | |
204 return s; | |
205 tracker_->DidSyncDir(filename_); | |
206 } | |
207 | |
208 size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_); | |
209 if (r != data.size()) { | |
210 int saved_errno = errno; | |
211 uma_logger_->RecordOSError(kWritableFileAppend, saved_errno); | |
212 return MakeIOError( | |
213 filename_, strerror(saved_errno), kWritableFileAppend, saved_errno); | |
214 } | |
215 return Status::OK(); | |
216 } | |
217 | |
218 Status ChromiumWritableFile::Close() { | |
219 Status result; | |
220 if (fclose(file_) != 0) { | |
221 result = MakeIOError(filename_, strerror(errno), kWritableFileClose, errno); | |
222 uma_logger_->RecordErrorAt(kWritableFileClose); | |
223 } | |
224 file_ = NULL; | |
225 return result; | |
226 } | |
227 | |
228 Status ChromiumWritableFile::Flush() { | |
229 Status result; | |
230 if (HANDLE_EINTR(fflush_unlocked(file_))) { | |
231 int saved_errno = errno; | |
232 result = MakeIOError( | |
233 filename_, strerror(saved_errno), kWritableFileFlush, saved_errno); | |
234 uma_logger_->RecordOSError(kWritableFileFlush, saved_errno); | |
235 } | |
236 return result; | |
237 } | |
238 | |
239 Status ChromiumWritableFile::Sync() { | |
240 TRACE_EVENT0("leveldb", "ChromiumEnvPosix::Sync"); | |
241 Status result; | |
242 int error = 0; | |
243 | |
244 if (HANDLE_EINTR(fflush_unlocked(file_))) | |
245 error = errno; | |
246 // Sync even if fflush gave an error; perhaps the data actually got out, | |
247 // even though something went wrong. | |
248 if (fdatasync(fileno(file_)) && !error) | |
249 error = errno; | |
250 // Report the first error we found. | |
251 if (error) { | |
252 result = MakeIOError(filename_, strerror(error), kWritableFileSync, error); | |
253 uma_logger_->RecordErrorAt(kWritableFileSync); | |
254 } else if (make_backup_ && file_type_ == kTable) { | |
255 bool success = ChromiumEnv::MakeBackup(filename_); | |
256 uma_logger_->RecordBackupResult(success); | |
257 } | |
258 return result; | |
259 } | |
260 | |
261 ChromiumEnvPosix::ChromiumEnvPosix() { | |
jsbell
2013/12/12 01:11:42
Naming: Calling it Posix is confusing, since it's
cmumford
2013/12/12 17:40:51
So ChromiumEnv calls the file abstractions in base
jsbell
2013/12/12 18:11:41
It's more about how the terms "win" and "posix" ar
cmumford
2013/12/12 19:37:46
TL;DR How about ChromiumEnvPosix -> ChromiumEnvStd
dgrogan
2013/12/12 19:39:57
This SGTM.
| |
262 } | |
263 | |
264 ChromiumEnvPosix::~ChromiumEnvPosix() { | |
265 } | |
266 | |
267 Status ChromiumEnvPosix::NewSequentialFile(const std::string& fname, | |
268 SequentialFile** result) { | |
269 FILE* f = fopen_internal(fname.c_str(), "rb"); | |
270 if (f == NULL) { | |
271 *result = NULL; | |
272 int saved_errno = errno; | |
273 RecordOSError(kNewSequentialFile, saved_errno); | |
274 return MakeIOError( | |
275 fname, strerror(saved_errno), kNewSequentialFile, saved_errno); | |
276 } else { | |
277 *result = new ChromiumSequentialFile(fname, f, this); | |
278 return Status::OK(); | |
279 } | |
280 } | |
281 | |
282 void ChromiumEnvPosix::RecordOpenFilesLimit(const std::string& type) { | |
283 #if defined(OS_POSIX) | |
284 struct rlimit nofile; | |
285 if (getrlimit(RLIMIT_NOFILE, &nofile)) | |
286 return; | |
287 GetMaxFDHistogram(type)->Add(nofile.rlim_cur); | |
288 #endif | |
289 } | |
290 | |
291 Status ChromiumEnvPosix::NewRandomAccessFile(const std::string& fname, | |
292 RandomAccessFile** result) { | |
293 int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN; | |
294 bool created; | |
295 ::base::PlatformFileError error_code; | |
296 ::base::PlatformFile file = ::base::CreatePlatformFile( | |
297 ChromiumEnv::CreateFilePath(fname), flags, &created, &error_code); | |
298 if (error_code == ::base::PLATFORM_FILE_OK) { | |
299 *result = new ChromiumRandomAccessFile(fname, file, this); | |
300 RecordOpenFilesLimit("Success"); | |
301 return Status::OK(); | |
302 } | |
303 if (error_code == ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED) | |
304 RecordOpenFilesLimit("TooManyOpened"); | |
305 else | |
306 RecordOpenFilesLimit("OtherError"); | |
307 *result = NULL; | |
308 RecordOSError(kNewRandomAccessFile, error_code); | |
309 return MakeIOError(fname, | |
310 PlatformFileErrorString(error_code), | |
311 kNewRandomAccessFile, | |
312 error_code); | |
313 } | |
314 | |
315 Status ChromiumEnvPosix::NewWritableFile(const std::string& fname, | |
316 WritableFile** result) { | |
317 *result = NULL; | |
318 FILE* f = fopen_internal(fname.c_str(), "wb"); | |
319 if (f == NULL) { | |
320 int saved_errno = errno; | |
321 RecordErrorAt(kNewWritableFile); | |
322 return MakeIOError( | |
323 fname, strerror(saved_errno), kNewWritableFile, saved_errno); | |
324 } else { | |
325 *result = new ChromiumWritableFile(fname, f, this, this, make_backup_); | |
326 return Status::OK(); | |
327 } | |
328 } | |
329 | |
330 #if defined(OS_WIN) | |
331 base::PlatformFileError ChromiumEnvPosix::GetDirectoryEntries( | |
332 const base::FilePath& dir_param, | |
333 std::vector<base::FilePath>* result) const { | |
334 result->clear(); | |
335 base::FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*")); | |
336 WIN32_FIND_DATA find_data; | |
337 HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data); | |
338 if (find_handle == INVALID_HANDLE_VALUE) { | |
339 DWORD last_error = GetLastError(); | |
340 if (last_error == ERROR_FILE_NOT_FOUND) | |
341 return base::PLATFORM_FILE_OK; | |
342 return base::LastErrorToPlatformFileError(last_error); | |
343 } | |
344 do { | |
345 base::FilePath filepath(find_data.cFileName); | |
346 base::FilePath::StringType basename = filepath.BaseName().value(); | |
347 if (basename == FILE_PATH_LITERAL(".") || | |
348 basename == FILE_PATH_LITERAL("..")) | |
349 continue; | |
350 result->push_back(filepath.BaseName()); | |
351 } while (FindNextFile(find_handle, &find_data)); | |
352 DWORD last_error = GetLastError(); | |
353 base::PlatformFileError return_value = base::PLATFORM_FILE_OK; | |
354 if (last_error != ERROR_NO_MORE_FILES) | |
355 return_value = base::LastErrorToPlatformFileError(last_error); | |
356 FindClose(find_handle); | |
357 return return_value; | |
358 } | |
359 #else | |
360 base::PlatformFileError ChromiumEnvPosix::GetDirectoryEntries( | |
361 const base::FilePath& dir_filepath, | |
362 std::vector<base::FilePath>* result) const { | |
363 const std::string dir_string = FilePathToString(dir_filepath); | |
364 result->clear(); | |
365 DIR* dir = opendir(dir_string.c_str()); | |
366 if (!dir) | |
367 return base::ErrnoToPlatformFileError(errno); | |
368 struct dirent dent_buf; | |
369 struct dirent* dent; | |
370 int readdir_result; | |
371 while ((readdir_result = readdir_r(dir, &dent_buf, &dent)) == 0 && dent) { | |
372 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) | |
373 continue; | |
374 result->push_back(ChromiumEnv::CreateFilePath(dent->d_name)); | |
375 } | |
376 int saved_errno = errno; | |
377 closedir(dir); | |
378 if (readdir_result != 0) | |
379 return base::ErrnoToPlatformFileError(saved_errno); | |
380 return base::PLATFORM_FILE_OK; | |
381 } | |
382 #endif | |
383 | |
384 Status ChromiumEnvPosix::NewLogger(const std::string& fname, Logger** result) { | |
385 FILE* f = fopen_internal(fname.c_str(), "w"); | |
386 if (f == NULL) { | |
387 *result = NULL; | |
388 int saved_errno = errno; | |
389 RecordOSError(kNewLogger, saved_errno); | |
390 return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno); | |
391 } else { | |
392 *result = new ChromiumLogger(f); | |
393 return Status::OK(); | |
394 } | |
395 } | |
396 | |
397 } // namespace leveldb_env | |
OLD | NEW |