/src/mysql-server/mysys/my_lib.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2000, 2025, Oracle and/or its affiliates. |
2 | | |
3 | | This program is free software; you can redistribute it and/or modify |
4 | | it under the terms of the GNU General Public License, version 2.0, |
5 | | as published by the Free Software Foundation. |
6 | | |
7 | | This program is designed to work with certain software (including |
8 | | but not limited to OpenSSL) that is licensed under separate terms, |
9 | | as designated in a particular file or component or in included license |
10 | | documentation. The authors of MySQL hereby grant you an additional |
11 | | permission to link the program and your derivative works with the |
12 | | separately licensed software that they have either included with |
13 | | the program or referenced in the documentation. |
14 | | |
15 | | Without limiting anything contained in the foregoing, this file, |
16 | | which is part of C Driver for MySQL (Connector/C), is also subject to the |
17 | | Universal FOSS Exception, version 1.0, a copy of which can be found at |
18 | | http://oss.oracle.com/licenses/universal-foss-exception. |
19 | | |
20 | | This program is distributed in the hope that it will be useful, |
21 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 | | GNU General Public License, version 2.0, for more details. |
24 | | |
25 | | You should have received a copy of the GNU General Public License |
26 | | along with this program; if not, write to the Free Software |
27 | | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
28 | | */ |
29 | | |
30 | | /** |
31 | | @file mysys/my_lib.cc |
32 | | */ |
33 | | |
34 | | /* TODO: check for overrun of memory for names. */ |
35 | | |
36 | | #include <sys/stat.h> |
37 | | #include <algorithm> |
38 | | #include <cerrno> |
39 | | #include <cstring> |
40 | | #include <memory> |
41 | | #include <new> |
42 | | |
43 | | #include "m_string.h" |
44 | | #include "my_alloc.h" |
45 | | #include "my_dbug.h" |
46 | | #include "my_dir.h" /* Structs used by my_dir,includes sys/types */ |
47 | | #include "my_inttypes.h" |
48 | | #include "my_io.h" |
49 | | #include "my_pointer_arithmetic.h" |
50 | | #include "my_sys.h" |
51 | | #include "my_thread_local.h" |
52 | | #include "mysql/service_mysql_alloc.h" |
53 | | #include "mysys/mysys_priv.h" |
54 | | #include "mysys_err.h" |
55 | | #include "prealloced_array.h" |
56 | | #include "template_utils.h" |
57 | | #if !defined(_WIN32) |
58 | | #include <dirent.h> |
59 | | #endif |
60 | | |
61 | | /* |
62 | | Allocate space for 100 FILEINFO structs up-front. |
63 | | */ |
64 | | typedef Prealloced_array<FILEINFO, 100> Entries_array; |
65 | | |
66 | 0 | #define NAMES_START_SIZE 32768 |
67 | | |
68 | | /* We need this because program don't know with malloc we used */ |
69 | | |
70 | 0 | void my_dirend(MY_DIR *buffer) { |
71 | 0 | DBUG_TRACE; |
72 | 0 | if (buffer != nullptr) { |
73 | 0 | auto *array = pointer_cast<Entries_array *>(pointer_cast<char *>(buffer) + |
74 | 0 | ALIGN_SIZE(sizeof(MY_DIR))); |
75 | 0 | array->~Entries_array(); |
76 | 0 | ::destroy_at(pointer_cast<MEM_ROOT *>(pointer_cast<char *>(buffer) + |
77 | 0 | ALIGN_SIZE(sizeof(MY_DIR)) + |
78 | 0 | ALIGN_SIZE(sizeof(Entries_array)))); |
79 | 0 | my_free(buffer); |
80 | 0 | } |
81 | 0 | } /* my_dirend */ |
82 | | |
83 | | #if !defined(_WIN32) |
84 | | |
85 | | static char *directory_file_name(char *dst, const char *src); |
86 | | |
87 | 0 | MY_DIR *my_dir(const char *path, myf MyFlags) { |
88 | 0 | char *buffer; |
89 | 0 | MY_DIR *result = nullptr; |
90 | 0 | FILEINFO finfo; |
91 | 0 | Entries_array *dir_entries_storage; |
92 | 0 | MEM_ROOT *names_storage; |
93 | 0 | DIR *dirp; |
94 | 0 | char tmp_path[FN_REFLEN + 2], *tmp_file; |
95 | 0 | void *rawmem = nullptr; |
96 | |
|
97 | 0 | DBUG_TRACE; |
98 | 0 | DBUG_PRINT("my", ("path: '%s' MyFlags: %d", path, MyFlags)); |
99 | |
|
100 | 0 | dirp = opendir(directory_file_name(tmp_path, path)); |
101 | 0 | if (dirp == nullptr || |
102 | 0 | !(buffer = static_cast<char *>( |
103 | 0 | my_malloc(key_memory_MY_DIR, |
104 | 0 | ALIGN_SIZE(sizeof(MY_DIR)) + |
105 | 0 | ALIGN_SIZE(sizeof(Entries_array)) + sizeof(MEM_ROOT), |
106 | 0 | MyFlags)))) |
107 | 0 | goto error; |
108 | | |
109 | 0 | rawmem = pointer_cast<Entries_array *>(buffer + ALIGN_SIZE(sizeof(MY_DIR))); |
110 | 0 | dir_entries_storage = new (rawmem) Entries_array(key_memory_MY_DIR); |
111 | 0 | names_storage = new (buffer + ALIGN_SIZE(sizeof(MY_DIR)) + |
112 | 0 | ALIGN_SIZE(sizeof(Entries_array))) |
113 | 0 | MEM_ROOT(key_memory_MY_DIR, NAMES_START_SIZE); |
114 | 0 | ; |
115 | | /* MY_DIR structure is allocated and completely initialized at this point */ |
116 | 0 | result = (MY_DIR *)buffer; |
117 | |
|
118 | 0 | tmp_file = strend(tmp_path); |
119 | |
|
120 | 0 | for (const dirent *dp = readdir(dirp); dp; dp = readdir(dirp)) { |
121 | 0 | if (!(finfo.name = strdup_root(names_storage, dp->d_name))) goto error; |
122 | | |
123 | 0 | if (MyFlags & MY_WANT_STAT) { |
124 | 0 | if (!(finfo.mystat = (MY_STAT *)names_storage->Alloc(sizeof(MY_STAT)))) |
125 | 0 | goto error; |
126 | | |
127 | 0 | memset(finfo.mystat, 0, sizeof(MY_STAT)); |
128 | 0 | (void)my_stpcpy(tmp_file, dp->d_name); |
129 | 0 | (void)my_stat(tmp_path, finfo.mystat, MyFlags); |
130 | 0 | if (!(finfo.mystat->st_mode & MY_S_IREAD)) continue; |
131 | 0 | } else |
132 | 0 | finfo.mystat = nullptr; |
133 | | |
134 | 0 | if (dir_entries_storage->push_back(finfo)) goto error; |
135 | 0 | } |
136 | | |
137 | 0 | (void)closedir(dirp); |
138 | |
|
139 | 0 | result->dir_entry = dir_entries_storage->begin(); |
140 | 0 | result->number_off_files = dir_entries_storage->size(); |
141 | |
|
142 | 0 | if (!(MyFlags & MY_DONT_SORT)) |
143 | 0 | std::sort(result->dir_entry, result->dir_entry + result->number_off_files, |
144 | 0 | [](const fileinfo &a, const fileinfo &b) { |
145 | 0 | return strcmp(a.name, b.name) < 0; |
146 | 0 | }); |
147 | 0 | return result; |
148 | | |
149 | 0 | error: |
150 | 0 | set_my_errno(errno); |
151 | 0 | if (dirp) (void)closedir(dirp); |
152 | 0 | my_dirend(result); |
153 | 0 | if (MyFlags & (MY_FAE | MY_WME)) { |
154 | 0 | MyOsError(my_errno(), EE_DIR, MYF(0), path); |
155 | 0 | } |
156 | 0 | return nullptr; |
157 | 0 | } |
158 | | |
159 | | /* |
160 | | * Convert from directory name to filename. |
161 | | * On UNIX, it's simple: just make sure there is a terminating / |
162 | | |
163 | | * Returns pointer to dst; |
164 | | */ |
165 | | |
166 | 0 | static char *directory_file_name(char *dst, const char *src) { |
167 | | /* Process as Unix format: just remove test the final slash. */ |
168 | 0 | char *end; |
169 | 0 | assert(strlen(src) < (FN_REFLEN + 1)); |
170 | |
|
171 | 0 | if (src[0] == 0) src = "."; /* Use empty as current */ |
172 | 0 | end = my_stpnmov(dst, src, FN_REFLEN + 1); |
173 | 0 | if (end[-1] != FN_LIBCHAR) { |
174 | 0 | end[0] = FN_LIBCHAR; /* Add last '/' */ |
175 | 0 | end[1] = '\0'; |
176 | 0 | } |
177 | 0 | return dst; |
178 | 0 | } |
179 | | |
180 | | #else |
181 | | |
182 | | /* |
183 | | ***************************************************************************** |
184 | | ** Read long filename using windows rutines |
185 | | ***************************************************************************** |
186 | | */ |
187 | | |
188 | | MY_DIR *my_dir(const char *path, myf MyFlags) { |
189 | | char *buffer; |
190 | | MY_DIR *result = nullptr; |
191 | | FILEINFO finfo; |
192 | | Entries_array *dir_entries_storage; |
193 | | MEM_ROOT *names_storage; |
194 | | struct _finddata_t find; |
195 | | ushort mode; |
196 | | char tmp_path[FN_REFLEN], *tmp_file, attrib; |
197 | | __int64 handle = -1; |
198 | | void *rawmem = nullptr; |
199 | | |
200 | | DBUG_TRACE; |
201 | | DBUG_PRINT("my", ("path: '%s' MyFlags: %d", path, MyFlags)); |
202 | | |
203 | | /* Put LIB-CHAR as last path-character if not there */ |
204 | | tmp_file = tmp_path; |
205 | | if (!*path) *tmp_file++ = '.'; /* From current dir */ |
206 | | tmp_file = my_stpnmov(tmp_file, path, FN_REFLEN - 5); |
207 | | if (tmp_file[-1] == FN_DEVCHAR) *tmp_file++ = '.'; /* From current dev-dir */ |
208 | | if (tmp_file[-1] != FN_LIBCHAR) *tmp_file++ = FN_LIBCHAR; |
209 | | tmp_file[0] = '*'; /* Windows needs this !??? */ |
210 | | tmp_file[1] = '.'; |
211 | | tmp_file[2] = '*'; |
212 | | tmp_file[3] = '\0'; |
213 | | |
214 | | if (!(buffer = static_cast<char *>( |
215 | | my_malloc(key_memory_MY_DIR, |
216 | | ALIGN_SIZE(sizeof(MY_DIR)) + |
217 | | ALIGN_SIZE(sizeof(Entries_array)) + sizeof(MEM_ROOT), |
218 | | MyFlags)))) |
219 | | goto error; |
220 | | |
221 | | rawmem = buffer + ALIGN_SIZE(sizeof(MY_DIR)); |
222 | | dir_entries_storage = new (rawmem) Entries_array(key_memory_MY_DIR); |
223 | | names_storage = new (buffer + ALIGN_SIZE(sizeof(MY_DIR)) + |
224 | | ALIGN_SIZE(sizeof(Entries_array))) |
225 | | MEM_ROOT(key_memory_MY_DIR, NAMES_START_SIZE); |
226 | | |
227 | | /* MY_DIR structure is allocated and completely initialized at this point */ |
228 | | result = (MY_DIR *)buffer; |
229 | | |
230 | | if ((handle = _findfirst(tmp_path, &find)) == -1L) { |
231 | | DBUG_PRINT("info", ("findfirst returned error, errno: %d", errno)); |
232 | | if (errno != EINVAL) goto error; |
233 | | /* |
234 | | Could not read the directory, no read access. |
235 | | Probably because by "chmod -r". |
236 | | continue and return zero files in dir |
237 | | */ |
238 | | } else { |
239 | | do { |
240 | | attrib = find.attrib; |
241 | | /* |
242 | | Do not show hidden and system files which Windows sometimes create. |
243 | | Note. Because Borland's findfirst() is called with the third |
244 | | argument = 0 hidden/system files are excluded from the search. |
245 | | */ |
246 | | if (attrib & (_A_HIDDEN | _A_SYSTEM)) continue; |
247 | | if (!(finfo.name = strdup_root(names_storage, find.name))) goto error; |
248 | | if (MyFlags & MY_WANT_STAT) { |
249 | | if (!(finfo.mystat = (MY_STAT *)names_storage->Alloc(sizeof(MY_STAT)))) |
250 | | goto error; |
251 | | |
252 | | memset(finfo.mystat, 0, sizeof(MY_STAT)); |
253 | | finfo.mystat->st_size = find.size; |
254 | | mode = MY_S_IREAD; |
255 | | if (!(attrib & _A_RDONLY)) mode |= MY_S_IWRITE; |
256 | | if (attrib & _A_SUBDIR) mode |= MY_S_IFDIR; |
257 | | finfo.mystat->st_mode = mode; |
258 | | finfo.mystat->st_mtime = ((uint32)find.time_write); |
259 | | } else |
260 | | finfo.mystat = nullptr; |
261 | | |
262 | | if (dir_entries_storage->push_back(finfo)) goto error; |
263 | | } while (_findnext(handle, &find) == 0); |
264 | | |
265 | | _findclose(handle); |
266 | | } |
267 | | |
268 | | result->dir_entry = dir_entries_storage->begin(); |
269 | | result->number_off_files = dir_entries_storage->size(); |
270 | | |
271 | | if (!(MyFlags & MY_DONT_SORT)) |
272 | | std::sort(result->dir_entry, result->dir_entry + result->number_off_files, |
273 | | [](const fileinfo &a, const fileinfo &b) { |
274 | | return strcmp(a.name, b.name) < 0; |
275 | | }); |
276 | | DBUG_PRINT("exit", ("found %d files", result->number_off_files)); |
277 | | return result; |
278 | | error: |
279 | | set_my_errno(errno); |
280 | | if (handle != -1) _findclose(handle); |
281 | | my_dirend(result); |
282 | | if (MyFlags & (MY_FAE | MY_WME)) { |
283 | | MyOsError(my_errno(), EE_DIR, MYF(0), path); |
284 | | } |
285 | | return nullptr; |
286 | | } |
287 | | |
288 | | #endif /* _WIN32 */ |
289 | | |
290 | | /**************************************************************************** |
291 | | ** File status |
292 | | ** Note that MY_STAT is assumed to be same as struct stat |
293 | | ****************************************************************************/ |
294 | | |
295 | 0 | int my_fstat(File Filedes, MY_STAT *stat_area) { |
296 | 0 | DBUG_TRACE; |
297 | 0 | DBUG_PRINT("my", ("fd: %d", Filedes)); |
298 | | #ifdef _WIN32 |
299 | | return my_win_fstat(Filedes, stat_area); |
300 | | #else |
301 | 0 | return fstat(Filedes, stat_area); |
302 | 0 | #endif |
303 | 0 | } |
304 | | |
305 | 2 | MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf MyFlags) { |
306 | 2 | DBUG_TRACE; |
307 | 2 | assert(stat_area != nullptr); |
308 | 2 | DBUG_PRINT("my", ("path: '%s' stat_area: %p MyFlags: %d", path, stat_area, |
309 | 2 | MyFlags)); |
310 | | |
311 | 2 | #ifndef _WIN32 |
312 | 2 | if (!stat(path, stat_area)) return stat_area; |
313 | | #else |
314 | | if (!my_win_stat(path, stat_area)) return stat_area; |
315 | | #endif |
316 | | |
317 | 2 | DBUG_PRINT("error", ("Got errno: %d from stat", errno)); |
318 | 2 | set_my_errno(errno); |
319 | | |
320 | 2 | if (MyFlags & (MY_FAE | MY_WME)) { |
321 | 0 | MyOsError(my_errno(), EE_STAT, MYF(0), path); |
322 | 0 | } |
323 | 2 | return nullptr; |
324 | 2 | } |