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