1 /* Copyright 2012-present Facebook, Inc.
2  * Licensed under the Apache License, Version 2.0 */
3 
4 #include "FileDescriptor.h"
5 #include "FileSystem.h"
6 #include "watchman.h"
7 #ifdef __APPLE__
8 #include <sys/attr.h>
9 #include <sys/utsname.h>
10 #include <sys/vnode.h>
11 #endif
12 #include <system_error>
13 #ifdef _WIN32
14 #include "WinIoCtl.h"
15 #endif
16 #include "watchman_scopeguard.h"
17 
18 #if defined(_WIN32) || defined(O_PATH)
19 #define CAN_OPEN_SYMLINKS 1
20 #else
21 #define CAN_OPEN_SYMLINKS 0
22 #endif
23 
24 #ifdef _WIN32
25 // We declare our own copy here because Ntifs.h is not included in the
26 // standard install of the Visual Studio Community compiler.
27 namespace {
28 struct REPARSE_DATA_BUFFER {
29   ULONG ReparseTag;
30   USHORT ReparseDataLength;
31   USHORT Reserved;
32   union {
33     struct {
34       USHORT SubstituteNameOffset;
35       USHORT SubstituteNameLength;
36       USHORT PrintNameOffset;
37       USHORT PrintNameLength;
38       ULONG Flags;
39       WCHAR PathBuffer[1];
40     } SymbolicLinkReparseBuffer;
41     struct {
42       USHORT SubstituteNameOffset;
43       USHORT SubstituteNameLength;
44       USHORT PrintNameOffset;
45       USHORT PrintNameLength;
46       WCHAR PathBuffer[1];
47     } MountPointReparseBuffer;
48     struct {
49       UCHAR DataBuffer[1];
50     } GenericReparseBuffer;
51   };
52 };
53 }
54 #endif
55 
56 namespace watchman {
57 
~FileDescriptor()58 FileDescriptor::~FileDescriptor() {
59   close();
60 }
61 
normalizeHandleValue(system_handle_type h)62 FileDescriptor::system_handle_type FileDescriptor::normalizeHandleValue(
63     system_handle_type h) {
64 #ifdef _WIN32
65   // Windows uses both 0 and INVALID_HANDLE_VALUE as invalid handle values.
66   if (h == intptr_t(INVALID_HANDLE_VALUE) || h == 0) {
67     return FileDescriptor::kInvalid;
68   }
69 #else
70   // Posix defines -1 to be an invalid value, but we'll also recognize and
71   // normalize any negative descriptor value.
72   if (h < 0) {
73     return FileDescriptor::kInvalid;
74   }
75 #endif
76   return h;
77 }
78 
FileDescriptor(FileDescriptor::system_handle_type fd)79 FileDescriptor::FileDescriptor(FileDescriptor::system_handle_type fd)
80     : fd_(normalizeHandleValue(fd)) {}
81 
FileDescriptor(FileDescriptor::system_handle_type fd,const char * operation)82 FileDescriptor::FileDescriptor(
83     FileDescriptor::system_handle_type fd,
84     const char* operation)
85     : fd_(normalizeHandleValue(fd)) {
86   if (fd_ == kInvalid) {
87     throw std::system_error(
88         errno,
89         std::generic_category(),
90         std::string(operation) + ": " + strerror(errno));
91   }
92 }
93 
FileDescriptor(FileDescriptor && other)94 FileDescriptor::FileDescriptor(FileDescriptor&& other) noexcept
95     : fd_(other.release()) {}
96 
operator =(FileDescriptor && other)97 FileDescriptor& FileDescriptor::operator=(FileDescriptor&& other) noexcept {
98   close();
99   fd_ = other.fd_;
100   other.fd_ = kInvalid;
101   return *this;
102 }
103 
close()104 void FileDescriptor::close() {
105   if (fd_ != kInvalid) {
106 #ifndef _WIN32
107     ::close(fd_);
108 #else
109     CloseHandle((HANDLE)fd_);
110 #endif
111     fd_ = kInvalid;
112   }
113 }
114 
release()115 FileDescriptor::system_handle_type FileDescriptor::release() {
116   system_handle_type result = fd_;
117   fd_ = kInvalid;
118   return result;
119 }
120 
setCloExec()121 void FileDescriptor::setCloExec() {
122 #ifndef _WIN32
123   ignore_result(fcntl(fd_, F_SETFD, FD_CLOEXEC));
124 #endif
125 }
126 
setNonBlock()127 void FileDescriptor::setNonBlock() {
128 #ifndef _WIN32
129   ignore_result(fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL) | O_NONBLOCK));
130 #endif
131 }
132 
clearNonBlock()133 void FileDescriptor::clearNonBlock() {
134 #ifndef _WIN32
135   ignore_result(fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL) & ~O_NONBLOCK));
136 #endif
137 }
138 
isNonBlock() const139 bool FileDescriptor::isNonBlock() const {
140 #ifndef _WIN32
141   return (fcntl(fd_, F_GETFL) & O_NONBLOCK) == O_NONBLOCK;
142 #else
143   return false;
144 #endif
145 }
146 
147 #if !CAN_OPEN_SYMLINKS
148 /** Checks that the basename component of the input path exactly
149  * matches the canonical case of the path on disk.
150  * It only makes sense to call this function on a case insensitive filesystem.
151  * If the case does not match, throws an exception. */
checkCanonicalBaseName(const char * path)152 static void checkCanonicalBaseName(const char *path) {
153 #ifdef __APPLE__
154   struct attrlist attrlist;
155   struct {
156     uint32_t len;
157     attrreference_t ref;
158     char canonical_name[WATCHMAN_NAME_MAX];
159   } vomit;
160   w_string_piece pathPiece(path);
161   auto base = pathPiece.baseName();
162 
163   memset(&attrlist, 0, sizeof(attrlist));
164   attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
165   attrlist.commonattr = ATTR_CMN_NAME;
166 
167   if (getattrlist(path, &attrlist, &vomit, sizeof(vomit), FSOPT_NOFOLLOW) ==
168       -1) {
169     throw std::system_error(errno, std::generic_category(),
170                             to<std::string>("checkCanonicalBaseName(", path,
171                                             "): getattrlist failed"));
172   }
173 
174   w_string_piece name(((char *)&vomit.ref) + vomit.ref.attr_dataoffset);
175   if (name != base) {
176     throw std::system_error(
177         ENOENT, std::generic_category(),
178         to<std::string>("checkCanonicalBaseName(", path, "): (", name,
179                         ") doesn't match canonical base (", base, ")"));
180   }
181 #else
182   // Older Linux and BSDish systems are in this category.
183   // This is the awful portable fallback used in the absence of
184   // a system specific way to detect this.
185   w_string_piece pathPiece(path);
186   auto parent = pathPiece.dirName().asWString();
187   auto dir = w_dir_open(parent.c_str());
188   auto base = pathPiece.baseName();
189 
190   while (true) {
191     auto ent = dir->readDir();
192     if (!ent) {
193       // We didn't find an entry that exactly matched -> fail
194       throw std::system_error(
195           ENOENT, std::generic_category(),
196           to<std::string>("checkCanonicalBaseName(", path,
197                           "): no match found in parent dir"));
198     }
199     // Note: we don't break out early if we get a case-insensitive match
200     // because the dir may contain multiple representations of the same
201     // name.  For example, Bash-for-Windows has dirs that contain both
202     // "pod" and "Pod" dirs in its perl installation.  We want to make
203     // sure that we've observed all of the entries in the dir before
204     // giving up.
205     if (w_string_piece(ent->d_name) == base) {
206       // Exact match; all is good!
207       return;
208     }
209   }
210 #endif
211 }
212 #endif
213 
openFileHandle(const char * path,const OpenFileHandleOptions & opts)214 FileDescriptor openFileHandle(const char *path,
215                               const OpenFileHandleOptions &opts) {
216 #ifndef _WIN32
217   int flags = (!opts.followSymlinks ? O_NOFOLLOW : 0) |
218               (opts.closeOnExec ? O_CLOEXEC : 0) |
219 #ifdef O_PATH
220               (opts.metaDataOnly ? O_PATH : 0) |
221 #endif
222               ((opts.readContents && opts.writeContents)
223                    ? O_RDWR
224                    : (opts.writeContents ? O_WRONLY
225                                          : opts.readContents ? O_RDONLY : 0)) |
226               (opts.create ? O_CREAT : 0) |
227               (opts.exclusiveCreate ? O_EXCL : 0) |
228               (opts.truncate ? O_TRUNC : 0);
229 
230   auto fd = open(path, flags);
231   if (fd == -1) {
232     int err = errno;
233     throw std::system_error(
234         err, std::generic_category(), to<std::string>("open: ", path));
235   }
236   FileDescriptor file(fd);
237 #else // _WIN32
238   DWORD access = 0, share = 0, create = 0, attrs = 0;
239   DWORD err;
240   SECURITY_ATTRIBUTES sec;
241 
242   if (!strcmp(path, "/dev/null")) {
243     path = "NUL:";
244   }
245 
246   auto wpath = w_string_piece(path).asWideUNC();
247 
248   if (opts.metaDataOnly) {
249     access = 0;
250   } else {
251     if (opts.writeContents) {
252       access |= GENERIC_WRITE;
253     }
254     if (opts.readContents) {
255       access |= GENERIC_READ;
256     }
257   }
258 
259   // We want more posix-y behavior by default
260   share = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
261 
262   memset(&sec, 0, sizeof(sec));
263   sec.nLength = sizeof(sec);
264   sec.bInheritHandle = TRUE;
265   if (opts.closeOnExec) {
266     sec.bInheritHandle = FALSE;
267   }
268 
269   if (opts.create && opts.exclusiveCreate) {
270     create = CREATE_NEW;
271   } else if (opts.create && opts.truncate) {
272     create = CREATE_ALWAYS;
273   } else if (opts.create) {
274     create = OPEN_ALWAYS;
275   } else if (opts.truncate) {
276     create = TRUNCATE_EXISTING;
277   } else {
278     create = OPEN_EXISTING;
279   }
280 
281   attrs = FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_BACKUP_SEMANTICS;
282   if (!opts.followSymlinks) {
283     attrs |= FILE_FLAG_OPEN_REPARSE_POINT;
284   }
285 
286   FileDescriptor file(intptr_t(
287       CreateFileW(wpath.c_str(), access, share, &sec, create, attrs, nullptr)));
288   err = GetLastError();
289   if (!file) {
290     throw std::system_error(
291         err,
292         std::system_category(),
293         std::string("CreateFileW for openFileHandle: ") + path);
294   }
295 
296 #endif
297 
298   if (!opts.strictNameChecks) {
299     return file;
300   }
301 
302   auto opened = file.getOpenedPath();
303   if (w_string_piece(opened).pathIsEqual(path)) {
304 #if !CAN_OPEN_SYMLINKS
305     CaseSensitivity caseSensitive = opts.caseSensitive;
306     if (caseSensitive == CaseSensitivity::Unknown) {
307       caseSensitive = getCaseSensitivityForPath(path);
308     }
309     if (caseSensitive == CaseSensitivity::CaseInSensitive) {
310       // We need to perform one extra check for case-insensitive
311       // paths to make sure that we didn't accidentally open
312       // the wrong case name.
313       checkCanonicalBaseName(path);
314     }
315 #endif
316     return file;
317   }
318 
319   throw std::system_error(
320       ENOENT, std::generic_category(),
321       to<std::string>("open(", path,
322                       "): opened path doesn't match canonical path ", opened));
323 }
324 
getInfo() const325 FileInformation FileDescriptor::getInfo() const {
326 #ifndef _WIN32
327   struct stat st;
328   if (fstat(fd_, &st)) {
329     int err = errno;
330     throw std::system_error(err, std::generic_category(), "fstat");
331   }
332   return FileInformation(st);
333 #else // _WIN32
334   FILE_BASIC_INFO binfo;
335   FILE_STANDARD_INFO sinfo;
336 
337   if (!GetFileInformationByHandleEx(
338           (HANDLE)handle(), FileBasicInfo, &binfo, sizeof(binfo))) {
339     throw std::system_error(
340         GetLastError(),
341         std::system_category(),
342         "GetFileInformationByHandleEx FileBasicInfo");
343   }
344 
345   FileInformation info(binfo.FileAttributes);
346 
347   FILETIME_LARGE_INTEGER_to_timespec(binfo.CreationTime, &info.ctime);
348   FILETIME_LARGE_INTEGER_to_timespec(binfo.LastAccessTime, &info.atime);
349   FILETIME_LARGE_INTEGER_to_timespec(binfo.LastWriteTime, &info.mtime);
350 
351   if (!GetFileInformationByHandleEx(
352           (HANDLE)handle(), FileStandardInfo, &sinfo, sizeof(sinfo))) {
353     throw std::system_error(
354         GetLastError(),
355         std::system_category(),
356         "GetFileInformationByHandleEx FileStandardInfo");
357   }
358 
359   info.size = sinfo.EndOfFile.QuadPart;
360   info.nlink = sinfo.NumberOfLinks;
361 
362   return info;
363 #endif
364 }
365 
366 
getOpenedPath() const367 w_string FileDescriptor::getOpenedPath() const {
368 #if defined(F_GETPATH)
369   // macOS.  The kernel interface only allows MAXPATHLEN
370   char buf[MAXPATHLEN + 1];
371   if (fcntl(fd_, F_GETPATH, buf) == -1) {
372     throw std::system_error(errno, std::generic_category(),
373                             "fcntl for getOpenedPath");
374   }
375   return w_string(buf);
376 #elif defined(__linux__)
377   char procpath[1024];
378   snprintf(procpath, sizeof(procpath), "/proc/%d/fd/%d", getpid(), fd_);
379 
380   // Avoid an extra stat by speculatively attempting to read into
381   // a reasonably sized buffer.
382   char buf[WATCHMAN_NAME_MAX];
383   auto len = readlink(procpath, buf, sizeof(buf));
384   if (len == sizeof(buf)) {
385     len = -1;
386     // We need to stat it to discover the required length
387     errno = ENAMETOOLONG;
388   }
389 
390   if (len >= 0) {
391     return w_string(buf, len);
392   }
393 
394   if (errno == ENOENT) {
395     // For this path to not exist must mean that /proc is not mounted.
396     // Report this with an actionable message
397     throw std::system_error(ENOSYS, std::generic_category(),
398                             "getOpenedPath: need /proc to be mounted!");
399   }
400 
401   if (errno != ENAMETOOLONG) {
402     throw std::system_error(errno, std::generic_category(),
403                             "readlink for getOpenedPath");
404   }
405 
406   // Figure out how much space we need
407   struct stat st;
408   if (fstat(fd_, &st)) {
409     throw std::system_error(errno, std::generic_category(),
410                             "fstat for getOpenedPath");
411   }
412   std::string result;
413   result.resize(st.st_size + 1, 0);
414 
415   len = readlink(procpath, &result[0], result.size());
416   if (len == int(result.size())) {
417     // It's longer than we expected; TOCTOU detected!
418     throw std::system_error(
419         ENAMETOOLONG, std::generic_category(),
420         "readlinkat: link contents grew while examining file");
421   }
422   if (len >= 0) {
423     return w_string(&result[0], len);
424   }
425 
426   throw std::system_error(errno, std::generic_category(),
427                           "readlink for getOpenedPath");
428 #elif defined(_WIN32)
429   std::wstring wchar;
430   wchar.resize(WATCHMAN_NAME_MAX);
431   auto len = GetFinalPathNameByHandleW(
432       (HANDLE)fd_,
433       &wchar[0],
434       wchar.size(),
435       FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
436   auto err = GetLastError();
437 
438   if (len >= wchar.size()) {
439     // Grow it
440     wchar.resize(len);
441     len = GetFinalPathNameByHandleW(
442         (HANDLE)fd_, &wchar[0], len, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
443     err = GetLastError();
444   }
445 
446   if (len == 0) {
447     throw std::system_error(
448         GetLastError(), std::system_category(), "GetFinalPathNameByHandleW");
449   }
450 
451   return w_string(wchar.data(), len);
452 #else
453   throw std::system_error(ENOSYS, std::generic_category(),
454                           "getOpenedPath not implemented on this platform");
455 #endif
456 }
457 
readSymbolicLink(const char * path)458 w_string readSymbolicLink(const char* path) {
459 #ifndef _WIN32
460   std::string result;
461 
462   // Speculatively assume that this is large enough to read the
463   // symlink text.  This helps to avoid an extra lstat call.
464   result.resize(256);
465 
466   for (int retry = 0; retry < 2; ++retry) {
467     auto len = readlink(path, &result[0], result.size());
468     if (len < 0) {
469       throw std::system_error(
470           errno, std::generic_category(), "readlink for readSymbolicLink");
471     }
472     if (size_t(len) < result.size()) {
473       return w_string(result.data(), len);
474     }
475 
476     // Truncated read; we need to figure out the right size to use
477     struct stat st;
478     if (lstat(path, &st)) {
479       throw std::system_error(
480           errno, std::generic_category(), "lstat for readSymbolicLink");
481     }
482 
483     result.resize(st.st_size + 1, 0);
484   }
485 
486   throw std::system_error(
487       E2BIG,
488       std::generic_category(),
489       "readlink for readSymbolicLink: symlink changed while reading it");
490 #else
491   return openFileHandle(path, OpenFileHandleOptions::queryFileInfo())
492       .readSymbolicLink();
493 #endif
494 }
495 
readSymbolicLink() const496 w_string FileDescriptor::readSymbolicLink() const {
497 #ifndef _WIN32
498   struct stat st;
499   if (fstat(fd_, &st)) {
500     throw std::system_error(
501         errno, std::generic_category(), "fstat for readSymbolicLink");
502   }
503   std::string result;
504   result.resize(st.st_size + 1, 0);
505 
506 #ifdef __linux__
507   // Linux 2.6.39 and later provide this interface
508   auto atlen = readlinkat(fd_, "", &result[0], result.size());
509   if (atlen == int(result.size())) {
510     // It's longer than we expected; TOCTOU detected!
511     throw std::system_error(
512         ENAMETOOLONG, std::generic_category(),
513         "readlinkat: link contents grew while examining file");
514   }
515   if (atlen >= 0) {
516     return w_string(result.data(), atlen);
517   }
518   // if we get ENOTDIR back then we're probably on an older linux and
519   // should fall back to the technique used below.
520   if (errno != ENOTDIR) {
521     throw std::system_error(
522         errno, std::generic_category(), "readlinkat for readSymbolicLink");
523   }
524 #endif
525 
526   auto myName = getOpenedPath();
527   auto len = readlink(myName.c_str(), &result[0], result.size());
528   if (len == int(result.size())) {
529     // It's longer than we expected; TOCTOU detected!
530     throw std::system_error(
531         ENAMETOOLONG, std::generic_category(),
532         "readlink: link contents grew while examining file");
533   }
534   if (len >= 0) {
535     return w_string(result.data(), len);
536   }
537 
538   throw std::system_error(
539       errno, std::generic_category(), "readlink for readSymbolicLink");
540 #else // _WIN32
541   DWORD len = 64 * 1024;
542   auto buf = malloc(len);
543   if (!buf) {
544     throw std::bad_alloc();
545   }
546   SCOPE_EXIT {
547     free(buf);
548   };
549   WCHAR* target;
550   USHORT targetlen;
551 
552   auto result = DeviceIoControl(
553       (HANDLE)fd_,
554       FSCTL_GET_REPARSE_POINT,
555       nullptr,
556       0,
557       buf,
558       len,
559       &len,
560       nullptr);
561 
562   // We only give one retry; if the size changed again already, we'll
563   // have another pending notify from the OS to go look at it again
564   // later, and it's totally fine to give up here for now.
565   if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
566     free(buf);
567     buf = malloc(len);
568     if (!buf) {
569       throw std::bad_alloc();
570     }
571 
572     result = DeviceIoControl(
573         (HANDLE)fd_,
574         FSCTL_GET_REPARSE_POINT,
575         nullptr,
576         0,
577         buf,
578         len,
579         &len,
580         nullptr);
581   }
582 
583   if (!result) {
584     throw std::system_error(
585         GetLastError(), std::system_category(), "FSCTL_GET_REPARSE_POINT");
586   }
587 
588   auto rep = reinterpret_cast<REPARSE_DATA_BUFFER*>(buf);
589 
590   switch (rep->ReparseTag) {
591     case IO_REPARSE_TAG_SYMLINK:
592       target = rep->SymbolicLinkReparseBuffer.PathBuffer +
593           (rep->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
594       targetlen =
595           rep->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
596       break;
597 
598     case IO_REPARSE_TAG_MOUNT_POINT:
599       target = rep->MountPointReparseBuffer.PathBuffer +
600           (rep->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
601       targetlen =
602           rep->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
603       break;
604     default:
605       throw std::system_error(
606           ENOSYS, std::generic_category(), "Unsupported ReparseTag");
607   }
608 
609   return w_string(target, targetlen);
610 #endif
611 }
612 
realPath(const char * path)613 w_string realPath(const char *path) {
614   auto options = OpenFileHandleOptions::queryFileInfo();
615   // Follow symlinks, because that's really the point of this function
616   options.followSymlinks = 1;
617   options.strictNameChecks = 0;
618 
619 #ifdef _WIN32
620   // Special cases for cwd
621   w_string_piece pathPiece(path);
622   // On Windows, "" is used to refer to the CWD.
623   // We also allow using "." for parity with unix, even though that
624   // doesn't generally work for that purpose on windows.
625   // This allows `watchman watch-project .` to succeeed on windows.
626   if (pathPiece.size() == 0 || pathPiece == ".") {
627     std::wstring wchar;
628     wchar.resize(WATCHMAN_NAME_MAX);
629     auto len = GetCurrentDirectoryW(wchar.size(), &wchar[0]);
630     auto err = GetLastError();
631     if (len == 0) {
632       throw std::system_error(err, std::system_category(),
633                               "GetCurrentDirectoryW");
634     }
635     // Assumption: that the OS maintains the CWD in canonical form
636     return w_string(wchar.data(), len);
637   }
638 #endif
639 
640   auto handle = openFileHandle(path, options);
641   return handle.getOpenedPath();
642 }
643 
getFileInformation(const char * path,CaseSensitivity caseSensitive)644 FileInformation getFileInformation(const char *path,
645                                    CaseSensitivity caseSensitive) {
646   auto options = OpenFileHandleOptions::queryFileInfo();
647   options.caseSensitive = caseSensitive;
648 #if defined(_WIN32) || defined(O_PATH)
649   // These operating systems allow opening symlink nodes and querying them
650   // for stat information
651   auto handle = openFileHandle(path, options);
652   auto info = handle.getInfo();
653   return info;
654 #else
655   // Since the leaf of the path may be a symlink, and this system doesn't
656   // allow opening symlinks for stat purposes, we have to resort to performing
657   // a relative fstatat() from the parent dir.
658   w_string_piece pathPiece(path);
659   auto parent = pathPiece.dirName().asWString();
660   auto handle = openFileHandle(parent.c_str(), options);
661   struct stat st;
662   if (fstatat(
663         handle.fd(), pathPiece.baseName().data(), &st, AT_SYMLINK_NOFOLLOW)) {
664     throw std::system_error(errno, std::generic_category(), "fstatat");
665   }
666 
667   if (caseSensitive == CaseSensitivity::Unknown) {
668     caseSensitive = getCaseSensitivityForPath(path);
669   }
670   if (caseSensitive == CaseSensitivity::CaseInSensitive) {
671     // We need to perform one extra check for case-insensitive
672     // paths to make sure that we didn't accidentally open
673     // the wrong case name.
674     checkCanonicalBaseName(path);
675   }
676 
677   return FileInformation(st);
678 #endif
679 }
680 
getCaseSensitivityForPath(const char * path)681 CaseSensitivity getCaseSensitivityForPath(const char *path) {
682 #ifdef __APPLE__
683   return pathconf(path, _PC_CASE_SENSITIVE) ? CaseSensitivity::CaseSensitive
684                                             : CaseSensitivity::CaseInSensitive;
685 #elif defined(_WIN32)
686   unused_parameter(path);
687   return CaseSensitivity::CaseInSensitive;
688 #else
689   unused_parameter(path);
690   return CaseSensitivity::CaseSensitive;
691 #endif
692 
693 }
694 
read(void * buf,int size) const695 Result<int, std::error_code> FileDescriptor::read(void* buf, int size) const {
696 #ifndef _WIN32
697   auto result = ::read(fd_, buf, size);
698   if (result == -1) {
699     int errcode = errno;
700     return Result<int, std::error_code>(
701         std::error_code(errcode, std::generic_category()));
702   }
703   return Result<int, std::error_code>(result);
704 #else
705   DWORD result = 0;
706   if (!ReadFile((HANDLE)fd_, buf, size, &result, nullptr)) {
707     return Result<int, std::error_code>(
708         std::error_code(GetLastError(), std::system_category()));
709   }
710   return Result<int, std::error_code>(result);
711 #endif
712 }
713 
write(const void * buf,int size) const714 Result<int, std::error_code> FileDescriptor::write(const void* buf, int size)
715     const {
716 #ifndef _WIN32
717   auto result = ::write(fd_, buf, size);
718   if (result == -1) {
719     int errcode = errno;
720     return Result<int, std::error_code>(
721         std::error_code(errcode, std::generic_category()));
722   }
723   return Result<int, std::error_code>(result);
724 #else
725   DWORD result = 0;
726   if (!WriteFile((HANDLE)fd_, buf, size, &result, nullptr)) {
727     return Result<int, std::error_code>(
728         std::error_code(GetLastError(), std::system_category()));
729   }
730   return Result<int, std::error_code>(result);
731 #endif
732 }
733 
stdIn()734 const FileDescriptor& FileDescriptor::stdIn() {
735   static FileDescriptor f(
736 #ifdef _WIN32
737       intptr_t(GetStdHandle(STD_INPUT_HANDLE))
738 #else
739       STDIN_FILENO
740 #endif
741           );
742   return f;
743 }
744 
stdOut()745 const FileDescriptor& FileDescriptor::stdOut() {
746   static FileDescriptor f(
747 #ifdef _WIN32
748       intptr_t(GetStdHandle(STD_OUTPUT_HANDLE))
749 #else
750       STDOUT_FILENO
751 #endif
752           );
753   return f;
754 }
755 
stdErr()756 const FileDescriptor& FileDescriptor::stdErr() {
757   static FileDescriptor f(
758 #ifdef _WIN32
759       intptr_t(GetStdHandle(STD_ERROR_HANDLE))
760 #else
761       STDERR_FILENO
762 #endif
763           );
764   return f;
765 }
766 }
767