Home | History | Annotate | Download | only in base
      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 {
     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 
    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 
    115 bool ClosePlatformFile(PlatformFile file) {
    116   base::ThreadRestrictions::AssertIOAllowed();
    117   return (CloseHandle(file) != 0);
    118 }
    119 
    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 
    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 
    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 
    170 int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, char* data,
    171                                  int size) {
    172   return ReadPlatformFile(file, offset, data, size);
    173 }
    174 
    175 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file,
    176                                        char* data, int size) {
    177   return ReadPlatformFileAtCurrentPos(file, data, size);
    178 }
    179 
    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 
    200 int WritePlatformFileAtCurrentPos(PlatformFile file, const char* data,
    201                                   int size) {
    202   return WritePlatformFile(file, 0, data, size);
    203 }
    204 
    205 int WritePlatformFileCurPosNoBestEffort(PlatformFile file,
    206                                         const char* data, int size) {
    207   return WritePlatformFile(file, 0, data, size);
    208 }
    209 
    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 
    236 bool FlushPlatformFile(PlatformFile file) {
    237   base::ThreadRestrictions::AssertIOAllowed();
    238   return ((file != kInvalidPlatformFileValue) && ::FlushFileBuffers(file));
    239 }
    240 
    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 
    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 
    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 
    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 
    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