Home | History | Annotate | Download | only in files
      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/files/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 {
     15 
     16 // Make sure our Whence mappings match the system headers.
     17 COMPILE_ASSERT(File::FROM_BEGIN   == FILE_BEGIN &&
     18                File::FROM_CURRENT == FILE_CURRENT &&
     19                File::FROM_END     == FILE_END, whence_matches_system);
     20 
     21 void File::InitializeUnsafe(const FilePath& name, uint32 flags) {
     22   base::ThreadRestrictions::AssertIOAllowed();
     23   DCHECK(!IsValid());
     24 
     25   DWORD disposition = 0;
     26 
     27   if (flags & FLAG_OPEN)
     28     disposition = OPEN_EXISTING;
     29 
     30   if (flags & FLAG_CREATE) {
     31     DCHECK(!disposition);
     32     disposition = CREATE_NEW;
     33   }
     34 
     35   if (flags & FLAG_OPEN_ALWAYS) {
     36     DCHECK(!disposition);
     37     disposition = OPEN_ALWAYS;
     38   }
     39 
     40   if (flags & FLAG_CREATE_ALWAYS) {
     41     DCHECK(!disposition);
     42     DCHECK(flags & FLAG_WRITE);
     43     disposition = CREATE_ALWAYS;
     44   }
     45 
     46   if (flags & FLAG_OPEN_TRUNCATED) {
     47     DCHECK(!disposition);
     48     DCHECK(flags & FLAG_WRITE);
     49     disposition = TRUNCATE_EXISTING;
     50   }
     51 
     52   if (!disposition) {
     53     NOTREACHED();
     54     return;
     55   }
     56 
     57   DWORD access = 0;
     58   if (flags & FLAG_WRITE)
     59     access = GENERIC_WRITE;
     60   if (flags & FLAG_APPEND) {
     61     DCHECK(!access);
     62     access = FILE_APPEND_DATA;
     63   }
     64   if (flags & FLAG_READ)
     65     access |= GENERIC_READ;
     66   if (flags & FLAG_WRITE_ATTRIBUTES)
     67     access |= FILE_WRITE_ATTRIBUTES;
     68   if (flags & FLAG_EXECUTE)
     69     access |= GENERIC_EXECUTE;
     70 
     71   DWORD sharing = (flags & FLAG_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ;
     72   if (!(flags & FLAG_EXCLUSIVE_WRITE))
     73     sharing |= FILE_SHARE_WRITE;
     74   if (flags & FLAG_SHARE_DELETE)
     75     sharing |= FILE_SHARE_DELETE;
     76 
     77   DWORD create_flags = 0;
     78   if (flags & FLAG_ASYNC)
     79     create_flags |= FILE_FLAG_OVERLAPPED;
     80   if (flags & FLAG_TEMPORARY)
     81     create_flags |= FILE_ATTRIBUTE_TEMPORARY;
     82   if (flags & FLAG_HIDDEN)
     83     create_flags |= FILE_ATTRIBUTE_HIDDEN;
     84   if (flags & FLAG_DELETE_ON_CLOSE)
     85     create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
     86   if (flags & FLAG_BACKUP_SEMANTICS)
     87     create_flags |= FILE_FLAG_BACKUP_SEMANTICS;
     88 
     89   file_.Set(CreateFile(name.value().c_str(), access, sharing, NULL,
     90                        disposition, create_flags, NULL));
     91 
     92   if (file_.IsValid()) {
     93     error_details_ = FILE_OK;
     94     async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
     95 
     96     if (flags & (FLAG_OPEN_ALWAYS))
     97       created_ = (ERROR_ALREADY_EXISTS != GetLastError());
     98     else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
     99       created_ = true;
    100   } else {
    101     error_details_ = OSErrorToFileError(GetLastError());
    102   }
    103 }
    104 
    105 bool File::IsValid() const {
    106   return file_.IsValid();
    107 }
    108 
    109 PlatformFile File::GetPlatformFile() const {
    110   return file_.Get();
    111 }
    112 
    113 PlatformFile File::TakePlatformFile() {
    114   return file_.Take();
    115 }
    116 
    117 void File::Close() {
    118   if (file_.IsValid()) {
    119     base::ThreadRestrictions::AssertIOAllowed();
    120     file_.Close();
    121   }
    122 }
    123 
    124 int64 File::Seek(Whence whence, int64 offset) {
    125   base::ThreadRestrictions::AssertIOAllowed();
    126   DCHECK(IsValid());
    127 
    128   LARGE_INTEGER distance, res;
    129   distance.QuadPart = offset;
    130   DWORD move_method = static_cast<DWORD>(whence);
    131   if (!SetFilePointerEx(file_.Get(), distance, &res, move_method))
    132     return -1;
    133   return res.QuadPart;
    134 }
    135 
    136 int File::Read(int64 offset, char* data, int size) {
    137   base::ThreadRestrictions::AssertIOAllowed();
    138   DCHECK(IsValid());
    139   DCHECK(!async_);
    140   if (size < 0)
    141     return -1;
    142 
    143   LARGE_INTEGER offset_li;
    144   offset_li.QuadPart = offset;
    145 
    146   OVERLAPPED overlapped = {0};
    147   overlapped.Offset = offset_li.LowPart;
    148   overlapped.OffsetHigh = offset_li.HighPart;
    149 
    150   DWORD bytes_read;
    151   if (::ReadFile(file_.Get(), data, size, &bytes_read, &overlapped))
    152     return bytes_read;
    153   if (ERROR_HANDLE_EOF == GetLastError())
    154     return 0;
    155 
    156   return -1;
    157 }
    158 
    159 int File::ReadAtCurrentPos(char* data, int size) {
    160   base::ThreadRestrictions::AssertIOAllowed();
    161   DCHECK(IsValid());
    162   DCHECK(!async_);
    163   if (size < 0)
    164     return -1;
    165 
    166   DWORD bytes_read;
    167   if (::ReadFile(file_.Get(), data, size, &bytes_read, NULL))
    168     return bytes_read;
    169   if (ERROR_HANDLE_EOF == GetLastError())
    170     return 0;
    171 
    172   return -1;
    173 }
    174 
    175 int File::ReadNoBestEffort(int64 offset, char* data, int size) {
    176   return Read(offset, data, size);
    177 }
    178 
    179 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
    180   return ReadAtCurrentPos(data, size);
    181 }
    182 
    183 int File::Write(int64 offset, const char* data, int size) {
    184   base::ThreadRestrictions::AssertIOAllowed();
    185   DCHECK(IsValid());
    186   DCHECK(!async_);
    187 
    188   LARGE_INTEGER offset_li;
    189   offset_li.QuadPart = offset;
    190 
    191   OVERLAPPED overlapped = {0};
    192   overlapped.Offset = offset_li.LowPart;
    193   overlapped.OffsetHigh = offset_li.HighPart;
    194 
    195   DWORD bytes_written;
    196   if (::WriteFile(file_.Get(), data, size, &bytes_written, &overlapped))
    197     return bytes_written;
    198 
    199   return -1;
    200 }
    201 
    202 int File::WriteAtCurrentPos(const char* data, int size) {
    203   base::ThreadRestrictions::AssertIOAllowed();
    204   DCHECK(IsValid());
    205   DCHECK(!async_);
    206   if (size < 0)
    207     return -1;
    208 
    209   DWORD bytes_written;
    210   if (::WriteFile(file_.Get(), data, size, &bytes_written, NULL))
    211     return bytes_written;
    212 
    213   return -1;
    214 }
    215 
    216 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
    217   return WriteAtCurrentPos(data, size);
    218 }
    219 
    220 int64 File::GetLength() {
    221   base::ThreadRestrictions::AssertIOAllowed();
    222   DCHECK(IsValid());
    223   LARGE_INTEGER size;
    224   if (!::GetFileSizeEx(file_.Get(), &size))
    225     return -1;
    226 
    227   return static_cast<int64>(size.QuadPart);
    228 }
    229 
    230 bool File::SetLength(int64 length) {
    231   base::ThreadRestrictions::AssertIOAllowed();
    232   DCHECK(IsValid());
    233 
    234   // Get the current file pointer.
    235   LARGE_INTEGER file_pointer;
    236   LARGE_INTEGER zero;
    237   zero.QuadPart = 0;
    238   if (!::SetFilePointerEx(file_.Get(), zero, &file_pointer, FILE_CURRENT))
    239     return false;
    240 
    241   LARGE_INTEGER length_li;
    242   length_li.QuadPart = length;
    243   // If length > file size, SetFilePointerEx() should extend the file
    244   // with zeroes on all Windows standard file systems (NTFS, FATxx).
    245   if (!::SetFilePointerEx(file_.Get(), length_li, NULL, FILE_BEGIN))
    246     return false;
    247 
    248   // Set the new file length and move the file pointer to its old position.
    249   // This is consistent with ftruncate()'s behavior, even when the file
    250   // pointer points to a location beyond the end of the file.
    251   // TODO(rvargas): Emulating ftruncate details seem suspicious and it is not
    252   // promised by the interface (nor was promised by PlatformFile). See if this
    253   // implementation detail can be removed.
    254   return ((::SetEndOfFile(file_.Get()) != FALSE) &&
    255           (::SetFilePointerEx(file_.Get(), file_pointer, NULL, FILE_BEGIN) !=
    256            FALSE));
    257 }
    258 
    259 bool File::Flush() {
    260   base::ThreadRestrictions::AssertIOAllowed();
    261   DCHECK(IsValid());
    262   return ::FlushFileBuffers(file_.Get()) != FALSE;
    263 }
    264 
    265 bool File::SetTimes(Time last_access_time, Time last_modified_time) {
    266   base::ThreadRestrictions::AssertIOAllowed();
    267   DCHECK(IsValid());
    268 
    269   FILETIME last_access_filetime = last_access_time.ToFileTime();
    270   FILETIME last_modified_filetime = last_modified_time.ToFileTime();
    271   return (::SetFileTime(file_.Get(), NULL, &last_access_filetime,
    272                         &last_modified_filetime) != FALSE);
    273 }
    274 
    275 bool File::GetInfo(Info* info) {
    276   base::ThreadRestrictions::AssertIOAllowed();
    277   DCHECK(IsValid());
    278 
    279   BY_HANDLE_FILE_INFORMATION file_info;
    280   if (!GetFileInformationByHandle(file_.Get(), &file_info))
    281     return false;
    282 
    283   LARGE_INTEGER size;
    284   size.HighPart = file_info.nFileSizeHigh;
    285   size.LowPart = file_info.nFileSizeLow;
    286   info->size = size.QuadPart;
    287   info->is_directory =
    288       (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
    289   info->is_symbolic_link = false;  // Windows doesn't have symbolic links.
    290   info->last_modified = base::Time::FromFileTime(file_info.ftLastWriteTime);
    291   info->last_accessed = base::Time::FromFileTime(file_info.ftLastAccessTime);
    292   info->creation_time = base::Time::FromFileTime(file_info.ftCreationTime);
    293   return true;
    294 }
    295 
    296 File::Error base::File::Lock() {
    297   DCHECK(IsValid());
    298   BOOL result = LockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
    299   if (!result)
    300     return OSErrorToFileError(GetLastError());
    301   return FILE_OK;
    302 }
    303 
    304 File::Error File::Unlock() {
    305   DCHECK(IsValid());
    306   BOOL result = UnlockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD);
    307   if (!result)
    308     return OSErrorToFileError(GetLastError());
    309   return FILE_OK;
    310 }
    311 
    312 // Static.
    313 File::Error File::OSErrorToFileError(DWORD last_error) {
    314   switch (last_error) {
    315     case ERROR_SHARING_VIOLATION:
    316       return FILE_ERROR_IN_USE;
    317     case ERROR_FILE_EXISTS:
    318       return FILE_ERROR_EXISTS;
    319     case ERROR_FILE_NOT_FOUND:
    320     case ERROR_PATH_NOT_FOUND:
    321       return FILE_ERROR_NOT_FOUND;
    322     case ERROR_ACCESS_DENIED:
    323       return FILE_ERROR_ACCESS_DENIED;
    324     case ERROR_TOO_MANY_OPEN_FILES:
    325       return FILE_ERROR_TOO_MANY_OPENED;
    326     case ERROR_OUTOFMEMORY:
    327     case ERROR_NOT_ENOUGH_MEMORY:
    328       return FILE_ERROR_NO_MEMORY;
    329     case ERROR_HANDLE_DISK_FULL:
    330     case ERROR_DISK_FULL:
    331     case ERROR_DISK_RESOURCES_EXHAUSTED:
    332       return FILE_ERROR_NO_SPACE;
    333     case ERROR_USER_MAPPED_FILE:
    334       return FILE_ERROR_INVALID_OPERATION;
    335     case ERROR_NOT_READY:
    336     case ERROR_SECTOR_NOT_FOUND:
    337     case ERROR_DEV_NOT_EXIST:
    338     case ERROR_IO_DEVICE:
    339     case ERROR_FILE_CORRUPT:
    340     case ERROR_DISK_CORRUPT:
    341       return FILE_ERROR_IO;
    342     default:
    343       UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Windows",
    344                                   last_error);
    345       return FILE_ERROR_FAILED;
    346   }
    347 }
    348 
    349 void File::SetPlatformFile(PlatformFile file) {
    350   file_.Set(file);
    351 }
    352 
    353 }  // namespace base
    354