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