• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 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.
4 
5 #include "base/debug/trace_event.h"
6 #include "base/files/file.h"
7 #include "base/files/file_path.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/win/win_util.h"
10 #include "chromium_logger.h"
11 #include "env_chromium_stdio.h"
12 #include "env_chromium_win.h"
13 
14 using namespace leveldb;
15 
16 namespace leveldb_env {
17 
18 namespace {
19 
GetWindowsErrorMessage(DWORD err)20 static std::string GetWindowsErrorMessage(DWORD err) {
21   LPTSTR errorText(NULL);
22   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
23                     FORMAT_MESSAGE_IGNORE_INSERTS,
24                 NULL,
25                 err,
26                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
27                 (LPTSTR) & errorText,
28                 0,
29                 NULL);
30   if (errorText != NULL) {
31     std::string message(base::UTF16ToUTF8(errorText));
32     // FormatMessage adds CR/LF to messages so we remove it.
33     base::TrimWhitespace(message, base::TRIM_TRAILING, &message);
34     LocalFree(errorText);
35     return message;
36   } else {
37     return std::string();
38   }
39 }
40 
41 class ChromiumSequentialFileWin : public SequentialFile {
42  private:
43   std::string filename_;
44   HANDLE file_;
45   const UMALogger* uma_logger_;
46 
47  public:
ChromiumSequentialFileWin(const std::string & fname,HANDLE f,const UMALogger * uma_logger)48   ChromiumSequentialFileWin(const std::string& fname,
49                          HANDLE f,
50                          const UMALogger* uma_logger)
51       : filename_(fname), file_(f), uma_logger_(uma_logger) {
52     DCHECK(file_ != INVALID_HANDLE_VALUE);
53   }
~ChromiumSequentialFileWin()54   virtual ~ChromiumSequentialFileWin() {
55     DCHECK(file_ != INVALID_HANDLE_VALUE);
56     CloseHandle(file_);
57     file_ = INVALID_HANDLE_VALUE;
58   }
59 
Read(size_t n,Slice * result,char * scratch)60   virtual Status Read(size_t n, Slice* result, char* scratch) {
61     Status s;
62     DWORD bytes_read(0);
63     DCHECK(file_ != INVALID_HANDLE_VALUE);
64     if (ReadFile(file_, scratch, n, &bytes_read, NULL)) {
65       *result = Slice(scratch, bytes_read);
66     } else {
67       DWORD err = GetLastError();
68       s = MakeIOErrorWin(
69           filename_, GetWindowsErrorMessage(err), kSequentialFileRead, err);
70       uma_logger_->RecordErrorAt(kSequentialFileRead);
71       if (bytes_read > 0)
72         *result = Slice(scratch, bytes_read);
73     }
74     return s;
75   }
76 
Skip(uint64_t n)77   virtual Status Skip(uint64_t n) {
78     LARGE_INTEGER li;
79     li.QuadPart = n;
80     if (SetFilePointer(file_, li.LowPart, &li.HighPart, FILE_CURRENT) ==
81         INVALID_SET_FILE_POINTER) {
82       DWORD err = GetLastError();
83       uma_logger_->RecordErrorAt(kSequentialFileSkip);
84       return MakeIOErrorWin(
85           filename_, GetWindowsErrorMessage(err), kSequentialFileSkip, err);
86     }
87     return Status::OK();
88   }
89 };
90 
91 class ChromiumRandomAccessFileWin : public RandomAccessFile {
92  private:
93   std::string filename_;
94   mutable ::base::File file_;
95   const UMALogger* uma_logger_;
96 
97  public:
ChromiumRandomAccessFileWin(const std::string & fname,::base::File file,const UMALogger * uma_logger)98   ChromiumRandomAccessFileWin(const std::string& fname,
99                               ::base::File file,
100                               const UMALogger* uma_logger)
101       : filename_(fname), file_(file.Pass()), uma_logger_(uma_logger) {}
~ChromiumRandomAccessFileWin()102   virtual ~ChromiumRandomAccessFileWin() {}
103 
Read(uint64_t offset,size_t n,Slice * result,char * scratch) const104   virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch)
105       const {
106     Status s;
107     int r = file_.Read(offset, scratch, n);
108     *result = Slice(scratch, (r < 0) ? 0 : r);
109     if (r < 0) {
110       // An error: return a non-ok status
111       s = MakeIOError(
112           filename_, "Could not perform read", kRandomAccessFileRead);
113       uma_logger_->RecordErrorAt(kRandomAccessFileRead);
114     }
115     return s;
116   }
117 };
118 
119 }  // unnamed namespace
120 
MakeIOErrorWin(Slice filename,const std::string & message,MethodID method,DWORD error)121 Status MakeIOErrorWin(Slice filename,
122                         const std::string& message,
123                         MethodID method,
124                         DWORD error) {
125   char buf[512];
126   if (snprintf(buf,
127                sizeof(buf),
128                "%s (ChromeMethodErrno: %d::%s::%u)",
129                message.c_str(),
130                method,
131                MethodIDToString(method),
132                error) >= 0) {
133     return Status::IOError(filename, buf);
134   } else {
135     return Status::IOError(filename, "<unknown>");
136   }
137 }
138 
ChromiumWritableFileWin(const std::string & fname,HANDLE f,const UMALogger * uma_logger,WriteTracker * tracker,bool make_backup)139 ChromiumWritableFileWin::ChromiumWritableFileWin(
140     const std::string& fname,
141     HANDLE f,
142     const UMALogger* uma_logger,
143     WriteTracker* tracker,
144     bool make_backup)
145     : filename_(fname),
146       file_(f),
147       uma_logger_(uma_logger),
148       tracker_(tracker),
149       file_type_(kOther),
150       make_backup_(make_backup) {
151   DCHECK(f != INVALID_HANDLE_VALUE);
152   base::FilePath path = base::FilePath::FromUTF8Unsafe(fname);
153   if (FilePathToString(path.BaseName()).find("MANIFEST") == 0)
154     file_type_ = kManifest;
155   else if (ChromiumEnv::HasTableExtension(path))
156     file_type_ = kTable;
157   if (file_type_ != kManifest)
158     tracker_->DidCreateNewFile(filename_);
159   parent_dir_ = FilePathToString(ChromiumEnv::CreateFilePath(fname).DirName());
160 }
161 
~ChromiumWritableFileWin()162 ChromiumWritableFileWin::~ChromiumWritableFileWin() {
163   if (file_ != INVALID_HANDLE_VALUE) {
164     // Ignoring any potential errors
165 
166     CloseHandle(file_);
167     file_ = INVALID_HANDLE_VALUE;
168   }
169 }
170 
SyncParent()171 Status ChromiumWritableFileWin::SyncParent() {
172   // On Windows no need to sync parent directory. It's metadata will be
173   // updated via the creation of the new file, without an explicit sync.
174   return Status();
175 }
176 
Append(const Slice & data)177 Status ChromiumWritableFileWin::Append(const Slice& data) {
178   if (file_type_ == kManifest && tracker_->DoesDirNeedSync(filename_)) {
179     Status s = SyncParent();
180     if (!s.ok())
181       return s;
182     tracker_->DidSyncDir(filename_);
183   }
184 
185   DWORD written(0);
186   if (!WriteFile(file_, data.data(), data.size(), &written, NULL)) {
187     DWORD err = GetLastError();
188     uma_logger_->RecordOSError(kWritableFileAppend,
189                                base::File::OSErrorToFileError(err));
190     return MakeIOErrorWin(
191         filename_, GetWindowsErrorMessage(err), kWritableFileAppend, err);
192   }
193   return Status::OK();
194 }
195 
Close()196 Status ChromiumWritableFileWin::Close() {
197   Status result;
198   DCHECK(file_ != INVALID_HANDLE_VALUE);
199   if (!CloseHandle(file_)) {
200     DWORD err = GetLastError();
201     result = MakeIOErrorWin(
202         filename_, GetWindowsErrorMessage(err), kWritableFileClose, err);
203     uma_logger_->RecordErrorAt(kWritableFileClose);
204   }
205   file_ = INVALID_HANDLE_VALUE;
206   return result;
207 }
208 
Flush()209 Status ChromiumWritableFileWin::Flush() {
210   // Windows doesn't use POSIX file streams, so there are no file stream buffers
211   // to flush - this is a no-op.
212   return Status();
213 }
214 
Sync()215 Status ChromiumWritableFileWin::Sync() {
216   TRACE_EVENT0("leveldb", "ChromiumEnvWin::Sync");
217   Status result;
218   DWORD error = ERROR_SUCCESS;
219 
220   DCHECK(file_ != INVALID_HANDLE_VALUE);
221   if (!FlushFileBuffers(file_))
222     error = GetLastError();
223   if (error != ERROR_SUCCESS) {
224     result = MakeIOErrorWin(
225         filename_, GetWindowsErrorMessage(error), kWritableFileSync, error);
226     uma_logger_->RecordErrorAt(kWritableFileSync);
227   } else if (make_backup_ && file_type_ == kTable) {
228     bool success = ChromiumEnv::MakeBackup(filename_);
229     uma_logger_->RecordBackupResult(success);
230   }
231   return result;
232 }
233 
ChromiumEnvWin()234 ChromiumEnvWin::ChromiumEnvWin() {}
235 
~ChromiumEnvWin()236 ChromiumEnvWin::~ChromiumEnvWin() {}
237 
NewSequentialFile(const std::string & fname,SequentialFile ** result)238 Status ChromiumEnvWin::NewSequentialFile(const std::string& fname,
239                                            SequentialFile** result) {
240   HANDLE f = CreateFile(base::UTF8ToUTF16(fname).c_str(),
241                         GENERIC_READ,
242                         FILE_SHARE_READ,
243                         NULL,
244                         OPEN_EXISTING,
245                         FILE_ATTRIBUTE_NORMAL,
246                         NULL);
247   if (f == INVALID_HANDLE_VALUE) {
248     *result = NULL;
249     DWORD err = GetLastError();
250     RecordOSError(kNewSequentialFile, err);
251     return MakeIOErrorWin(
252         fname, GetWindowsErrorMessage(err), kNewSequentialFile, err);
253   } else {
254     *result = new ChromiumSequentialFileWin(fname, f, this);
255     return Status::OK();
256   }
257 }
258 
RecordOpenFilesLimit(const std::string & type)259 void ChromiumEnvWin::RecordOpenFilesLimit(const std::string& type) {
260   // The Windows POSIX implementation (which this class doesn't use)
261   // has an open file limit, but when using the Win32 API this is limited by
262   // available memory, so no value to report.
263 }
264 
NewRandomAccessFile(const std::string & fname,RandomAccessFile ** result)265 Status ChromiumEnvWin::NewRandomAccessFile(const std::string& fname,
266                                            RandomAccessFile** result) {
267   int flags = ::base::File::FLAG_READ | ::base::File::FLAG_OPEN;
268   ::base::File file(ChromiumEnv::CreateFilePath(fname), flags);
269   if (file.IsValid()) {
270     *result = new ChromiumRandomAccessFileWin(fname, file.Pass(), this);
271     RecordOpenFilesLimit("Success");
272     return Status::OK();
273   }
274   ::base::File::Error error_code = file.error_details();
275   if (error_code == ::base::File::FILE_ERROR_TOO_MANY_OPENED)
276     RecordOpenFilesLimit("TooManyOpened");
277   else
278     RecordOpenFilesLimit("OtherError");
279   *result = NULL;
280   RecordOSError(kNewRandomAccessFile, error_code);
281   return MakeIOErrorWin(fname,
282                         FileErrorString(error_code),
283                         kNewRandomAccessFile,
284                         error_code);
285 }
286 
NewWritableFile(const std::string & fname,WritableFile ** result)287 Status ChromiumEnvWin::NewWritableFile(const std::string& fname,
288                                          WritableFile** result) {
289   *result = NULL;
290   HANDLE f = CreateFile(base::UTF8ToUTF16(fname).c_str(),
291                         GENERIC_WRITE,
292                         FILE_SHARE_READ,
293                         NULL,
294                         CREATE_ALWAYS,
295                         FILE_ATTRIBUTE_NORMAL,
296                         NULL);
297   if (f == INVALID_HANDLE_VALUE) {
298     DWORD err = GetLastError();
299     RecordErrorAt(kNewWritableFile);
300     return MakeIOErrorWin(
301         fname, GetWindowsErrorMessage(err), kNewWritableFile, err);
302   } else {
303     *result = new ChromiumWritableFileWin(fname, f, this, this, make_backup_);
304     return Status::OK();
305   }
306 }
307 
GetDirectoryEntries(const base::FilePath & dir_param,std::vector<base::FilePath> * result) const308 base::File::Error ChromiumEnvWin::GetDirectoryEntries(
309     const base::FilePath& dir_param,
310     std::vector<base::FilePath>* result) const {
311   result->clear();
312   base::FilePath dir_filepath = dir_param.Append(FILE_PATH_LITERAL("*"));
313   WIN32_FIND_DATA find_data;
314   HANDLE find_handle = FindFirstFile(dir_filepath.value().c_str(), &find_data);
315   if (find_handle == INVALID_HANDLE_VALUE) {
316     DWORD last_error = GetLastError();
317     if (last_error == ERROR_FILE_NOT_FOUND)
318       return base::File::FILE_OK;
319     return base::File::OSErrorToFileError(last_error);
320   }
321   do {
322     base::FilePath filepath(find_data.cFileName);
323     base::FilePath::StringType basename = filepath.BaseName().value();
324     if (basename == FILE_PATH_LITERAL(".") ||
325         basename == FILE_PATH_LITERAL(".."))
326       continue;
327     result->push_back(filepath.BaseName());
328   } while (FindNextFile(find_handle, &find_data));
329   DWORD last_error = GetLastError();
330   base::File::Error return_value = base::File::FILE_OK;
331   if (last_error != ERROR_NO_MORE_FILES)
332     return_value = base::File::OSErrorToFileError(last_error);
333   FindClose(find_handle);
334   return return_value;
335 }
336 
NewLogger(const std::string & fname,Logger ** result)337 Status ChromiumEnvWin::NewLogger(const std::string& fname, Logger** result) {
338   FILE* f = _wfopen(base::UTF8ToUTF16(fname).c_str(), L"w");
339   if (f == NULL) {
340     *result = NULL;
341     int saved_errno = errno;
342     RecordOSError(kNewLogger, saved_errno);
343     return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno);
344   } else {
345     *result = new ChromiumLogger(f);
346     return Status::OK();
347   }
348 }
349 
RecordOSError(MethodID method,DWORD error) const350 void ChromiumEnvWin::RecordOSError(MethodID method, DWORD error) const {
351   RecordErrorAt(method);
352   GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
353 }
354 
355 }  // namespace leveldb_env
356