allow AsyncSignalHandler to attach and detach from an EventBase
[folly.git] / folly / portability / Dirent.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/portability/Dirent.h>
18
19 #ifdef _WIN32
20 #include <stdlib.h>
21 #include <string>
22
23 #include <folly/portability/Windows.h>
24
25 struct DIR {
26   dirent dir{};
27   HANDLE searchHandle{INVALID_HANDLE_VALUE};
28   int entriesRead{0};
29   char currentName[MAX_PATH * 3];
30   std::string pattern;
31
32   int close() {
33     return FindClose(searchHandle) ? 0 : -1;
34   }
35
36   DIR* open() {
37     wchar_t patternBuf[MAX_PATH + 3];
38     size_t len;
39
40     if (mbstowcs_s(&len, patternBuf, MAX_PATH, pattern.c_str(), MAX_PATH - 2)) {
41       return nullptr;
42     }
43
44     if (len && patternBuf[len - 1] != '/' && patternBuf[len - 1] != '\\') {
45       patternBuf[len++] = '\\';
46     }
47     patternBuf[len++] = '*';
48     patternBuf[len] = 0;
49
50     WIN32_FIND_DATAW fdata;
51     HANDLE h = FindFirstFileW(patternBuf, &fdata);
52     if (h == INVALID_HANDLE_VALUE) {
53       return nullptr;
54     }
55
56     searchHandle = h;
57     dir.d_name = currentName;
58     if (wcstombs(currentName, fdata.cFileName, MAX_PATH * 3) == (size_t)-1) {
59       return nullptr;
60     }
61
62     setEntryType(fdata.dwFileAttributes);
63     return this;
64   }
65
66   dirent* nextDir() {
67     if (entriesRead) {
68       WIN32_FIND_DATAW fdata;
69       if (!FindNextFileW(searchHandle, &fdata)) {
70         return nullptr;
71       }
72
73       if (wcstombs(currentName, fdata.cFileName, MAX_PATH * 3) == (size_t)-1) {
74         errno = EBADF;
75         return nullptr;
76       }
77       setEntryType(fdata.dwFileAttributes);
78     }
79
80     entriesRead++;
81     return &dir;
82   }
83
84  private:
85   void setEntryType(DWORD attr) {
86     if (attr & FILE_ATTRIBUTE_DIRECTORY) {
87       dir.d_type = DT_DIR;
88     } else {
89       dir.d_type = DT_REG;
90     }
91   }
92 };
93
94 extern "C" {
95 int closedir(DIR* dir) {
96   auto ret = dir->close();
97   delete dir;
98   return ret;
99 }
100
101 DIR* opendir(const char* name) {
102   auto dir = new DIR();
103   dir->pattern = name;
104   if (!dir->open()) {
105     delete dir;
106     return nullptr;
107   }
108   return dir;
109 }
110
111 dirent* readdir(DIR* dir) {
112   return dir->nextDir();
113 }
114
115 int readdir_r(DIR* dir, dirent* buf, dirent** ent) {
116   if (!dir || !buf || !ent) {
117     return EBADF;
118   }
119   *ent = dir->nextDir();
120   // Our normal readdir implementation is actually
121   // already reentrant, but we need to do this copy
122   // in case the caller expects buf to have the value.
123   if (*ent) {
124     *buf = dir->dir;
125   }
126   return 0;
127 }
128
129 void rewinddir(DIR* dir) {
130   dir->close();
131   dir->open();
132 }
133 }
134 #endif