1 | /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. |
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 as published by |
5 | the Free Software Foundation; version 2 of the License. |
6 | |
7 | This program is distributed in the hope that it will be useful, |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | GNU General Public License for more details. |
11 | |
12 | You should have received a copy of the GNU General Public License |
13 | along with this program; if not, write to the Free Software |
14 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
15 | */ |
16 | |
17 | /* TODO: check for overrun of memory for names. */ |
18 | |
19 | #include "mysys_priv.h" |
20 | #include <m_string.h> |
21 | #include <my_dir.h> /* Structs used by my_dir,includes sys/types */ |
22 | #include "mysys_err.h" |
23 | #if defined(HAVE_DIRENT_H) |
24 | # include <dirent.h> |
25 | #else |
26 | # define dirent direct |
27 | # if defined(HAVE_SYS_NDIR_H) |
28 | # include <sys/ndir.h> |
29 | # endif |
30 | # if defined(HAVE_SYS_DIR_H) |
31 | # include <sys/dir.h> |
32 | # endif |
33 | # if defined(HAVE_NDIR_H) |
34 | # include <ndir.h> |
35 | # endif |
36 | # if defined(_WIN32) |
37 | # ifdef __BORLANDC__ |
38 | # include <dir.h> |
39 | # endif |
40 | # endif |
41 | #endif |
42 | |
43 | #if defined(HAVE_READDIR_R) |
44 | #define READDIR(A,B,C) ((errno=readdir_r(A,B,&C)) != 0 || !C) |
45 | #else |
46 | #define READDIR(A,B,C) (!(C=readdir(A))) |
47 | #endif |
48 | |
49 | /* |
50 | We are assuming that directory we are reading is either has less than |
51 | 100 files and so can be read in one initial chunk or has more than 1000 |
52 | files and so big increment are suitable. |
53 | */ |
54 | #define ENTRIES_START_SIZE (8192/sizeof(FILEINFO)) |
55 | #define ENTRIES_INCREMENT (65536/sizeof(FILEINFO)) |
56 | #define NAMES_START_SIZE 32768 |
57 | |
58 | |
59 | static int comp_names(struct fileinfo *a,struct fileinfo *b); |
60 | |
61 | typedef struct { |
62 | MY_DIR dir; |
63 | DYNAMIC_ARRAY array; |
64 | MEM_ROOT root; |
65 | } MY_DIR_HANDLE; |
66 | |
67 | /* We need this because the caller doesn't know which malloc we've used */ |
68 | |
69 | void my_dirend(MY_DIR *dir) |
70 | { |
71 | MY_DIR_HANDLE *dirh= (MY_DIR_HANDLE*) dir; |
72 | DBUG_ENTER("my_dirend" ); |
73 | if (dirh) |
74 | { |
75 | delete_dynamic(&dirh->array); |
76 | free_root(&dirh->root, MYF(0)); |
77 | my_free(dirh); |
78 | } |
79 | DBUG_VOID_RETURN; |
80 | } /* my_dirend */ |
81 | |
82 | |
83 | /* Compare in sort of filenames */ |
84 | |
85 | static int comp_names(struct fileinfo *a, struct fileinfo *b) |
86 | { |
87 | return (strcmp(a->name,b->name)); |
88 | } /* comp_names */ |
89 | |
90 | |
91 | #if !defined(_WIN32) |
92 | |
93 | static char *directory_file_name (char * dst, const char *src) |
94 | { |
95 | /* Process as Unix format: just remove test the final slash. */ |
96 | char *end; |
97 | DBUG_ASSERT(strlen(src) < (FN_REFLEN + 1)); |
98 | |
99 | if (src[0] == 0) |
100 | src= (char*) "." ; /* Use empty as current */ |
101 | end= strnmov(dst, src, FN_REFLEN + 1); |
102 | if (end[-1] != FN_LIBCHAR) |
103 | { |
104 | *end++= FN_LIBCHAR; /* Add last '/' */ |
105 | *end='\0'; |
106 | } |
107 | return end; |
108 | } |
109 | |
110 | MY_DIR *my_dir(const char *path, myf MyFlags) |
111 | { |
112 | MY_DIR_HANDLE *dirh= 0; |
113 | FILEINFO finfo; |
114 | DIR *dirp; |
115 | struct dirent *dp; |
116 | char tmp_path[FN_REFLEN + 2], *tmp_file; |
117 | char dirent_tmp[sizeof(struct dirent)+_POSIX_PATH_MAX+1]; |
118 | |
119 | DBUG_ENTER("my_dir" ); |
120 | DBUG_PRINT("my" ,("path: '%s' MyFlags: %lu" ,path,MyFlags)); |
121 | |
122 | tmp_file= directory_file_name(tmp_path, path); |
123 | |
124 | if (!(dirp= opendir(tmp_path))) |
125 | goto error; |
126 | |
127 | if (!(dirh= my_malloc(sizeof(*dirh), MyFlags | MY_ZEROFILL))) |
128 | goto error; |
129 | |
130 | if (my_init_dynamic_array(&dirh->array, sizeof(FILEINFO), |
131 | ENTRIES_START_SIZE, ENTRIES_INCREMENT, |
132 | MYF(MyFlags))) |
133 | goto error; |
134 | |
135 | init_alloc_root(&dirh->root, "dir" , NAMES_START_SIZE, NAMES_START_SIZE, |
136 | MYF(MyFlags)); |
137 | |
138 | dp= (struct dirent*) dirent_tmp; |
139 | |
140 | while (!(READDIR(dirp,(struct dirent*) dirent_tmp,dp))) |
141 | { |
142 | MY_STAT statbuf, *mystat= 0; |
143 | |
144 | if (dp->d_name[0] == '.' && |
145 | (dp->d_name[1] == '\0' || |
146 | (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) |
147 | continue; /* . or .. */ |
148 | |
149 | if (MyFlags & MY_WANT_STAT) |
150 | { |
151 | mystat= &statbuf; |
152 | bzero(mystat, sizeof(*mystat)); |
153 | (void) strmov(tmp_file, dp->d_name); |
154 | (void) my_stat(tmp_path, mystat, MyFlags); |
155 | if (!(mystat->st_mode & MY_S_IREAD)) |
156 | continue; |
157 | } |
158 | |
159 | if (!(finfo.name= strdup_root(&dirh->root, dp->d_name))) |
160 | goto error; |
161 | |
162 | if (mystat && |
163 | !((mystat= memdup_root(&dirh->root, mystat, sizeof(*mystat))))) |
164 | goto error; |
165 | |
166 | finfo.mystat= mystat; |
167 | |
168 | if (push_dynamic(&dirh->array, (uchar*)&finfo)) |
169 | goto error; |
170 | } |
171 | |
172 | (void) closedir(dirp); |
173 | |
174 | if (MyFlags & MY_WANT_SORT) |
175 | sort_dynamic(&dirh->array, (qsort_cmp) comp_names); |
176 | |
177 | dirh->dir.dir_entry= dynamic_element(&dirh->array, 0, FILEINFO *); |
178 | dirh->dir.number_of_files= dirh->array.elements; |
179 | |
180 | DBUG_RETURN(&dirh->dir); |
181 | |
182 | error: |
183 | my_errno=errno; |
184 | if (dirp) |
185 | (void) closedir(dirp); |
186 | my_dirend(&dirh->dir); |
187 | if (MyFlags & (MY_FAE | MY_WME)) |
188 | my_error(EE_DIR, MYF(ME_BELL | ME_WAITTANG), path, my_errno); |
189 | DBUG_RETURN(NULL); |
190 | } /* my_dir */ |
191 | |
192 | |
193 | #else |
194 | |
195 | /* |
196 | ***************************************************************************** |
197 | ** Read long filename using windows rutines |
198 | ***************************************************************************** |
199 | */ |
200 | |
201 | MY_DIR *my_dir(const char *path, myf MyFlags) |
202 | { |
203 | MY_DIR_HANDLE *dirh= 0; |
204 | FILEINFO finfo; |
205 | struct _finddata_t find; |
206 | ushort mode; |
207 | char tmp_path[FN_REFLEN], *tmp_file,attrib; |
208 | #ifdef _WIN64 |
209 | __int64 handle= -1; |
210 | #else |
211 | long handle= -1; |
212 | #endif |
213 | DBUG_ENTER("my_dir" ); |
214 | DBUG_PRINT("my" ,("path: '%s' MyFlags: %d" ,path,(int)MyFlags)); |
215 | |
216 | /* Put LIB-CHAR as last path-character if not there */ |
217 | tmp_file=tmp_path; |
218 | if (!*path) |
219 | *tmp_file++ ='.'; /* From current dir */ |
220 | tmp_file= strnmov(tmp_file, path, FN_REFLEN-5); |
221 | if (tmp_file[-1] == FN_DEVCHAR) |
222 | *tmp_file++= '.'; /* From current dev-dir */ |
223 | if (tmp_file[-1] != FN_LIBCHAR) |
224 | *tmp_file++ =FN_LIBCHAR; |
225 | tmp_file[0]='*'; /* Windows needs this !??? */ |
226 | tmp_file[1]='.'; |
227 | tmp_file[2]='*'; |
228 | tmp_file[3]='\0'; |
229 | |
230 | if (!(dirh= my_malloc(sizeof(*dirh), MyFlags | MY_ZEROFILL))) |
231 | goto error; |
232 | |
233 | if (my_init_dynamic_array(&dirh->array, sizeof(FILEINFO), |
234 | ENTRIES_START_SIZE, ENTRIES_INCREMENT, |
235 | MYF(MyFlags))) |
236 | goto error; |
237 | |
238 | init_alloc_root(&dirh->root, "dir" , NAMES_START_SIZE, NAMES_START_SIZE, |
239 | MYF(MyFlags)); |
240 | |
241 | if ((handle=_findfirst(tmp_path,&find)) == -1L) |
242 | { |
243 | DBUG_PRINT("info" , ("findfirst returned error, errno: %d" , errno)); |
244 | if (errno != EINVAL) |
245 | goto error; |
246 | /* |
247 | Could not read the directory, no read access. |
248 | Probably because by "chmod -r". |
249 | continue and return zero files in dir |
250 | */ |
251 | } |
252 | else |
253 | { |
254 | do |
255 | { |
256 | attrib= find.attrib; |
257 | /* |
258 | Do not show hidden and system files which Windows sometimes create. |
259 | Note. Because Borland's findfirst() is called with the third |
260 | argument = 0 hidden/system files are excluded from the search. |
261 | */ |
262 | if (attrib & (_A_HIDDEN | _A_SYSTEM)) |
263 | continue; |
264 | |
265 | if (find.name[0] == '.' && |
266 | (find.name[1] == '\0' || |
267 | (find.name[1] == '.' && find.name[2] == '\0'))) |
268 | continue; /* . or .. */ |
269 | |
270 | if (!(finfo.name= strdup_root(&dirh->root, find.name))) |
271 | goto error; |
272 | if (MyFlags & MY_WANT_STAT) |
273 | { |
274 | if (!(finfo.mystat= (MY_STAT*)alloc_root(&dirh->root, sizeof(MY_STAT)))) |
275 | goto error; |
276 | |
277 | bzero(finfo.mystat, sizeof(MY_STAT)); |
278 | finfo.mystat->st_size=find.size; |
279 | mode= MY_S_IREAD; |
280 | if (!(attrib & _A_RDONLY)) |
281 | mode|= MY_S_IWRITE; |
282 | if (attrib & _A_SUBDIR) |
283 | mode|= MY_S_IFDIR; |
284 | finfo.mystat->st_mode= mode; |
285 | finfo.mystat->st_mtime= ((uint32) find.time_write); |
286 | } |
287 | else |
288 | finfo.mystat= NULL; |
289 | |
290 | if (push_dynamic(&dirh->array, (uchar*)&finfo)) |
291 | goto error; |
292 | } |
293 | while (_findnext(handle,&find) == 0); |
294 | _findclose(handle); |
295 | } |
296 | |
297 | if (MyFlags & MY_WANT_SORT) |
298 | sort_dynamic(&dirh->array, (qsort_cmp) comp_names); |
299 | |
300 | dirh->dir.dir_entry= dynamic_element(&dirh->array, 0, FILEINFO *); |
301 | dirh->dir.number_of_files= dirh->array.elements; |
302 | |
303 | DBUG_PRINT("exit" , ("found %d files" , dirh->dir.number_of_files)); |
304 | DBUG_RETURN(&dirh->dir); |
305 | error: |
306 | my_errno=errno; |
307 | if (handle != -1) |
308 | _findclose(handle); |
309 | my_dirend(&dirh->dir); |
310 | if (MyFlags & (MY_FAE | MY_WME)) |
311 | my_error(EE_DIR,MYF(ME_BELL | ME_WAITTANG), path, errno); |
312 | DBUG_RETURN(NULL); |
313 | } /* my_dir */ |
314 | |
315 | #endif /* _WIN32 */ |
316 | |
317 | /**************************************************************************** |
318 | ** File status |
319 | ** Note that MY_STAT is assumed to be same as struct stat |
320 | ****************************************************************************/ |
321 | |
322 | |
323 | int my_fstat(File Filedes, MY_STAT *stat_area, |
324 | myf MyFlags __attribute__((unused))) |
325 | { |
326 | DBUG_ENTER("my_fstat" ); |
327 | DBUG_PRINT("my" ,("fd: %d MyFlags: %lu" , Filedes, MyFlags)); |
328 | #ifdef _WIN32 |
329 | DBUG_RETURN(my_win_fstat(Filedes, stat_area)); |
330 | #else |
331 | DBUG_RETURN(fstat(Filedes, (struct stat *) stat_area)); |
332 | #endif |
333 | } |
334 | |
335 | |
336 | MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf my_flags) |
337 | { |
338 | int m_used; |
339 | DBUG_ENTER("my_stat" ); |
340 | DBUG_PRINT("my" , ("path: '%s' stat_area: %p MyFlags: %lu" , path, |
341 | stat_area, my_flags)); |
342 | |
343 | if ((m_used= (stat_area == NULL))) |
344 | if (!(stat_area= (MY_STAT *) my_malloc(sizeof(MY_STAT), my_flags))) |
345 | goto error; |
346 | #ifndef _WIN32 |
347 | if (! stat((char *) path, (struct stat *) stat_area) ) |
348 | DBUG_RETURN(stat_area); |
349 | #else |
350 | if (! my_win_stat(path, stat_area) ) |
351 | DBUG_RETURN(stat_area); |
352 | #endif |
353 | DBUG_PRINT("error" ,("Got errno: %d from stat" , errno)); |
354 | my_errno= errno; |
355 | if (m_used) /* Free if new area */ |
356 | my_free(stat_area); |
357 | |
358 | error: |
359 | if (my_flags & (MY_FAE+MY_WME)) |
360 | { |
361 | my_error(EE_STAT, MYF(ME_BELL+ME_WAITTANG),path,my_errno); |
362 | DBUG_RETURN((MY_STAT *) NULL); |
363 | } |
364 | DBUG_RETURN((MY_STAT *) NULL); |
365 | } /* my_stat */ |
366 | |