1 /* Copyright (c) 2008, 2010, 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, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed 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 included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
22
23 #include <ndb_global.h>
24 #include <NdbDir.hpp>
25
26 #include <util/basestring_vsnprintf.h>
27
28 #ifndef __WIN__
29
30 #include <dirent.h>
31
32 class DirIteratorImpl {
33 DIR* m_dirp;
34 const char *m_path;
35 char* m_buf;
36
is_regular_file(struct dirent * dp) const37 bool is_regular_file(struct dirent* dp) const {
38 #ifdef _DIRENT_HAVE_D_TYPE
39 /*
40 Using dirent's d_type field to determine if
41 it's a regular file
42 */
43 if(dp->d_type != DT_UNKNOWN)
44 return (dp->d_type == DT_REG);
45 #endif
46 /* Using stat to read more info about the file */
47 basestring_snprintf(m_buf, PATH_MAX,
48 "%s/%s", m_path, dp->d_name);
49
50 struct stat buf;
51 if (lstat(m_buf, &buf)) // Use lstat to not follow symlinks
52 return false; // 'stat' failed
53
54 return S_ISREG(buf.st_mode);
55
56 }
57
58 public:
DirIteratorImpl()59 DirIteratorImpl():
60 m_dirp(NULL) {
61 m_buf = new char[PATH_MAX];
62 };
63
~DirIteratorImpl()64 ~DirIteratorImpl() {
65 close();
66 delete [] m_buf;
67 }
68
open(const char * path)69 int open(const char* path){
70 if ((m_dirp = opendir(path)) == NULL){
71 return -1;
72 }
73 m_path= path;
74 return 0;
75 }
76
close(void)77 void close(void)
78 {
79 if (m_dirp)
80 closedir(m_dirp);
81 m_dirp = NULL;
82 }
83
next_entry(bool & is_reg)84 const char* next_entry(bool& is_reg)
85 {
86 struct dirent* dp = readdir(m_dirp);
87
88 if (dp == NULL)
89 return NULL;
90
91 is_reg = is_regular_file(dp);
92 return dp->d_name;
93 }
94 };
95
96 #else
97
98 class DirIteratorImpl {
99 bool m_first;
100 WIN32_FIND_DATA m_find_data;
101 HANDLE m_find_handle;
102
is_dir(const WIN32_FIND_DATA find_data) const103 bool is_dir(const WIN32_FIND_DATA find_data) const {
104 return (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
105 }
is_regular_file(const WIN32_FIND_DATA find_data) const106 bool is_regular_file(const WIN32_FIND_DATA find_data) const {
107 return !is_dir(find_data);
108 }
109
110 public:
DirIteratorImpl()111 DirIteratorImpl():
112 m_first(true),
113 m_find_handle(INVALID_HANDLE_VALUE) {};
114
~DirIteratorImpl()115 ~DirIteratorImpl() {
116 close();
117 }
118
open(const char * path)119 int open(const char* path){
120 char path_buf[PATH_MAX+2];
121 m_first = true;
122 basestring_snprintf(path_buf, sizeof(path_buf), "%s\\*", path);
123 m_find_handle = FindFirstFile(path_buf, &m_find_data);
124 if(m_find_handle == INVALID_HANDLE_VALUE)
125 {
126 if (GetLastError() == ERROR_FILE_NOT_FOUND)
127 m_first= false; // Will do a seek in 'next_file' and return NULL
128 else
129 return -1;
130 }
131 return 0;
132 }
133
close(void)134 void close(void)
135 {
136 if (m_find_handle)
137 FindClose(m_find_handle);
138 m_find_handle = NULL;
139 }
140
next_entry(bool & is_reg)141 const char* next_entry(bool& is_reg)
142 {
143 if (m_first || FindNextFile(m_find_handle, &m_find_data))
144 {
145 m_first = false;
146 is_reg = is_regular_file(m_find_data);
147 return m_find_data.cFileName;
148 }
149 return NULL;
150 }
151 };
152
153 #endif
154
155
Iterator()156 NdbDir::Iterator::Iterator() :
157 m_impl(*new DirIteratorImpl())
158 {
159 }
160
~Iterator()161 NdbDir::Iterator::~Iterator()
162 {
163 delete &m_impl;
164 }
165
166
open(const char * path)167 int NdbDir::Iterator::open(const char* path)
168 {
169 return m_impl.open(path);
170 }
171
close(void)172 void NdbDir::Iterator::close(void)
173 {
174 m_impl.close();
175 }
176
next_file(void)177 const char* NdbDir::Iterator::next_file(void)
178 {
179 bool is_reg;
180 const char* name;
181 while((name = m_impl.next_entry(is_reg)) != NULL){
182 if (is_reg == true)
183 return name; // Found regular file
184 }
185 return NULL;
186 }
187
next_entry(void)188 const char* NdbDir::Iterator::next_entry(void)
189 {
190 bool is_reg;
191 return m_impl.next_entry(is_reg);
192 }
193
u_r(void)194 mode_t NdbDir::u_r(void) { return IF_WIN(0, S_IRUSR); };
u_w(void)195 mode_t NdbDir::u_w(void) { return IF_WIN(0, S_IWUSR); };
u_x(void)196 mode_t NdbDir::u_x(void) { return IF_WIN(0, S_IXUSR); };
197
g_r(void)198 mode_t NdbDir::g_r(void) { return IF_WIN(0, S_IRGRP); };
g_w(void)199 mode_t NdbDir::g_w(void) { return IF_WIN(0, S_IWGRP); };
g_x(void)200 mode_t NdbDir::g_x(void) { return IF_WIN(0, S_IXGRP); };
201
o_r(void)202 mode_t NdbDir::o_r(void) { return IF_WIN(0, S_IROTH); };
o_w(void)203 mode_t NdbDir::o_w(void) { return IF_WIN(0, S_IWOTH); };
o_x(void)204 mode_t NdbDir::o_x(void) { return IF_WIN(0, S_IXOTH); };
205
206
207 bool
create(const char * dir,mode_t mode,bool ignore_existing)208 NdbDir::create(const char *dir, mode_t mode, bool ignore_existing)
209 {
210 #ifdef _WIN32
211 if (CreateDirectory(dir, NULL) == 0)
212 {
213 if (ignore_existing &&
214 GetLastError() == ERROR_ALREADY_EXISTS)
215 return true;
216
217 fprintf(stderr,
218 "Failed to create directory '%s', error: %d\n",
219 dir, GetLastError());
220 return false;
221 }
222 #else
223 if (mkdir(dir, mode) != 0)
224 {
225 if (ignore_existing && errno == EEXIST)
226 return true;
227
228 fprintf(stderr,
229 "Failed to create directory '%s', error: %d\n",
230 dir, errno);
231 return false;
232 }
233 #endif
234 return true;
235 }
236
237
Temp()238 NdbDir::Temp::Temp()
239 {
240 #ifdef _WIN32
241 DWORD len = GetTempPath(0, NULL);
242 m_path = new char[len];
243 if (GetTempPath(len, (char*)m_path) == 0)
244 abort();
245 #else
246 char* tmp = getenv("TMPDIR");
247 if (tmp)
248 m_path = tmp;
249 else
250 m_path = "/tmp/";
251 #endif
252 }
253
~Temp()254 NdbDir::Temp::~Temp()
255 {
256 #ifdef _WIN32
257 delete [] m_path;
258 #endif
259 }
260
261
262 const char*
path(void) const263 NdbDir::Temp::path(void) const {
264 return m_path;
265 }
266
267
268 bool
remove(const char * path)269 NdbDir::remove(const char* path)
270 {
271 #ifdef _WIN32
272 if (RemoveDirectory(path) != 0)
273 return true; // Gone
274 #else
275 if (rmdir(path) == 0)
276 return true; // Gone
277 #endif
278 return false;
279 }
280
281 bool
remove_recursive(const char * dir,bool only_contents)282 NdbDir::remove_recursive(const char* dir, bool only_contents)
283 {
284 char path[PATH_MAX];
285 if (basestring_snprintf(path, sizeof(path),
286 "%s%s", dir, DIR_SEPARATOR) < 0) {
287 fprintf(stderr, "Too long path to remove: '%s'\n", dir);
288 return false;
289 }
290 int start_len = strlen(path);
291
292 const char* name;
293 NdbDir::Iterator iter;
294 loop:
295 {
296 if (iter.open(path) != 0)
297 {
298 fprintf(stderr, "Failed to open iterator for '%s'\n",
299 path);
300 return false;
301 }
302
303 while ((name = iter.next_entry()) != NULL)
304 {
305 if ((strcmp(".", name) == 0) || (strcmp("..", name) == 0))
306 continue;
307
308 int end_len, len = strlen(path);
309 if ((end_len = basestring_snprintf(path + len, sizeof(path) - len,
310 "%s", name)) < 0)
311 {
312 fprintf(stderr, "Too long path detected: '%s'+'%s'\n",
313 path, name);
314 return false;
315 }
316
317 if (unlink(path) == 0 || NdbDir::remove(path) == true)
318 {
319 path[len] = 0;
320 continue;
321 }
322
323 iter.close();
324
325 // Append ending slash to the string
326 int pos = len + end_len;
327 if (basestring_snprintf(path + pos, sizeof(path) - pos,
328 "%s", DIR_SEPARATOR) < 0)
329 {
330 fprintf(stderr, "Too long path detected: '%s'+'%s'\n",
331 path, DIR_SEPARATOR);
332 return false;
333 }
334
335 goto loop;
336 }
337 iter.close();
338
339 int len = strlen(path);
340 path[len - 1] = 0; // remove ending slash
341
342 char * prev_slash = strrchr(path, IF_WIN('\\', '/'));
343 if (len > start_len && prev_slash)
344 {
345 // Not done yet, step up one dir level
346 assert(prev_slash > path && prev_slash < path + sizeof(path));
347 prev_slash[1] = 0;
348 goto loop;
349 }
350 }
351
352 if (only_contents == false && NdbDir::remove(dir) == false)
353 {
354 fprintf(stderr,
355 "Failed to remove directory '%s', error: %d\n",
356 dir, errno);
357 return false;
358 }
359
360 return true;
361 }
362
363 #ifdef _WIN32
364 #include <direct.h> // chdir
365 #endif
366
367 int
chdir(const char * path)368 NdbDir::chdir(const char* path)
369 {
370 return ::chdir(path);
371 }
372
373
374 #ifdef TEST_NDBDIR
375 #include <NdbTap.hpp>
376
377 #define CHECK(x) \
378 if (!(x)) { \
379 fprintf(stderr, "failed at line %d\n", __LINE__ ); \
380 abort(); }
381
382 static void
build_tree(const char * path)383 build_tree(const char* path)
384 {
385 char tmp[PATH_MAX];
386 CHECK(NdbDir::create(path));
387
388 // Create files in path/
389 for (int i = 8; i < 14; i++){
390 basestring_snprintf(tmp, sizeof(tmp), "%s%sfile%d", path, DIR_SEPARATOR, i);
391 fclose(fopen(tmp, "w"));
392 }
393
394 // Create directories
395 for (int i = 8; i < 14; i++){
396 basestring_snprintf(tmp, sizeof(tmp), "%s%sdir%d", path, DIR_SEPARATOR, i);
397 CHECK(NdbDir::create(tmp));
398
399 // Create files in dir
400 for (int j = 0; j < 6; j++){
401 basestring_snprintf(tmp, sizeof(tmp), "%s%sdir%d%sfile%d",
402 path, DIR_SEPARATOR, i, DIR_SEPARATOR, j);
403 fclose(fopen(tmp, "w"));
404 }
405 }
406
407 #ifndef _WIN32
408 // Symlink the last file created to path/symlink
409 char tmp2[PATH_MAX];
410 basestring_snprintf(tmp2, sizeof(tmp2), "%s%ssymlink", path, DIR_SEPARATOR);
411 CHECK(symlink(tmp, tmp2) == 0);
412 #endif
413 }
414
415 static bool
gone(const char * dir)416 gone(const char *dir) {
417 return (access(dir, F_OK) == -1 && errno == ENOENT);
418 }
419
TAPTEST(DirIterator)420 TAPTEST(DirIterator)
421 {
422 NdbDir::Temp tempdir;
423 char path[PATH_MAX];
424 basestring_snprintf(path, sizeof(path),"%s%s%s",
425 tempdir.path(), DIR_SEPARATOR, "ndbdir_test");
426
427 printf("Using directory '%s'\n", path);
428
429 // Remove dir if it exists
430 if (access(path, F_OK) == 0)
431 CHECK(NdbDir::remove_recursive(path));
432
433 // Build dir tree
434 build_tree(path);
435 // Test to iterate over files
436 {
437 NdbDir::Iterator iter;
438 CHECK(iter.open(path) == 0);
439 const char* name;
440 int num_files = 0;
441 while((name = iter.next_file()) != NULL)
442 {
443 //printf("%s\n", name);
444 num_files++;
445 }
446 printf("Found %d files\n", num_files);
447 CHECK(num_files == 6);
448 }
449
450 // Remove all of tree
451 CHECK(NdbDir::remove_recursive(path));
452 CHECK(gone(path));
453
454 // Remove non existing directory
455 fprintf(stderr, "Checking that proper error is returned when "
456 "opening non existing directory\n");
457 CHECK(!NdbDir::remove_recursive(path));
458 CHECK(gone(path));
459
460 // Build dir tree and remove everything inside it
461 build_tree(path);
462 CHECK(NdbDir::remove_recursive(path, true));
463 CHECK(!gone(path));
464
465 // Remove also the empty dir
466 CHECK(NdbDir::remove_recursive(path));
467 CHECK(gone(path));
468
469 // Remove non exisiting directory(again)
470 CHECK(!NdbDir::remove_recursive(path));
471 CHECK(gone(path));
472
473 // Create directory with non default mode
474 CHECK(NdbDir::create(path,
475 NdbDir::u_rwx() | NdbDir::g_r() | NdbDir::o_r()));
476 CHECK(!gone(path));
477 CHECK(NdbDir::remove_recursive(path));
478 CHECK(gone(path));
479
480 // Create already existing directory
481 CHECK(NdbDir::create(path, NdbDir::u_rwx()));
482 CHECK(!gone(path));
483 CHECK(NdbDir::create(path, NdbDir::u_rwx(), true /* ignore existing!! */));
484 CHECK(!gone(path));
485 CHECK(NdbDir::remove_recursive(path));
486 CHECK(gone(path));
487
488 printf("Testing NdbDir::chdir...\n");
489 // Try chdir to the non existing dir, should fail
490 CHECK(NdbDir::chdir(path) != 0);
491
492 // Build dir tree
493 build_tree(path);
494
495 // Try chdir to the now existing dir, should work
496 CHECK(NdbDir::chdir(path) == 0);
497
498 // Try chdir to the root of tmpdir, should work
499 CHECK(NdbDir::chdir(tempdir.path()) == 0);
500
501 // Remove the dir tree again to leave clean
502 CHECK(NdbDir::remove_recursive(path));
503 CHECK(gone(path));
504
505 return 1; // OK
506 }
507 #endif
508