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.
8 #include "base/at_exit.h"
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/platform_file.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/stringprintf.h"
18 #include "base/synchronization/lock.h"
19 #include "base/sys_info.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/threading/thread.h"
22 #include "base/utf_string_conversions.h"
23 #include "chromium_logger.h"
24 #include "leveldb/env.h"
25 #include "leveldb/slice.h"
26 #include "port/port.h"
27 #include "util/logging.h"
31 #include "base/win/win_util.h"
40 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID) || \
42 // The following are glibc-specific
44 size_t fread_unlocked(void *ptr
, size_t size
, size_t n
, FILE *file
) {
45 return fread(ptr
, size
, n
, file
);
48 size_t fwrite_unlocked(const void *ptr
, size_t size
, size_t n
, FILE *file
) {
49 return fwrite(ptr
, size
, n
, file
);
52 int fflush_unlocked(FILE *file
) {
56 #if !defined(OS_ANDROID)
57 int fdatasync(int fildes
) {
59 return _commit(fildes
);
61 return HANDLE_EINTR(fsync(fildes
));
68 // Wide-char safe fopen wrapper.
69 FILE* fopen_internal(const char* fname
, const char* mode
) {
71 return _wfopen(UTF8ToUTF16(fname
).c_str(), ASCIIToUTF16(mode
).c_str());
73 return fopen(fname
, mode
);
77 ::FilePath
CreateFilePath(const std::string
& file_path
) {
79 return FilePath(UTF8ToUTF16(file_path
));
81 return FilePath(file_path
);
85 std::string
FilePathToString(const ::FilePath
& file_path
) {
87 return UTF16ToUTF8(file_path
.value());
89 return file_path
.value();
93 bool sync_parent(const std::string
& fname
) {
95 FilePath parent_dir
= CreateFilePath(fname
).DirName();
96 int parent_fd
= HANDLE_EINTR(open(FilePathToString(parent_dir
).c_str(), O_RDONLY
));
99 HANDLE_EINTR(fsync(parent_fd
));
100 HANDLE_EINTR(close(parent_fd
));
108 kRandomAccessFileRead
,
114 kNewRandomAccessFile
,
130 virtual void RecordErrorAt(UmaEntry entry
) const = 0;
131 virtual void LogRandomAccessFileError(base::PlatformFileError error_code
)
143 static const ::FilePath::CharType kLevelDBTestDirectoryPrefix
[]
144 = FILE_PATH_LITERAL("leveldb-test-");
146 const char* PlatformFileErrorString(const ::base::PlatformFileError
& error
) {
148 case ::base::PLATFORM_FILE_ERROR_FAILED
:
149 return "Opening file failed.";
150 case ::base::PLATFORM_FILE_ERROR_IN_USE
:
151 return "File currently in use.";
152 case ::base::PLATFORM_FILE_ERROR_EXISTS
:
153 return "File already exists.";
154 case ::base::PLATFORM_FILE_ERROR_NOT_FOUND
:
155 return "File not found.";
156 case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED
:
157 return "Access denied.";
158 case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED
:
159 return "Too many files open.";
160 case ::base::PLATFORM_FILE_ERROR_NO_MEMORY
:
161 return "Out of memory.";
162 case ::base::PLATFORM_FILE_ERROR_NO_SPACE
:
163 return "No space left on drive.";
164 case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
:
165 return "Not a directory.";
166 case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION
:
167 return "Invalid operation.";
168 case ::base::PLATFORM_FILE_ERROR_SECURITY
:
169 return "Security error.";
170 case ::base::PLATFORM_FILE_ERROR_ABORT
:
171 return "File operation aborted.";
172 case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE
:
173 return "The supplied path was not a file.";
174 case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY
:
175 return "The file was not empty.";
176 case ::base::PLATFORM_FILE_ERROR_INVALID_URL
:
177 return "Invalid URL.";
178 case ::base::PLATFORM_FILE_OK
:
180 case ::base::PLATFORM_FILE_ERROR_MAX
:
184 return "Unknown error.";
187 class ChromiumSequentialFile
: public SequentialFile
{
189 std::string filename_
;
191 const UMALogger
* uma_logger_
;
194 ChromiumSequentialFile(const std::string
& fname
, FILE* f
,
195 const UMALogger
* uma_logger
)
196 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) { }
197 virtual ~ChromiumSequentialFile() { fclose(file_
); }
199 virtual Status
Read(size_t n
, Slice
* result
, char* scratch
) {
201 size_t r
= fread_unlocked(scratch
, 1, n
, file_
);
202 *result
= Slice(scratch
, r
);
205 // We leave status as ok if we hit the end of the file
207 // A partial read with an error: return a non-ok status
208 s
= Status::IOError(filename_
, strerror(errno
));
209 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
215 virtual Status
Skip(uint64_t n
) {
216 if (fseek(file_
, n
, SEEK_CUR
)) {
217 uma_logger_
->RecordErrorAt(kSequentialFileSkip
);
218 return Status::IOError(filename_
, strerror(errno
));
224 class ChromiumRandomAccessFile
: public RandomAccessFile
{
226 std::string filename_
;
227 ::base::PlatformFile file_
;
228 const UMALogger
* uma_logger_
;
231 ChromiumRandomAccessFile(const std::string
& fname
, ::base::PlatformFile file
,
232 const UMALogger
* uma_logger
)
233 : filename_(fname
), file_(file
), uma_logger_(uma_logger
) { }
234 virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_
); }
236 virtual Status
Read(uint64_t offset
, size_t n
, Slice
* result
,
237 char* scratch
) const {
239 int r
= ::base::ReadPlatformFile(file_
, offset
, scratch
, n
);
240 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
242 // An error: return a non-ok status
243 s
= Status::IOError(filename_
, "Could not perform read");
244 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
250 class ChromiumWritableFile
: public WritableFile
{
252 std::string filename_
;
254 const UMALogger
* uma_logger_
;
257 ChromiumWritableFile(const std::string
& fname
, FILE* f
,
258 const UMALogger
* uma_logger
)
259 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) { }
261 ~ChromiumWritableFile() {
263 // Ignoring any potential errors
268 virtual Status
Append(const Slice
& data
) {
269 size_t r
= fwrite_unlocked(data
.data(), 1, data
.size(), file_
);
271 if (r
!= data
.size()) {
272 result
= Status::IOError(filename_
, strerror(errno
));
273 uma_logger_
->RecordErrorAt(kWritableFileAppend
);
278 virtual Status
Close() {
280 if (fclose(file_
) != 0) {
281 result
= Status::IOError(filename_
, strerror(errno
));
282 uma_logger_
->RecordErrorAt(kWritableFileClose
);
288 virtual Status
Flush() {
290 if (fflush_unlocked(file_
) != 0) {
291 result
= Status::IOError(filename_
, strerror(errno
));
292 uma_logger_
->RecordErrorAt(kWritableFileFlush
);
297 virtual Status
Sync() {
301 if (fflush_unlocked(file_
))
303 // Sync even if fflush gave an error; perhaps the data actually got out,
304 // even though something went wrong.
305 if (fdatasync(fileno(file_
)) && !error
)
307 // Report the first error we found.
309 result
= Status::IOError(filename_
, strerror(error
));
310 uma_logger_
->RecordErrorAt(kWritableFileSync
);
316 class ChromiumFileLock
: public FileLock
{
318 ::base::PlatformFile file_
;
321 class ChromiumEnv
: public Env
, public UMALogger
{
324 virtual ~ChromiumEnv() {
328 virtual Status
NewSequentialFile(const std::string
& fname
,
329 SequentialFile
** result
) {
330 FILE* f
= fopen_internal(fname
.c_str(), "rb");
333 RecordErrorAt(kNewSequentialFile
);
334 return Status::IOError(fname
, strerror(errno
));
336 *result
= new ChromiumSequentialFile(fname
, f
, this);
341 virtual Status
NewRandomAccessFile(const std::string
& fname
,
342 RandomAccessFile
** result
) {
343 int flags
= ::base::PLATFORM_FILE_READ
| ::base::PLATFORM_FILE_OPEN
;
345 ::base::PlatformFileError error_code
;
346 ::base::PlatformFile file
= ::base::CreatePlatformFile(
347 CreateFilePath(fname
), flags
, &created
, &error_code
);
348 if (error_code
!= ::base::PLATFORM_FILE_OK
) {
350 RecordErrorAt(kNewRandomAccessFile
);
351 LogRandomAccessFileError(error_code
);
352 return Status::IOError(fname
, PlatformFileErrorString(error_code
));
354 *result
= new ChromiumRandomAccessFile(fname
, file
, this);
358 virtual Status
NewWritableFile(const std::string
& fname
,
359 WritableFile
** result
) {
361 FILE* f
= fopen_internal(fname
.c_str(), "wb");
363 RecordErrorAt(kNewWritableFile
);
364 return Status::IOError(fname
, strerror(errno
));
366 if (!sync_parent(fname
)) {
368 return Status::IOError(fname
, strerror(errno
));
370 *result
= new ChromiumWritableFile(fname
, f
, this);
375 virtual bool FileExists(const std::string
& fname
) {
376 return ::file_util::PathExists(CreateFilePath(fname
));
379 virtual Status
GetChildren(const std::string
& dir
,
380 std::vector
<std::string
>* result
) {
382 ::file_util::FileEnumerator
iter(
383 CreateFilePath(dir
), false, ::file_util::FileEnumerator::FILES
);
384 ::FilePath current
= iter
.Next();
385 while (!current
.empty()) {
386 result
->push_back(FilePathToString(current
.BaseName()));
387 current
= iter
.Next();
389 // TODO(jorlow): Unfortunately, the FileEnumerator swallows errors, so
390 // we'll always return OK. Maybe manually check for error
391 // conditions like the file not existing?
395 virtual Status
DeleteFile(const std::string
& fname
) {
397 // TODO(jorlow): Should we assert this is a file?
398 if (!::file_util::Delete(CreateFilePath(fname
), false)) {
399 result
= Status::IOError(fname
, "Could not delete file.");
400 RecordErrorAt(kDeleteFile
);
405 virtual Status
CreateDir(const std::string
& name
) {
407 if (!::file_util::CreateDirectory(CreateFilePath(name
))) {
408 result
= Status::IOError(name
, "Could not create directory.");
409 RecordErrorAt(kCreateDir
);
414 virtual Status
DeleteDir(const std::string
& name
) {
416 // TODO(jorlow): Should we assert this is a directory?
417 if (!::file_util::Delete(CreateFilePath(name
), false)) {
418 result
= Status::IOError(name
, "Could not delete directory.");
419 RecordErrorAt(kDeleteDir
);
424 virtual Status
GetFileSize(const std::string
& fname
, uint64_t* size
) {
427 if (!::file_util::GetFileSize(CreateFilePath(fname
), &signed_size
)) {
429 s
= Status::IOError(fname
, "Could not determine file size.");
430 RecordErrorAt(kGetFileSize
);
432 *size
= static_cast<uint64_t>(signed_size
);
437 virtual Status
RenameFile(const std::string
& src
, const std::string
& dst
) {
439 FilePath src_file_path
= CreateFilePath(src
);
440 if (!::file_util::PathExists(src_file_path
))
442 if (!::file_util::ReplaceFile(src_file_path
, CreateFilePath(dst
))) {
443 result
= Status::IOError(src
, "Could not rename file.");
444 RecordErrorAt(kRenamefile
);
453 virtual Status
LockFile(const std::string
& fname
, FileLock
** lock
) {
456 int flags
= ::base::PLATFORM_FILE_OPEN_ALWAYS
|
457 ::base::PLATFORM_FILE_READ
|
458 ::base::PLATFORM_FILE_WRITE
|
459 ::base::PLATFORM_FILE_EXCLUSIVE_READ
|
460 ::base::PLATFORM_FILE_EXCLUSIVE_WRITE
;
462 ::base::PlatformFileError error_code
;
463 ::base::PlatformFile file
= ::base::CreatePlatformFile(
464 CreateFilePath(fname
), flags
, &created
, &error_code
);
465 if (error_code
!= ::base::PLATFORM_FILE_OK
) {
466 result
= Status::IOError(fname
, PlatformFileErrorString(error_code
));
467 RecordErrorAt(kLockFile
);
469 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
470 my_lock
->file_
= file
;
476 virtual Status
UnlockFile(FileLock
* lock
) {
477 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
479 if (!::base::ClosePlatformFile(my_lock
->file_
)) {
480 result
= Status::IOError("Could not close lock file.");
481 RecordErrorAt(kUnlockFile
);
487 virtual void Schedule(void (*function
)(void*), void* arg
);
489 virtual void StartThread(void (*function
)(void* arg
), void* arg
);
491 virtual std::string
UserIdentifier() {
493 std::wstring user_sid
;
494 bool ret
= ::base::win::GetUserSidString(&user_sid
);
496 return UTF16ToUTF8(user_sid
);
499 snprintf(buf
, sizeof(buf
), "%d", int(geteuid()));
504 virtual Status
GetTestDirectory(std::string
* path
) {
506 if (test_directory_
.empty()) {
507 if (!::file_util::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
510 RecordErrorAt(kGetTestDirectory
);
511 return Status::IOError("Could not create temp directory.");
514 *path
= FilePathToString(test_directory_
);
519 virtual Status
NewLogger(const std::string
& fname
, Logger
** result
) {
520 FILE* f
= fopen_internal(fname
.c_str(), "w");
523 RecordErrorAt(kNewLogger
);
524 return Status::IOError(fname
, strerror(errno
));
526 if (!sync_parent(fname
)) {
528 return Status::IOError(fname
, strerror(errno
));
530 *result
= new ChromiumLogger(f
);
535 virtual uint64_t NowMicros() {
536 return ::base::TimeTicks::Now().ToInternalValue();
539 virtual void SleepForMicroseconds(int micros
) {
540 // Round up to the next millisecond.
541 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros
));
544 void RecordErrorAt(UmaEntry entry
) const {
545 io_error_histogram_
->Add(entry
);
548 void LogRandomAccessFileError(base::PlatformFileError error_code
) const {
549 DCHECK(error_code
< 0);
550 random_access_file_histogram_
->Add(-error_code
);
554 void InitHistograms(const std::string
& uma_title
);
557 // BGThread() is the body of the background thread
559 static void BGThreadWrapper(void* arg
) {
560 reinterpret_cast<ChromiumEnv
*>(arg
)->BGThread();
563 FilePath test_directory_
;
567 ::base::ConditionVariable bgsignal_
;
568 bool started_bgthread_
;
570 // Entry per Schedule() call
571 struct BGItem
{ void* arg
; void (*function
)(void*); };
572 typedef std::deque
<BGItem
> BGQueue
;
575 base::Histogram
* io_error_histogram_
;
576 base::Histogram
* random_access_file_histogram_
;
579 ChromiumEnv::ChromiumEnv()
580 : page_size_(::base::SysInfo::VMAllocationGranularity()),
582 started_bgthread_(false) {
583 InitHistograms("LevelDBEnv");
586 void ChromiumEnv::InitHistograms(const std::string
& uma_title
) {
587 std::string
uma_name(uma_title
);
588 uma_name
.append(".IOError");
589 // Note: The calls to FactoryGet aren't thread-safe. It's ok to call them here
590 // because this method is only called from LazyInstance, which provides
592 io_error_histogram_
= base::LinearHistogram::FactoryGet(uma_name
, 1,
593 kNumEntries
, kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
595 uma_name
.append(".RandomAccessFile");
596 random_access_file_histogram_
= base::LinearHistogram::FactoryGet(uma_name
, 1,
597 -base::PLATFORM_FILE_ERROR_MAX
, -base::PLATFORM_FILE_ERROR_MAX
+ 1,
598 base::Histogram::kUmaTargetedHistogramFlag
);
601 class Thread
: public ::base::PlatformThread::Delegate
{
603 Thread(void (*function
)(void* arg
), void* arg
)
604 : function_(function
), arg_(arg
) {
605 ::base::PlatformThreadHandle handle
;
606 bool success
= ::base::PlatformThread::Create(0, this, &handle
);
610 virtual void ThreadMain() {
616 void (*function_
)(void* arg
);
620 void ChromiumEnv::Schedule(void (*function
)(void*), void* arg
) {
623 // Start background thread if necessary
624 if (!started_bgthread_
) {
625 started_bgthread_
= true;
626 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
629 // If the queue is currently empty, the background thread may currently be
631 if (queue_
.empty()) {
635 // Add to priority queue
636 queue_
.push_back(BGItem());
637 queue_
.back().function
= function
;
638 queue_
.back().arg
= arg
;
643 void ChromiumEnv::BGThread() {
645 // Wait until there is an item that is ready to run
647 while (queue_
.empty()) {
651 void (*function
)(void*) = queue_
.front().function
;
652 void* arg
= queue_
.front().arg
;
660 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
661 new Thread(function
, arg
); // Will self-delete.
664 class IDBEnv
: public ChromiumEnv
{
666 IDBEnv() : ChromiumEnv() {
667 InitHistograms("LevelDBEnv.IDB");
671 ::base::LazyInstance
<IDBEnv
>::Leaky
672 idb_env
= LAZY_INSTANCE_INITIALIZER
;
674 ::base::LazyInstance
<ChromiumEnv
>::Leaky
675 default_env
= LAZY_INSTANCE_INITIALIZER
;
680 return idb_env
.Pointer();
683 Env
* Env::Default() {
684 return default_env
.Pointer();