1 // Copyright (c) 2011-2013 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.
5 #include "third_party/leveldatabase/env_chromium_stdio.h"
11 #include <sys/resource.h>
13 #include <sys/types.h>
20 #include "base/debug/trace_event.h"
21 #include "base/metrics/histogram.h"
22 #include "base/posix/eintr_wrapper.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "third_party/leveldatabase/chromium_logger.h"
27 #include "base/win/win_util.h"
30 using leveldb::ChromiumLogger
;
31 using leveldb::Logger
;
32 using leveldb::RandomAccessFile
;
33 using leveldb::SequentialFile
;
35 using leveldb::Status
;
36 using leveldb::WritableFile
;
38 namespace leveldb_env
{
42 #if (defined(OS_POSIX) && !defined(OS_LINUX)) || defined(OS_WIN)
43 // The following are glibc-specific
45 size_t fread_unlocked(void* ptr
, size_t size
, size_t n
, FILE* file
) {
46 return fread(ptr
, size
, n
, file
);
49 size_t fwrite_unlocked(const void* ptr
, size_t size
, size_t n
, FILE* file
) {
50 return fwrite(ptr
, size
, n
, file
);
53 int fflush_unlocked(FILE* file
) { return fflush(file
); }
55 #if !defined(OS_ANDROID)
56 int fdatasync(int fildes
) {
58 return _commit(fildes
);
60 return HANDLE_EINTR(fsync(fildes
));
67 // Wide-char safe fopen wrapper.
68 FILE* fopen_internal(const char* fname
, const char* mode
) {
70 return _wfopen(base::UTF8ToUTF16(fname
).c_str(),
71 base::ASCIIToUTF16(mode
).c_str());
73 return fopen(fname
, mode
);
77 class ChromiumSequentialFile
: public SequentialFile
{
79 std::string filename_
;
81 const UMALogger
* uma_logger_
;
84 ChromiumSequentialFile(const std::string
& fname
,
86 const UMALogger
* uma_logger
)
87 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) {}
88 virtual ~ChromiumSequentialFile() { fclose(file_
); }
90 virtual Status
Read(size_t n
, Slice
* result
, char* scratch
) {
92 size_t r
= fread_unlocked(scratch
, 1, n
, file_
);
93 *result
= Slice(scratch
, r
);
96 // We leave status as ok if we hit the end of the file
98 // A partial read with an error: return a non-ok status
99 s
= MakeIOError(filename_
, strerror(errno
), kSequentialFileRead
, errno
);
100 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
106 virtual Status
Skip(uint64_t n
) {
107 if (fseek(file_
, n
, SEEK_CUR
)) {
108 int saved_errno
= errno
;
109 uma_logger_
->RecordErrorAt(kSequentialFileSkip
);
111 filename_
, strerror(saved_errno
), kSequentialFileSkip
, saved_errno
);
117 class ChromiumRandomAccessFile
: public RandomAccessFile
{
119 std::string filename_
;
120 mutable ::base::File file_
;
121 const UMALogger
* uma_logger_
;
124 ChromiumRandomAccessFile(const std::string
& fname
,
126 const UMALogger
* uma_logger
)
127 : filename_(fname
), file_(file
.Pass()), uma_logger_(uma_logger
) {}
128 virtual ~ChromiumRandomAccessFile() {}
130 virtual Status
Read(uint64_t offset
, size_t n
, Slice
* result
, char* scratch
)
133 int r
= file_
.Read(offset
, scratch
, n
);
134 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
136 // An error: return a non-ok status
138 filename_
, "Could not perform read", kRandomAccessFileRead
);
139 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
145 } // unnamed namespace
147 ChromiumWritableFile::ChromiumWritableFile(const std::string
& fname
,
149 const UMALogger
* uma_logger
,
150 WriteTracker
* tracker
,
154 uma_logger_(uma_logger
),
157 make_backup_(make_backup
) {
158 base::FilePath path
= base::FilePath::FromUTF8Unsafe(fname
);
159 if (FilePathToString(path
.BaseName()).find("MANIFEST") == 0)
160 file_type_
= kManifest
;
161 else if (ChromiumEnv::HasTableExtension(path
))
163 if (file_type_
!= kManifest
)
164 tracker_
->DidCreateNewFile(filename_
);
165 parent_dir_
= FilePathToString(ChromiumEnv::CreateFilePath(fname
).DirName());
168 ChromiumWritableFile::~ChromiumWritableFile() {
170 // Ignoring any potential errors
175 Status
ChromiumWritableFile::SyncParent() {
178 TRACE_EVENT0("leveldb", "SyncParent");
180 int parent_fd
= HANDLE_EINTR(open(parent_dir_
.c_str(), O_RDONLY
));
182 int saved_errno
= errno
;
184 parent_dir_
, strerror(saved_errno
), kSyncParent
, saved_errno
);
186 if (HANDLE_EINTR(fsync(parent_fd
)) != 0) {
187 int saved_errno
= errno
;
189 parent_dir_
, strerror(saved_errno
), kSyncParent
, saved_errno
);
196 Status
ChromiumWritableFile::Append(const Slice
& data
) {
197 if (file_type_
== kManifest
&& tracker_
->DoesDirNeedSync(filename_
)) {
198 Status s
= SyncParent();
201 tracker_
->DidSyncDir(filename_
);
204 size_t r
= fwrite_unlocked(data
.data(), 1, data
.size(), file_
);
205 if (r
!= data
.size()) {
206 int saved_errno
= errno
;
207 uma_logger_
->RecordOSError(kWritableFileAppend
, saved_errno
);
209 filename_
, strerror(saved_errno
), kWritableFileAppend
, saved_errno
);
214 Status
ChromiumWritableFile::Close() {
216 if (fclose(file_
) != 0) {
217 result
= MakeIOError(filename_
, strerror(errno
), kWritableFileClose
, errno
);
218 uma_logger_
->RecordErrorAt(kWritableFileClose
);
224 Status
ChromiumWritableFile::Flush() {
226 if (HANDLE_EINTR(fflush_unlocked(file_
))) {
227 int saved_errno
= errno
;
228 result
= MakeIOError(
229 filename_
, strerror(saved_errno
), kWritableFileFlush
, saved_errno
);
230 uma_logger_
->RecordOSError(kWritableFileFlush
, saved_errno
);
235 Status
ChromiumWritableFile::Sync() {
236 TRACE_EVENT0("leveldb", "ChromiumEnvStdio::Sync");
240 if (HANDLE_EINTR(fflush_unlocked(file_
)))
242 // Sync even if fflush gave an error; perhaps the data actually got out,
243 // even though something went wrong.
244 if (fdatasync(fileno(file_
)) && !error
)
246 // Report the first error we found.
248 result
= MakeIOError(filename_
, strerror(error
), kWritableFileSync
, error
);
249 uma_logger_
->RecordErrorAt(kWritableFileSync
);
250 } else if (make_backup_
&& file_type_
== kTable
) {
251 bool success
= ChromiumEnv::MakeBackup(filename_
);
252 uma_logger_
->RecordBackupResult(success
);
257 ChromiumEnvStdio::ChromiumEnvStdio() {}
259 ChromiumEnvStdio::~ChromiumEnvStdio() {}
261 Status
ChromiumEnvStdio::NewSequentialFile(const std::string
& fname
,
262 SequentialFile
** result
) {
263 FILE* f
= fopen_internal(fname
.c_str(), "rb");
266 int saved_errno
= errno
;
267 RecordOSError(kNewSequentialFile
, saved_errno
);
269 fname
, strerror(saved_errno
), kNewSequentialFile
, saved_errno
);
271 *result
= new ChromiumSequentialFile(fname
, f
, this);
276 void ChromiumEnvStdio::RecordOpenFilesLimit(const std::string
& type
) {
277 #if defined(OS_POSIX)
278 struct rlimit nofile
;
279 if (getrlimit(RLIMIT_NOFILE
, &nofile
))
281 GetMaxFDHistogram(type
)->Add(nofile
.rlim_cur
);
285 Status
ChromiumEnvStdio::NewRandomAccessFile(const std::string
& fname
,
286 RandomAccessFile
** result
) {
287 int flags
= ::base::File::FLAG_READ
| ::base::File::FLAG_OPEN
;
288 ::base::File
file(ChromiumEnv::CreateFilePath(fname
), flags
);
289 if (file
.IsValid()) {
290 *result
= new ChromiumRandomAccessFile(fname
, file
.Pass(), this);
291 RecordOpenFilesLimit("Success");
294 ::base::File::Error error_code
= file
.error_details();
295 if (error_code
== ::base::File::FILE_ERROR_TOO_MANY_OPENED
)
296 RecordOpenFilesLimit("TooManyOpened");
298 RecordOpenFilesLimit("OtherError");
300 RecordOSError(kNewRandomAccessFile
, error_code
);
301 return MakeIOError(fname
,
302 FileErrorString(error_code
),
303 kNewRandomAccessFile
,
307 Status
ChromiumEnvStdio::NewWritableFile(const std::string
& fname
,
308 WritableFile
** result
) {
310 FILE* f
= fopen_internal(fname
.c_str(), "wb");
312 int saved_errno
= errno
;
313 RecordErrorAt(kNewWritableFile
);
315 fname
, strerror(saved_errno
), kNewWritableFile
, saved_errno
);
317 *result
= new ChromiumWritableFile(fname
, f
, this, this, make_backup_
);
323 base::File::Error
ChromiumEnvStdio::GetDirectoryEntries(
324 const base::FilePath
& dir_param
,
325 std::vector
<base::FilePath
>* result
) const {
327 base::FilePath dir_filepath
= dir_param
.Append(FILE_PATH_LITERAL("*"));
328 WIN32_FIND_DATA find_data
;
329 HANDLE find_handle
= FindFirstFile(dir_filepath
.value().c_str(), &find_data
);
330 if (find_handle
== INVALID_HANDLE_VALUE
) {
331 DWORD last_error
= GetLastError();
332 if (last_error
== ERROR_FILE_NOT_FOUND
)
333 return base::File::FILE_OK
;
334 return base::File::OSErrorToFileError(last_error
);
337 base::FilePath
filepath(find_data
.cFileName
);
338 base::FilePath::StringType basename
= filepath
.BaseName().value();
339 if (basename
== FILE_PATH_LITERAL(".") ||
340 basename
== FILE_PATH_LITERAL(".."))
342 result
->push_back(filepath
.BaseName());
343 } while (FindNextFile(find_handle
, &find_data
));
344 DWORD last_error
= GetLastError();
345 base::File::Error return_value
= base::File::FILE_OK
;
346 if (last_error
!= ERROR_NO_MORE_FILES
)
347 return_value
= base::File::OSErrorToFileError(last_error
);
348 FindClose(find_handle
);
352 base::File::Error
ChromiumEnvStdio::GetDirectoryEntries(
353 const base::FilePath
& dir_filepath
,
354 std::vector
<base::FilePath
>* result
) const {
355 const std::string dir_string
= FilePathToString(dir_filepath
);
357 DIR* dir
= opendir(dir_string
.c_str());
359 return base::File::OSErrorToFileError(errno
);
360 struct dirent dent_buf
;
363 while ((readdir_result
= readdir_r(dir
, &dent_buf
, &dent
)) == 0 && dent
) {
364 if (strcmp(dent
->d_name
, ".") == 0 || strcmp(dent
->d_name
, "..") == 0)
366 result
->push_back(ChromiumEnv::CreateFilePath(dent
->d_name
));
368 int saved_errno
= errno
;
370 if (readdir_result
!= 0)
371 return base::File::OSErrorToFileError(saved_errno
);
372 return base::File::FILE_OK
;
376 Status
ChromiumEnvStdio::NewLogger(const std::string
& fname
, Logger
** result
) {
377 FILE* f
= fopen_internal(fname
.c_str(), "w");
380 int saved_errno
= errno
;
381 RecordOSError(kNewLogger
, saved_errno
);
382 return MakeIOError(fname
, strerror(saved_errno
), kNewLogger
, saved_errno
);
384 *result
= new ChromiumLogger(f
);
389 } // namespace leveldb_env