• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium 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.
4 
5 #include "base/platform_file.h"
6 
7 #include <io.h>
8 
9 #include "base/files/file_path.h"
10 #include "base/logging.h"
11 #include "base/metrics/sparse_histogram.h"
12 #include "base/threading/thread_restrictions.h"
13 
14 namespace base {
CreatePlatformFileUnsafe(const FilePath & name,int flags,bool * created,PlatformFileError * error)15 PlatformFile CreatePlatformFileUnsafe(const FilePath& name,
16                                       int flags,
17                                       bool* created,
18                                       PlatformFileError* error) {
19   base::ThreadRestrictions::AssertIOAllowed();
20 
21   DWORD disposition = 0;
22   if (created)
23     *created = false;
24 
25   if (flags & PLATFORM_FILE_OPEN)
26     disposition = OPEN_EXISTING;
27 
28   if (flags & PLATFORM_FILE_CREATE) {
29     DCHECK(!disposition);
30     disposition = CREATE_NEW;
31   }
32 
33   if (flags & PLATFORM_FILE_OPEN_ALWAYS) {
34     DCHECK(!disposition);
35     disposition = OPEN_ALWAYS;
36   }
37 
38   if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
39     DCHECK(!disposition);
40     disposition = CREATE_ALWAYS;
41   }
42 
43   if (flags & PLATFORM_FILE_OPEN_TRUNCATED) {
44     DCHECK(!disposition);
45     DCHECK(flags & PLATFORM_FILE_WRITE);
46     disposition = TRUNCATE_EXISTING;
47   }
48 
49   if (!disposition) {
50     NOTREACHED();
51     return NULL;
52   }
53 
54   DWORD access = 0;
55   if (flags & PLATFORM_FILE_WRITE)
56     access = GENERIC_WRITE;
57   if (flags & PLATFORM_FILE_APPEND) {
58     DCHECK(!access);
59     access = FILE_APPEND_DATA;
60   }
61   if (flags & PLATFORM_FILE_READ)
62     access |= GENERIC_READ;
63   if (flags & PLATFORM_FILE_WRITE_ATTRIBUTES)
64     access |= FILE_WRITE_ATTRIBUTES;
65   if (flags & PLATFORM_FILE_EXECUTE)
66     access |= GENERIC_EXECUTE;
67 
68   DWORD sharing = (flags & PLATFORM_FILE_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ;
69   if (!(flags & PLATFORM_FILE_EXCLUSIVE_WRITE))
70     sharing |= FILE_SHARE_WRITE;
71   if (flags & PLATFORM_FILE_SHARE_DELETE)
72     sharing |= FILE_SHARE_DELETE;
73 
74   DWORD create_flags = 0;
75   if (flags & PLATFORM_FILE_ASYNC)
76     create_flags |= FILE_FLAG_OVERLAPPED;
77   if (flags & PLATFORM_FILE_TEMPORARY)
78     create_flags |= FILE_ATTRIBUTE_TEMPORARY;
79   if (flags & PLATFORM_FILE_HIDDEN)
80     create_flags |= FILE_ATTRIBUTE_HIDDEN;
81   if (flags & PLATFORM_FILE_DELETE_ON_CLOSE)
82     create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
83   if (flags & PLATFORM_FILE_BACKUP_SEMANTICS)
84     create_flags |= FILE_FLAG_BACKUP_SEMANTICS;
85 
86   HANDLE file = CreateFile(name.value().c_str(), access, sharing, NULL,
87                            disposition, create_flags, NULL);
88 
89   if (created && (INVALID_HANDLE_VALUE != file)) {
90     if (flags & (PLATFORM_FILE_OPEN_ALWAYS))
91       *created = (ERROR_ALREADY_EXISTS != GetLastError());
92     else if (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE))
93       *created = true;
94   }
95 
96   if (error) {
97     if (file != kInvalidPlatformFileValue)
98       *error = PLATFORM_FILE_OK;
99     else
100       *error = LastErrorToPlatformFileError(GetLastError());
101   }
102 
103   return file;
104 }
105 
FdopenPlatformFile(PlatformFile file,const char * mode)106 FILE* FdopenPlatformFile(PlatformFile file, const char* mode) {
107   if (file == kInvalidPlatformFileValue)
108     return NULL;
109   int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0);
110   if (fd < 0)
111     return NULL;
112   return _fdopen(fd, mode);
113 }
114 
ClosePlatformFile(PlatformFile file)115 bool ClosePlatformFile(PlatformFile file) {
116   base::ThreadRestrictions::AssertIOAllowed();
117   return (CloseHandle(file) != 0);
118 }
119 
SeekPlatformFile(PlatformFile file,PlatformFileWhence whence,int64 offset)120 int64 SeekPlatformFile(PlatformFile file,
121                        PlatformFileWhence whence,
122                        int64 offset) {
123   base::ThreadRestrictions::AssertIOAllowed();
124   if (file == kInvalidPlatformFileValue || offset < 0)
125     return -1;
126 
127   LARGE_INTEGER distance, res;
128   distance.QuadPart = offset;
129   DWORD move_method = static_cast<DWORD>(whence);
130   if (!SetFilePointerEx(file, distance, &res, move_method))
131     return -1;
132   return res.QuadPart;
133 }
134 
ReadPlatformFile(PlatformFile file,int64 offset,char * data,int size)135 int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
136   base::ThreadRestrictions::AssertIOAllowed();
137   if (file == kInvalidPlatformFileValue || size < 0)
138     return -1;
139 
140   LARGE_INTEGER offset_li;
141   offset_li.QuadPart = offset;
142 
143   OVERLAPPED overlapped = {0};
144   overlapped.Offset = offset_li.LowPart;
145   overlapped.OffsetHigh = offset_li.HighPart;
146 
147   DWORD bytes_read;
148   if (::ReadFile(file, data, size, &bytes_read, &overlapped) != 0)
149     return bytes_read;
150   if (ERROR_HANDLE_EOF == GetLastError())
151     return 0;
152 
153   return -1;
154 }
155 
ReadPlatformFileAtCurrentPos(PlatformFile file,char * data,int size)156 int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) {
157   base::ThreadRestrictions::AssertIOAllowed();
158   if (file == kInvalidPlatformFileValue || size < 0)
159     return -1;
160 
161   DWORD bytes_read;
162   if (::ReadFile(file, data, size, &bytes_read, NULL) != 0)
163     return bytes_read;
164   if (ERROR_HANDLE_EOF == GetLastError())
165     return 0;
166 
167   return -1;
168 }
169 
ReadPlatformFileNoBestEffort(PlatformFile file,int64 offset,char * data,int size)170 int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, char* data,
171                                  int size) {
172   return ReadPlatformFile(file, offset, data, size);
173 }
174 
ReadPlatformFileCurPosNoBestEffort(PlatformFile file,char * data,int size)175 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file,
176                                        char* data, int size) {
177   return ReadPlatformFileAtCurrentPos(file, data, size);
178 }
179 
WritePlatformFile(PlatformFile file,int64 offset,const char * data,int size)180 int WritePlatformFile(PlatformFile file, int64 offset,
181                       const char* data, int size) {
182   base::ThreadRestrictions::AssertIOAllowed();
183   if (file == kInvalidPlatformFileValue)
184     return -1;
185 
186   LARGE_INTEGER offset_li;
187   offset_li.QuadPart = offset;
188 
189   OVERLAPPED overlapped = {0};
190   overlapped.Offset = offset_li.LowPart;
191   overlapped.OffsetHigh = offset_li.HighPart;
192 
193   DWORD bytes_written;
194   if (::WriteFile(file, data, size, &bytes_written, &overlapped) != 0)
195     return bytes_written;
196 
197   return -1;
198 }
199 
WritePlatformFileAtCurrentPos(PlatformFile file,const char * data,int size)200 int WritePlatformFileAtCurrentPos(PlatformFile file, const char* data,
201                                   int size) {
202   return WritePlatformFile(file, 0, data, size);
203 }
204 
WritePlatformFileCurPosNoBestEffort(PlatformFile file,const char * data,int size)205 int WritePlatformFileCurPosNoBestEffort(PlatformFile file,
206                                         const char* data, int size) {
207   return WritePlatformFile(file, 0, data, size);
208 }
209 
TruncatePlatformFile(PlatformFile file,int64 length)210 bool TruncatePlatformFile(PlatformFile file, int64 length) {
211   base::ThreadRestrictions::AssertIOAllowed();
212   if (file == kInvalidPlatformFileValue)
213     return false;
214 
215   // Get the current file pointer.
216   LARGE_INTEGER file_pointer;
217   LARGE_INTEGER zero;
218   zero.QuadPart = 0;
219   if (::SetFilePointerEx(file, zero, &file_pointer, FILE_CURRENT) == 0)
220     return false;
221 
222   LARGE_INTEGER length_li;
223   length_li.QuadPart = length;
224   // If length > file size, SetFilePointerEx() should extend the file
225   // with zeroes on all Windows standard file systems (NTFS, FATxx).
226   if (!::SetFilePointerEx(file, length_li, NULL, FILE_BEGIN))
227     return false;
228 
229   // Set the new file length and move the file pointer to its old position.
230   // This is consistent with ftruncate()'s behavior, even when the file
231   // pointer points to a location beyond the end of the file.
232   return ((::SetEndOfFile(file) != 0) &&
233           (::SetFilePointerEx(file, file_pointer, NULL, FILE_BEGIN) != 0));
234 }
235 
FlushPlatformFile(PlatformFile file)236 bool FlushPlatformFile(PlatformFile file) {
237   base::ThreadRestrictions::AssertIOAllowed();
238   return ((file != kInvalidPlatformFileValue) && ::FlushFileBuffers(file));
239 }
240 
TouchPlatformFile(PlatformFile file,const base::Time & last_access_time,const base::Time & last_modified_time)241 bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time,
242                        const base::Time& last_modified_time) {
243   base::ThreadRestrictions::AssertIOAllowed();
244   if (file == kInvalidPlatformFileValue)
245     return false;
246 
247   FILETIME last_access_filetime = last_access_time.ToFileTime();
248   FILETIME last_modified_filetime = last_modified_time.ToFileTime();
249   return (::SetFileTime(file, NULL, &last_access_filetime,
250                         &last_modified_filetime) != 0);
251 }
252 
GetPlatformFileInfo(PlatformFile file,PlatformFileInfo * info)253 bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
254   base::ThreadRestrictions::AssertIOAllowed();
255   if (!info)
256     return false;
257 
258   BY_HANDLE_FILE_INFORMATION file_info;
259   if (GetFileInformationByHandle(file, &file_info) == 0)
260     return false;
261 
262   LARGE_INTEGER size;
263   size.HighPart = file_info.nFileSizeHigh;
264   size.LowPart = file_info.nFileSizeLow;
265   info->size = size.QuadPart;
266   info->is_directory =
267       (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
268   info->is_symbolic_link = false; // Windows doesn't have symbolic links.
269   info->last_modified = base::Time::FromFileTime(file_info.ftLastWriteTime);
270   info->last_accessed = base::Time::FromFileTime(file_info.ftLastAccessTime);
271   info->creation_time = base::Time::FromFileTime(file_info.ftCreationTime);
272   return true;
273 }
274 
LockPlatformFile(PlatformFile file)275 PlatformFileError LockPlatformFile(PlatformFile file) {
276   BOOL result = LockFile(file, 0, 0, MAXDWORD, MAXDWORD);
277   if (!result)
278     return LastErrorToPlatformFileError(GetLastError());
279   return PLATFORM_FILE_OK;
280 }
281 
UnlockPlatformFile(PlatformFile file)282 PlatformFileError UnlockPlatformFile(PlatformFile file) {
283   BOOL result = UnlockFile(file, 0, 0, MAXDWORD, MAXDWORD);
284   if (!result)
285     return LastErrorToPlatformFileError(GetLastError());
286   return PLATFORM_FILE_OK;
287 }
288 
LastErrorToPlatformFileError(DWORD last_error)289 PlatformFileError LastErrorToPlatformFileError(DWORD last_error) {
290   switch (last_error) {
291     case ERROR_SHARING_VIOLATION:
292       return PLATFORM_FILE_ERROR_IN_USE;
293     case ERROR_FILE_EXISTS:
294       return PLATFORM_FILE_ERROR_EXISTS;
295     case ERROR_FILE_NOT_FOUND:
296     case ERROR_PATH_NOT_FOUND:
297       return PLATFORM_FILE_ERROR_NOT_FOUND;
298     case ERROR_ACCESS_DENIED:
299       return PLATFORM_FILE_ERROR_ACCESS_DENIED;
300     case ERROR_TOO_MANY_OPEN_FILES:
301       return PLATFORM_FILE_ERROR_TOO_MANY_OPENED;
302     case ERROR_OUTOFMEMORY:
303     case ERROR_NOT_ENOUGH_MEMORY:
304       return PLATFORM_FILE_ERROR_NO_MEMORY;
305     case ERROR_HANDLE_DISK_FULL:
306     case ERROR_DISK_FULL:
307     case ERROR_DISK_RESOURCES_EXHAUSTED:
308       return PLATFORM_FILE_ERROR_NO_SPACE;
309     case ERROR_USER_MAPPED_FILE:
310       return PLATFORM_FILE_ERROR_INVALID_OPERATION;
311     case ERROR_NOT_READY:
312     case ERROR_SECTOR_NOT_FOUND:
313     case ERROR_DEV_NOT_EXIST:
314     case ERROR_IO_DEVICE:
315     case ERROR_FILE_CORRUPT:
316     case ERROR_DISK_CORRUPT:
317       return PLATFORM_FILE_ERROR_IO;
318     default:
319       UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Windows",
320                                   last_error);
321       return PLATFORM_FILE_ERROR_FAILED;
322   }
323 }
324 
325 }  // namespace base
326