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
59static int comp_names(struct fileinfo *a,struct fileinfo *b);
60
61typedef 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
69void 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
85static 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
93static 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
110MY_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
201MY_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);
305error:
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
323int 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
336MY_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
358error:
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