xref: /aosp_15_r20/external/tensorflow/tensorflow/tsl/platform/windows/windows_file_system.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2015 Google Inc. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/tsl/platform/windows/windows_file_system.h"
17 
18 #include <Shlwapi.h>
19 #include <Windows.h>
20 #include <direct.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <io.h>
24 #undef StrCat
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <time.h>
29 
30 #include "tensorflow/core/platform/env.h"
31 #include "tensorflow/core/platform/errors.h"
32 #include "tensorflow/core/platform/file_system_helper.h"
33 #include "tensorflow/core/platform/logging.h"
34 #include "tensorflow/core/platform/strcat.h"
35 #include "tensorflow/core/protobuf/error_codes.pb.h"
36 #include "tensorflow/tsl/platform/windows/error_windows.h"
37 #include "tensorflow/tsl/platform/windows/wide_char.h"
38 
39 // TODO(mrry): Prevent this Windows.h #define from leaking out of our headers.
40 #undef DeleteFile
41 
42 namespace tensorflow {
43 
44 using ::tensorflow::errors::IOError;
45 
46 namespace {
47 
48 // RAII helpers for HANDLEs
__anon2210d87d0202(HANDLE h) 49 const auto CloseHandleFunc = [](HANDLE h) { ::CloseHandle(h); };
50 typedef std::unique_ptr<void, decltype(CloseHandleFunc)> UniqueCloseHandlePtr;
51 
IOErrorFromWindowsError(const string & context)52 inline Status IOErrorFromWindowsError(const string& context) {
53   auto last_error = ::GetLastError();
54   return IOError(
55       context + string(" : ") + internal::WindowsGetLastErrorMessage(),
56       last_error);
57 }
58 
59 // PLEASE NOTE: hfile is expected to be an async handle
60 // (i.e. opened with FILE_FLAG_OVERLAPPED)
pread(HANDLE hfile,char * src,size_t num_bytes,uint64_t offset)61 SSIZE_T pread(HANDLE hfile, char* src, size_t num_bytes, uint64_t offset) {
62   assert(num_bytes <= std::numeric_limits<DWORD>::max());
63   OVERLAPPED overlapped = {0};
64   ULARGE_INTEGER offset_union;
65   offset_union.QuadPart = offset;
66 
67   overlapped.Offset = offset_union.LowPart;
68   overlapped.OffsetHigh = offset_union.HighPart;
69   overlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
70 
71   if (NULL == overlapped.hEvent) {
72     return -1;
73   }
74 
75   SSIZE_T result = 0;
76 
77   unsigned long bytes_read = 0;
78   DWORD last_error = ERROR_SUCCESS;
79 
80   BOOL read_result = ::ReadFile(hfile, src, static_cast<DWORD>(num_bytes),
81                                 &bytes_read, &overlapped);
82   if (TRUE == read_result) {
83     result = bytes_read;
84   } else if ((FALSE == read_result) &&
85              ((last_error = GetLastError()) != ERROR_IO_PENDING)) {
86     result = (last_error == ERROR_HANDLE_EOF) ? 0 : -1;
87   } else {
88     if (ERROR_IO_PENDING ==
89         last_error) {  // Otherwise bytes_read already has the result.
90       BOOL overlapped_result =
91           ::GetOverlappedResult(hfile, &overlapped, &bytes_read, TRUE);
92       if (FALSE == overlapped_result) {
93         result = (::GetLastError() == ERROR_HANDLE_EOF) ? 0 : -1;
94       } else {
95         result = bytes_read;
96       }
97     }
98   }
99 
100   ::CloseHandle(overlapped.hEvent);
101 
102   return result;
103 }
104 
105 // read() based random-access
106 class WindowsRandomAccessFile : public RandomAccessFile {
107  private:
108   string filename_;
109   HANDLE hfile_;
110 
111  public:
WindowsRandomAccessFile(const string & fname,HANDLE hfile)112   WindowsRandomAccessFile(const string& fname, HANDLE hfile)
113       : filename_(fname), hfile_(hfile) {}
~WindowsRandomAccessFile()114   ~WindowsRandomAccessFile() override {
115     if (hfile_ != NULL && hfile_ != INVALID_HANDLE_VALUE) {
116       ::CloseHandle(hfile_);
117     }
118   }
119 
Name(StringPiece * result) const120   Status Name(StringPiece* result) const override {
121     *result = filename_;
122     return Status::OK();
123   }
124 
Read(uint64 offset,size_t n,StringPiece * result,char * scratch) const125   Status Read(uint64 offset, size_t n, StringPiece* result,
126               char* scratch) const override {
127     Status s;
128     char* dst = scratch;
129     while (n > 0 && s.ok()) {
130       size_t requested_read_length;
131       if (n > std::numeric_limits<DWORD>::max()) {
132         requested_read_length = std::numeric_limits<DWORD>::max();
133       } else {
134         requested_read_length = n;
135       }
136       SSIZE_T r = pread(hfile_, dst, requested_read_length, offset);
137       if (r > 0) {
138         offset += r;
139         dst += r;
140         n -= r;
141       } else if (r == 0) {
142         s = Status(error::OUT_OF_RANGE, "Read fewer bytes than requested");
143       } else if (errno == EINTR || errno == EAGAIN) {
144         // Retry
145       } else {
146         s = IOError(filename_, errno);
147       }
148     }
149     *result = StringPiece(scratch, dst - scratch);
150     return s;
151   }
152 
153 #if defined(TF_CORD_SUPPORT)
Read(uint64 offset,size_t n,absl::Cord * cord) const154   Status Read(uint64 offset, size_t n, absl::Cord* cord) const override {
155     if (n == 0) {
156       return Status::OK();
157     }
158     if (n < 0) {
159       return errors::InvalidArgument(
160           "Attempting to read ", n,
161           " bytes. You cannot read a negative number of bytes.");
162     }
163 
164     char* scratch = new char[n];
165     if (scratch == nullptr) {
166       return errors::ResourceExhausted("Unable to allocate ", n,
167                                        " bytes for file reading.");
168     }
169 
170     StringPiece tmp;
171     Status s = Read(offset, n, &tmp, scratch);
172 
173     absl::Cord tmp_cord = absl::MakeCordFromExternal(
174         absl::string_view(static_cast<char*>(scratch), tmp.size()),
175         [scratch](absl::string_view) { delete[] scratch; });
176     cord->Append(tmp_cord);
177     return s;
178   }
179 #endif
180 };
181 
182 class WindowsWritableFile : public WritableFile {
183  private:
184   string filename_;
185   HANDLE hfile_;
186 
187  public:
WindowsWritableFile(const string & fname,HANDLE hFile)188   WindowsWritableFile(const string& fname, HANDLE hFile)
189       : filename_(fname), hfile_(hFile) {}
190 
~WindowsWritableFile()191   ~WindowsWritableFile() override {
192     if (hfile_ != NULL && hfile_ != INVALID_HANDLE_VALUE) {
193       WindowsWritableFile::Close();
194     }
195   }
196 
Append(StringPiece data)197   Status Append(StringPiece data) override {
198     DWORD bytes_written = 0;
199     DWORD data_size = static_cast<DWORD>(data.size());
200     BOOL write_result =
201         ::WriteFile(hfile_, data.data(), data_size, &bytes_written, NULL);
202     if (FALSE == write_result) {
203       return IOErrorFromWindowsError("Failed to WriteFile: " + filename_);
204     }
205 
206     assert(size_t(bytes_written) == data.size());
207     return Status::OK();
208   }
209 
210 #if defined(TF_CORD_SUPPORT)
211   // \brief Append 'data' to the file.
Append(const absl::Cord & cord)212   Status Append(const absl::Cord& cord) override {
213     for (const auto& chunk : cord.Chunks()) {
214       DWORD bytes_written = 0;
215       DWORD data_size = static_cast<DWORD>(chunk.size());
216       BOOL write_result =
217           ::WriteFile(hfile_, chunk.data(), data_size, &bytes_written, NULL);
218       if (FALSE == write_result) {
219         return IOErrorFromWindowsError("Failed to WriteFile: " + filename_);
220       }
221 
222       assert(size_t(bytes_written) == chunk.size());
223     }
224     return Status::OK();
225   }
226 #endif
227 
Tell(int64 * position)228   Status Tell(int64* position) override {
229     Status result = Flush();
230     if (!result.ok()) {
231       return result;
232     }
233 
234     *position = SetFilePointer(hfile_, 0, NULL, FILE_CURRENT);
235 
236     if (*position == INVALID_SET_FILE_POINTER) {
237       return IOErrorFromWindowsError("Tell(SetFilePointer) failed for: " +
238                                      filename_);
239     }
240 
241     return Status::OK();
242   }
243 
Close()244   Status Close() override {
245     assert(INVALID_HANDLE_VALUE != hfile_);
246 
247     Status result = Flush();
248     if (!result.ok()) {
249       return result;
250     }
251 
252     if (FALSE == ::CloseHandle(hfile_)) {
253       return IOErrorFromWindowsError("CloseHandle failed for: " + filename_);
254     }
255 
256     hfile_ = INVALID_HANDLE_VALUE;
257     return Status::OK();
258   }
259 
Flush()260   Status Flush() override {
261     if (FALSE == ::FlushFileBuffers(hfile_)) {
262       return IOErrorFromWindowsError("FlushFileBuffers failed for: " +
263                                      filename_);
264     }
265     return Status::OK();
266   }
267 
Name(StringPiece * result) const268   Status Name(StringPiece* result) const override {
269     *result = filename_;
270     return Status::OK();
271   }
272 
Sync()273   Status Sync() override { return Flush(); }
274 };
275 
276 class WinReadOnlyMemoryRegion : public ReadOnlyMemoryRegion {
277  private:
278   const std::string filename_;
279   HANDLE hfile_;
280   HANDLE hmap_;
281 
282   const void* const address_;
283   const uint64 length_;
284 
285  public:
WinReadOnlyMemoryRegion(const std::string & filename,HANDLE hfile,HANDLE hmap,const void * address,uint64 length)286   WinReadOnlyMemoryRegion(const std::string& filename, HANDLE hfile,
287                           HANDLE hmap, const void* address, uint64 length)
288       : filename_(filename),
289         hfile_(hfile),
290         hmap_(hmap),
291         address_(address),
292         length_(length) {}
293 
~WinReadOnlyMemoryRegion()294   ~WinReadOnlyMemoryRegion() {
295     BOOL ret = ::UnmapViewOfFile(address_);
296     assert(ret);
297 
298     ret = ::CloseHandle(hmap_);
299     assert(ret);
300 
301     ret = ::CloseHandle(hfile_);
302     assert(ret);
303   }
304 
data()305   const void* data() override { return address_; }
length()306   uint64 length() override { return length_; }
307 };
308 
309 }  // namespace
310 
NewRandomAccessFile(const string & fname,TransactionToken * token,std::unique_ptr<RandomAccessFile> * result)311 Status WindowsFileSystem::NewRandomAccessFile(
312     const string& fname, TransactionToken* token,
313     std::unique_ptr<RandomAccessFile>* result) {
314   string translated_fname = TranslateName(fname);
315   std::wstring ws_translated_fname = Utf8ToWideChar(translated_fname);
316   result->reset();
317 
318   // Open the file for read-only random access
319   // Open in async mode which makes Windows allow more parallelism even
320   // if we need to do sync I/O on top of it.
321   DWORD file_flags = FILE_ATTRIBUTE_READONLY | FILE_FLAG_OVERLAPPED;
322   // Shared access is necessary for tests to pass
323   // almost all tests would work with a possible exception of fault_injection.
324   DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
325 
326   HANDLE hfile =
327       ::CreateFileW(ws_translated_fname.c_str(), GENERIC_READ, share_mode, NULL,
328                     OPEN_EXISTING, file_flags, NULL);
329 
330   if (INVALID_HANDLE_VALUE == hfile) {
331     string context = "NewRandomAccessFile failed to Create/Open: " + fname;
332     return IOErrorFromWindowsError(context);
333   }
334 
335   result->reset(new WindowsRandomAccessFile(translated_fname, hfile));
336   return Status::OK();
337 }
338 
NewWritableFile(const string & fname,TransactionToken * token,std::unique_ptr<WritableFile> * result)339 Status WindowsFileSystem::NewWritableFile(
340     const string& fname, TransactionToken* token,
341     std::unique_ptr<WritableFile>* result) {
342   string translated_fname = TranslateName(fname);
343   std::wstring ws_translated_fname = Utf8ToWideChar(translated_fname);
344   result->reset();
345 
346   DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
347   HANDLE hfile =
348       ::CreateFileW(ws_translated_fname.c_str(), GENERIC_WRITE, share_mode,
349                     NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
350 
351   if (INVALID_HANDLE_VALUE == hfile) {
352     string context = "Failed to create a NewWriteableFile: " + fname;
353     return IOErrorFromWindowsError(context);
354   }
355 
356   result->reset(new WindowsWritableFile(translated_fname, hfile));
357   return Status::OK();
358 }
359 
NewAppendableFile(const string & fname,TransactionToken * token,std::unique_ptr<WritableFile> * result)360 Status WindowsFileSystem::NewAppendableFile(
361     const string& fname, TransactionToken* token,
362     std::unique_ptr<WritableFile>* result) {
363   string translated_fname = TranslateName(fname);
364   std::wstring ws_translated_fname = Utf8ToWideChar(translated_fname);
365   result->reset();
366 
367   DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
368   HANDLE hfile =
369       ::CreateFileW(ws_translated_fname.c_str(), GENERIC_WRITE, share_mode,
370                     NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
371 
372   if (INVALID_HANDLE_VALUE == hfile) {
373     string context = "Failed to create a NewAppendableFile: " + fname;
374     return IOErrorFromWindowsError(context);
375   }
376 
377   UniqueCloseHandlePtr file_guard(hfile, CloseHandleFunc);
378 
379   DWORD file_ptr = ::SetFilePointer(hfile, NULL, NULL, FILE_END);
380   if (INVALID_SET_FILE_POINTER == file_ptr) {
381     string context = "Failed to create a NewAppendableFile: " + fname;
382     return IOErrorFromWindowsError(context);
383   }
384 
385   result->reset(new WindowsWritableFile(translated_fname, hfile));
386   file_guard.release();
387 
388   return Status::OK();
389 }
390 
NewReadOnlyMemoryRegionFromFile(const string & fname,TransactionToken * token,std::unique_ptr<ReadOnlyMemoryRegion> * result)391 Status WindowsFileSystem::NewReadOnlyMemoryRegionFromFile(
392     const string& fname, TransactionToken* token,
393     std::unique_ptr<ReadOnlyMemoryRegion>* result) {
394   string translated_fname = TranslateName(fname);
395   std::wstring ws_translated_fname = Utf8ToWideChar(translated_fname);
396   result->reset();
397   Status s = Status::OK();
398 
399   // Open the file for read-only
400   DWORD file_flags = FILE_ATTRIBUTE_READONLY;
401 
402   // Open in async mode which makes Windows allow more parallelism even
403   // if we need to do sync I/O on top of it.
404   file_flags |= FILE_FLAG_OVERLAPPED;
405 
406   DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
407   HANDLE hfile =
408       ::CreateFileW(ws_translated_fname.c_str(), GENERIC_READ, share_mode, NULL,
409                     OPEN_EXISTING, file_flags, NULL);
410 
411   if (INVALID_HANDLE_VALUE == hfile) {
412     return IOErrorFromWindowsError(
413         "NewReadOnlyMemoryRegionFromFile failed to Create/Open: " + fname);
414   }
415 
416   UniqueCloseHandlePtr file_guard(hfile, CloseHandleFunc);
417 
418   // Use mmap when virtual address-space is plentiful.
419   uint64_t file_size;
420   s = GetFileSize(translated_fname, &file_size);
421   if (s.ok()) {
422     // Will not map empty files
423     if (file_size == 0) {
424       return IOError(
425           "NewReadOnlyMemoryRegionFromFile failed to map empty file: " + fname,
426           EINVAL);
427     }
428 
429     HANDLE hmap = ::CreateFileMappingA(hfile, NULL, PAGE_READONLY,
430                                        0,  // Whole file at its present length
431                                        0,
432                                        NULL);  // Mapping name
433 
434     if (!hmap) {
435       string context =
436           "Failed to create file mapping for "
437           "NewReadOnlyMemoryRegionFromFile: " +
438           fname;
439       return IOErrorFromWindowsError(context);
440     }
441 
442     UniqueCloseHandlePtr map_guard(hmap, CloseHandleFunc);
443 
444     const void* mapped_region =
445         ::MapViewOfFileEx(hmap, FILE_MAP_READ,
446                           0,  // High DWORD of access start
447                           0,  // Low DWORD
448                           file_size,
449                           NULL);  // Let the OS choose the mapping
450 
451     if (!mapped_region) {
452       string context =
453           "Failed to MapViewOfFile for "
454           "NewReadOnlyMemoryRegionFromFile: " +
455           fname;
456       return IOErrorFromWindowsError(context);
457     }
458 
459     result->reset(new WinReadOnlyMemoryRegion(fname, hfile, hmap, mapped_region,
460                                               file_size));
461 
462     map_guard.release();
463     file_guard.release();
464   }
465 
466   return s;
467 }
468 
FileExists(const string & fname,TransactionToken * token)469 Status WindowsFileSystem::FileExists(const string& fname,
470                                      TransactionToken* token) {
471   constexpr int kOk = 0;
472   std::wstring ws_translated_fname = Utf8ToWideChar(TranslateName(fname));
473   if (_waccess(ws_translated_fname.c_str(), kOk) == 0) {
474     return Status::OK();
475   }
476   return errors::NotFound(fname, " not found");
477 }
478 
GetChildren(const string & dir,TransactionToken * token,std::vector<string> * result)479 Status WindowsFileSystem::GetChildren(const string& dir,
480                                       TransactionToken* token,
481                                       std::vector<string>* result) {
482   string translated_dir = TranslateName(dir);
483   std::wstring ws_translated_dir = Utf8ToWideChar(translated_dir);
484   result->clear();
485 
486   std::wstring pattern = ws_translated_dir;
487   if (!pattern.empty() && pattern.back() != '\\' && pattern.back() != '/') {
488     pattern += L"\\*";
489   } else {
490     pattern += L'*';
491   }
492 
493   WIN32_FIND_DATAW find_data;
494   HANDLE find_handle = ::FindFirstFileW(pattern.c_str(), &find_data);
495   if (find_handle == INVALID_HANDLE_VALUE) {
496     string context = "FindFirstFile failed for: " + translated_dir;
497     return IOErrorFromWindowsError(context);
498   }
499 
500   do {
501     string file_name = WideCharToUtf8(find_data.cFileName);
502     const StringPiece basename = file_name;
503     if (basename != "." && basename != "..") {
504       result->push_back(file_name);
505     }
506   } while (::FindNextFileW(find_handle, &find_data));
507 
508   if (!::FindClose(find_handle)) {
509     string context = "FindClose failed for: " + translated_dir;
510     return IOErrorFromWindowsError(context);
511   }
512 
513   return Status::OK();
514 }
515 
DeleteFile(const string & fname,TransactionToken * token)516 Status WindowsFileSystem::DeleteFile(const string& fname,
517                                      TransactionToken* token) {
518   Status result;
519   std::wstring file_name = Utf8ToWideChar(fname);
520   if (_wunlink(file_name.c_str()) != 0) {
521     result = IOError("Failed to delete a file: " + fname, errno);
522   }
523   return result;
524 }
525 
CreateDir(const string & name,TransactionToken * token)526 Status WindowsFileSystem::CreateDir(const string& name,
527                                     TransactionToken* token) {
528   Status result;
529   std::wstring ws_name = Utf8ToWideChar(name);
530   if (ws_name.empty()) {
531     return errors::AlreadyExists(name);
532   }
533   if (_wmkdir(ws_name.c_str()) != 0) {
534     result = IOError("Failed to create a directory: " + name, errno);
535   }
536   return result;
537 }
538 
DeleteDir(const string & name,TransactionToken * token)539 Status WindowsFileSystem::DeleteDir(const string& name,
540                                     TransactionToken* token) {
541   Status result;
542   std::wstring ws_name = Utf8ToWideChar(name);
543   if (_wrmdir(ws_name.c_str()) != 0) {
544     result = IOError("Failed to remove a directory: " + name, errno);
545   }
546   return result;
547 }
548 
GetFileSize(const string & fname,TransactionToken * token,uint64 * size)549 Status WindowsFileSystem::GetFileSize(const string& fname,
550                                       TransactionToken* token, uint64* size) {
551   string translated_fname = TranslateName(fname);
552   std::wstring ws_translated_dir = Utf8ToWideChar(translated_fname);
553   Status result;
554   WIN32_FILE_ATTRIBUTE_DATA attrs;
555   if (TRUE == ::GetFileAttributesExW(ws_translated_dir.c_str(),
556                                      GetFileExInfoStandard, &attrs)) {
557     ULARGE_INTEGER file_size;
558     file_size.HighPart = attrs.nFileSizeHigh;
559     file_size.LowPart = attrs.nFileSizeLow;
560     *size = file_size.QuadPart;
561   } else {
562     string context = "Can not get size for: " + fname;
563     result = IOErrorFromWindowsError(context);
564   }
565   return result;
566 }
567 
IsDirectory(const string & fname,TransactionToken * token)568 Status WindowsFileSystem::IsDirectory(const string& fname,
569                                       TransactionToken* token) {
570   TF_RETURN_IF_ERROR(FileExists(fname));
571   std::wstring ws_translated_fname = Utf8ToWideChar(TranslateName(fname));
572   if (PathIsDirectoryW(ws_translated_fname.c_str())) {
573     return Status::OK();
574   }
575   return Status(tensorflow::error::FAILED_PRECONDITION, "Not a directory");
576 }
577 
RenameFile(const string & src,const string & target,TransactionToken * token)578 Status WindowsFileSystem::RenameFile(const string& src, const string& target,
579                                      TransactionToken* token) {
580   // rename() is not capable of replacing the existing file as on Linux
581   // so use OS API directly
582   std::wstring ws_translated_src = Utf8ToWideChar(TranslateName(src));
583   std::wstring ws_translated_target = Utf8ToWideChar(TranslateName(target));
584 
585   // Calling MoveFileExW with the MOVEFILE_REPLACE_EXISTING flag can fail if
586   // another process has a handle to the file that it didn't close yet. On the
587   // other hand, calling DeleteFileW + MoveFileExW will work in that scenario
588   // because it allows the process to keep using the old handle while also
589   // creating a new handle for the new file.
590   WIN32_FIND_DATAW find_file_data;
591   HANDLE target_file_handle =
592       ::FindFirstFileW(ws_translated_target.c_str(), &find_file_data);
593   if (target_file_handle != INVALID_HANDLE_VALUE) {
594     if (!::DeleteFileW(ws_translated_target.c_str())) {
595       ::FindClose(target_file_handle);
596       return IOErrorFromWindowsError(
597           strings::StrCat("Failed to rename: ", src, " to: ", target));
598     }
599     ::FindClose(target_file_handle);
600   }
601 
602   if (!::MoveFileExW(ws_translated_src.c_str(), ws_translated_target.c_str(),
603                      0)) {
604     return IOErrorFromWindowsError(
605         strings::StrCat("Failed to rename: ", src, " to: ", target));
606   }
607 
608   return Status::OK();
609 }
610 
GetMatchingPaths(const string & pattern,TransactionToken * token,std::vector<string> * results)611 Status WindowsFileSystem::GetMatchingPaths(const string& pattern,
612                                            TransactionToken* token,
613                                            std::vector<string>* results) {
614   // NOTE(mrry): The existing implementation of FileSystem::GetMatchingPaths()
615   // does not handle Windows paths containing backslashes correctly. Since
616   // Windows APIs will accept forward and backslashes equivalently, we
617   // convert the pattern to use forward slashes exclusively. Note that this
618   // is not ideal, since the API expects backslash as an escape character,
619   // but no code appears to rely on this behavior.
620   string converted_pattern(pattern);
621   std::replace(converted_pattern.begin(), converted_pattern.end(), '\\', '/');
622   TF_RETURN_IF_ERROR(internal::GetMatchingPaths(this, Env::Default(),
623                                                 converted_pattern, results));
624   for (string& result : *results) {
625     std::replace(result.begin(), result.end(), '/', '\\');
626   }
627   return Status::OK();
628 }
629 
Match(const string & filename,const string & pattern)630 bool WindowsFileSystem::Match(const string& filename, const string& pattern) {
631   std::wstring ws_path(Utf8ToWideChar(filename));
632   std::wstring ws_pattern(Utf8ToWideChar(pattern));
633   return PathMatchSpecW(ws_path.c_str(), ws_pattern.c_str()) == TRUE;
634 }
635 
Stat(const string & fname,TransactionToken * token,FileStatistics * stat)636 Status WindowsFileSystem::Stat(const string& fname, TransactionToken* token,
637                                FileStatistics* stat) {
638   Status result;
639   struct _stat64 sbuf;
640   std::wstring ws_translated_fname = Utf8ToWideChar(TranslateName(fname));
641   if (_wstat64(ws_translated_fname.c_str(), &sbuf) != 0) {
642     result = IOError(fname, errno);
643   } else {
644     stat->mtime_nsec = sbuf.st_mtime * 1e9;
645     stat->length = sbuf.st_size;
646     stat->is_directory = IsDirectory(fname).ok();
647   }
648   return result;
649 }
650 
651 }  // namespace tensorflow
652