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