Chromium Code Reviews| 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 |