OpenDDS  Snapshot(2023/04/28-20:55)
FileSystemStorage.cpp
Go to the documentation of this file.
1 /*
2  *
3  *
4  * Distributed under the OpenDDS License.
5  * See: http://www.opendds.org/license.html
6  */
7 
8 #include "DCPS/DdsDcps_pch.h" //Only the _pch include should start with DCPS/
9 
10 #ifndef OPENDDS_SAFETY_PROFILE
11 
12 #include "FileSystemStorage.h"
13 
14 #include "DirentWrapper.h"
15 
16 #include <ace/Vector_T.h>
17 #include <ace/OS_NS_sys_stat.h>
18 #include <ace/OS_NS_macros.h>
19 #include <ace/OS_NS_unistd.h>
20 #include <ace/OS_NS_stdio.h>
21 
22 #include <cstdio>
23 #include <cstring>
24 #include <stdexcept>
25 #include <fstream>
26 
27 typedef size_t String_Index_t;
28 
29 namespace {
30 
31 const size_t FSS_MAX_FILE_NAME = 150, FSS_MAX_FILE_NAME_ENCODED = 240,
32  FSS_MAX_OVERFLOW_DIR = 9999;
33 const ACE_TCHAR FSS_DEFAULT_FILE_NAME[] = ACE_TEXT("F00000000000000");
34 const ACE_TCHAR FSS_DEFAULT_DIR_NAME[] = ACE_TEXT("D00000000000000");
35 
36 void add_slash(ACE_TString& str)
37 {
38  if (str.length() == 0) return;
39 
40  if (str[str.length() - 1] == ACE_TEXT('\\')) {
41  str[str.length() - 1] = ACE_TEXT('/');
42 
43  } else if (str[str.length() - 1] != ACE_TEXT('/')) {
44  str += ACE_TEXT('/');
45  }
46 }
47 
48 // increment a filename in a lexicographical ordering
49 bool increment(ACE_TString& logical)
50 {
51  for (size_t i = logical.length() - 1; i >= 1; --i) {
52  //we could use the entire range of 255 values but we'll keep
53  //these names to 7-bit ASCII
54  if (logical[i] < 0x7F) {
55  ++logical[i];
56  return true;
57  }
58  }
59 
60  return false;
61 }
62 
63 // support long paths (> 260 chars in absolute path) on Win32
64 /* Windows Mobile / Windows CE (WinCE) porting notes:
65  * These platforms have no concept of "current directory"
66  * and the Win32-specific \\?\ syntax may or may not work.
67  * This file has been set up to compile under WinCE but we
68  * don't expect that persistent storage to work well at runtime
69  * so PERSISTENT_DURABILITY_QOS may not actually work.
70  */
71 #if defined ACE_WIN32 && !defined ACE_HAS_WINCE
72 
73 void fwd_slash_to_back_slash(ACE_WString& str)
74 {
75  for (String_Index_t idx = str.find(L'/'); idx != ACE_WString::npos;
76  idx = str.find(L'/', idx + 1)) {
77  str[idx] = L'\\';
78  }
79 }
80 
81 bool is_relative(const ACE_TCHAR* path)
82 {
83  return !path[0] ||
84  (path[1] != ACE_TEXT(':') &&
85  (ACE_OS::strncmp(ACE_TEXT("\\\\?\\"), path, 4) != 0));
86 }
87 
88 /// @returns \\?<absolute_path>
89 ACE_WString to_win32_long_path(const ACE_TCHAR* path)
90 {
91  ACE_WString wpath = ACE_TEXT_ALWAYS_WCHAR(path);
92  fwd_slash_to_back_slash(wpath);
93  ACE_WString dir;
94 
95  if (is_relative(path)) {
96  DWORD sz = ::GetCurrentDirectoryW(0, 0);
97  ACE_Vector<wchar_t> cur(sz);
98  cur.resize(sz, L'\0');
99  ::GetCurrentDirectoryW(sz, &cur[0]);
100  dir = &cur[0];
101 
102  if (dir[dir.length() - 1] != L'\\') dir += L'\\';
103  }
104 
105  if (dir.substr(0, 4) != L"\\\\?\\" && wpath.substr(0, 4) != L"\\\\?\\")
106  dir = L"\\\\?\\" + dir;
107 
108  dir += wpath;
109  return dir;
110 }
111 
112 int dds_mkdir(const ACE_TCHAR* path)
113 {
114  ACE_WString wpath = to_win32_long_path(path);
115  ACE_WIN32CALL_RETURN(ACE_ADAPT_RETVAL(::CreateDirectoryW(wpath.c_str(), 0),
116  ace_result_), int, -1);
117 }
118 
119 int dds_chdir(const ACE_TCHAR* path)
120 {
121  // In all the other cases we are going to \\?<absolute> long paths, but
122  // SetCurrentDirectory() doesn't allow this workaround.
123  ACE_WString wpath = to_win32_long_path(path);
124  wchar_t spath[MAX_PATH];
125 
126  if (0 == ::GetShortPathNameW(wpath.c_str(), spath, MAX_PATH)) {
127  throw std::runtime_error("GetShortPathNameW failed.");
128  }
129 
130  return ::_wchdir(spath);
131 }
132 
133 bool is_dir(const ACE_TCHAR* path)
134 {
135  ACE_WString wpath = to_win32_long_path(path);
136  DWORD attrib = ::GetFileAttributesW(wpath.c_str());
137 
138  if (attrib != INVALID_FILE_ATTRIBUTES) {
139  return attrib & FILE_ATTRIBUTE_DIRECTORY;
140  }
141 
142  return false;
143 }
144 
145 int dds_rmdir(const ACE_TCHAR* path)
146 {
147  ACE_WString wpath = to_win32_long_path(path);
148  ACE_WIN32CALL_RETURN(ACE_ADAPT_RETVAL(::RemoveDirectoryW(wpath.c_str()),
149  ace_result_), int, -1);
150 }
151 
152 #else // !ACE_WIN32
153 
154 inline int dds_mkdir(const ACE_TCHAR* path)
155 {
156  return ACE_OS::mkdir(path);
157 }
158 
159 inline int dds_chdir(const ACE_TCHAR* path)
160 {
161  return ACE_OS::chdir(path);
162 }
163 
164 inline bool is_dir(const ACE_TCHAR* path)
165 {
166  ACE_stat st;
167  return ACE_OS::stat(path, &st) != -1 && (st.st_mode & S_IFDIR);
168 }
169 
170 inline int dds_rmdir(const ACE_TCHAR* path)
171 {
172  return ACE_OS::rmdir(path);
173 }
174 
175 #endif // ACE_WIN32
176 
177 ACE_TString overflow_dir_name(unsigned int n)
178 {
179  const size_t buf_size=32;
180  ACE_TCHAR of_name[buf_size];
181  ACE_OS::snprintf(of_name, buf_size, ACE_TEXT("_overflow.%04u/"), n);
182  return of_name;
183 }
184 
185 struct CwdGuard {
186  ACE_TString cwd_;
187 
188  explicit CwdGuard(const ACE_TString& dir) {
189  ACE_Vector<ACE_TCHAR> cwd_buf(128);
190  cwd_buf.resize(128, ACE_TCHAR(0));
191 
192  while (ACE_OS::getcwd(&cwd_buf[0], cwd_buf.size()) == 0) {
193  if (errno == ERANGE) {
194  cwd_buf.resize(cwd_buf.size() * 2, ACE_TCHAR(0));
195 
196  } else break;
197  }
198 
199  if (cwd_buf[0]) cwd_ = &cwd_buf[0];
200 
201  dds_chdir(dir.c_str());
202  }
203 
204  ~CwdGuard() {
205  dds_chdir(cwd_.c_str());
206  }
207 };
208 
209 void recursive_remove(const ACE_TString& dirname)
210 {
211  using namespace OpenDDS::FileSystemStorage;
212  DDS_Dirent dir(dirname.c_str());
213  {
214  CwdGuard cg(dirname);
215 
216  for (DDS_DIRENT* ent = dir.read(); ent; ent = dir.read()) {
217  if (ent->d_name[0] == ACE_TEXT('.') && (!ent->d_name[1] ||
218  (ent->d_name[1] == ACE_TEXT('.') && !ent->d_name[2]))) {
219  continue; // skip '.' and '..'
220  }
221 
222  if (is_dir(ent->d_name)) {
223  recursive_remove(ent->d_name);
224 
225  } else { // regular file
226  ACE_OS::unlink(ent->d_name);
227  }
228  }
229  }
230  dds_rmdir(dirname.c_str());
231 }
232 
233 } // namespace
234 
236 
237 namespace OpenDDS {
238 namespace FileSystemStorage {
239 
240 #ifdef ACE_WIN32
241 
242 // DDS_DIR
243 
244 struct DDS_DIR {
245  /// The name of the directory we are looking into
246  wchar_t* directory_name_;
247 
248  /// Remember the handle between calls.
249  HANDLE current_handle_;
250 
251  /// The struct for the results
252  ACE_DIRENT* dirent_;
253 
254  /// The struct for intermediate results.
255  WIN32_FIND_DATAW fdata_;
256 
257  /// A flag to remember if we started reading already.
258  int started_reading_;
259 };
260 
261 // DDS_Dirent
262 
264  : dirp_(0)
265 {
266  if (path) open(path);
267 }
268 
269 int DDS_Dirent::open(const ACE_TCHAR* path)
270 {
271  close();
272  ACE_WString wpath =
273 #ifdef ACE_HAS_WINCE
274  path;
275 #else
276  to_win32_long_path(path);
277 #endif
278 
279  // taken from ACE_OS::opendir_emulation() and translated to wchar
280 
281  wchar_t extra[3] = {0, 0, 0};
282  const wchar_t* filename = wpath.c_str();
283 
284  // Check if filename is a directory.
285  DWORD fileAttribute = ::GetFileAttributesW(filename);
286 
287  if (fileAttribute == INVALID_FILE_ATTRIBUTES) {
289  return -1;
290  }
291 
292  if (!(fileAttribute & FILE_ATTRIBUTE_DIRECTORY)) {
293  errno = ENOTDIR;
294  return -1;
295  }
296 
297  size_t const lastchar = ACE_OS::strlen(filename);
298 
299  if (lastchar > 0) {
300  if (filename[lastchar-1] != L'*') {
301  if (filename[lastchar-1] != L'/' && filename[lastchar-1] != L'\\')
302  ACE_OS::strcpy(extra, L"\\*");
303 
304  else
305  ACE_OS::strcpy(extra, L"*");
306  }
307  }
308 
309  ACE_NEW_RETURN(dirp_, DDS_DIR, -1);
310  ACE_NEW_RETURN(dirp_->directory_name_,
311  wchar_t[lastchar + ACE_OS::strlen(extra) + 1],
312  -1);
313  ACE_OS::strcpy(dirp_->directory_name_, filename);
314 
315  if (extra[0])
316  ACE_OS::strcat(dirp_->directory_name_, extra);
317 
318  dirp_->current_handle_ = INVALID_HANDLE_VALUE;
319  dirp_->started_reading_ = 0;
320  dirp_->dirent_ = 0;
321  return 0;
322 }
323 
324 DDS_DIRENT* DDS_Dirent::read()
325 {
326  if (!dirp_) return 0;
327 
328  // taken from ACE_OS::readdir_emulation() and translated to wchar
329  // note that the file name inside ACE_DIRENT is still in ACE_TCHARs as long
330  // as ACE_HAS_TCHAR_DIRENT is defined (true for MSVC, false for GCC/MinGW)
331 
332  if (dirp_->dirent_ != 0) {
333 #ifdef ACE_HAS_TCHAR_DIRENT
334  ACE_OS::free(dirp_->dirent_->d_name);
335 #endif
336  ACE_OS::free(dirp_->dirent_);
337  dirp_->dirent_ = 0;
338  }
339 
340  if (!dirp_->started_reading_) {
341  dirp_->current_handle_ = ::FindFirstFileW(dirp_->directory_name_,
342  &dirp_->fdata_);
343  dirp_->started_reading_ = 1;
344 
345  } else {
346  int retval = ::FindNextFileW(dirp_->current_handle_, &dirp_->fdata_);
347 
348  if (retval == 0) {
349  // Make sure to close the handle explicitly to avoid a leak!
350  ::FindClose(dirp_->current_handle_);
351  dirp_->current_handle_ = INVALID_HANDLE_VALUE;
352  }
353  }
354 
355  if (dirp_->current_handle_ != INVALID_HANDLE_VALUE) {
356  dirp_->dirent_ = (DDS_DIRENT*) ACE_Allocator::instance()->malloc(sizeof(DDS_DIRENT));
357 
358  if (dirp_->dirent_ != 0) {
359 #ifdef ACE_HAS_TCHAR_DIRENT
360  dirp_->dirent_->d_name =
361  (ACE_TCHAR*) ACE_Allocator::instance()->malloc((ACE_OS::strlen(dirp_->fdata_.cFileName)
362  + 1) * sizeof(ACE_TCHAR));
363  ACE_OS::strcpy(dirp_->dirent_->d_name,
364  ACE_TEXT_WCHAR_TO_TCHAR(dirp_->fdata_.cFileName));
365 #else // MinGW: d_name is a fixed-size char array
366  ACE_OS::strncpy(dirp_->dirent_->d_name,
367  ACE_Wide_To_Ascii(dirp_->fdata_.cFileName).char_rep(),
368  sizeof(dirp_->dirent_->d_name));
369 #endif
370  dirp_->dirent_->d_reclen = sizeof(DDS_DIRENT);
371  }
372 
373  return dirp_->dirent_;
374 
375  } else
376  return 0;
377 }
378 
379 void DDS_Dirent::close()
380 {
381  if (dirp_) {
382  if (dirp_->current_handle_ != INVALID_HANDLE_VALUE)
383  ::FindClose(dirp_->current_handle_);
384 
385  dirp_->current_handle_ = INVALID_HANDLE_VALUE;
386  dirp_->started_reading_ = 0;
387 
388  if (dirp_->dirent_ != 0) {
389 #ifdef ACE_HAS_TCHAR_DIRENT
390  ACE_OS::free(dirp_->dirent_->d_name);
391 #endif
392  ACE_OS::free(dirp_->dirent_);
393  }
394 
395  dirp_ = 0;
396  }
397 }
398 
399 DDS_Dirent::~DDS_Dirent()
400 {
401  close();
402 }
403 
404 #elif defined ACE_USES_WCHAR // non-Win32 uses-WChar
405 
406 struct DDS_DIR {
407  ACE_DIR* real_dir_;
408  DDS_DIRENT ent_;
409 
410  DDS_DIR() : real_dir_(), ent_() {}
411 };
412 
414  : dirp_(new DDS_DIR)
415 {
416  if (path) open(path);
417 }
418 
419 int DDS_Dirent::open(const ACE_TCHAR* path)
420 {
421  close();
422  return (dirp_->real_dir_ = ACE_OS::opendir(path)) == 0 ? -1 : 0;
423 }
424 
425 DDS_DIRENT* DDS_Dirent::read()
426 {
427  if (!dirp_->real_dir_) return 0;
428 
429  dirp_->ent_.real_dirent_ = ACE_OS::readdir(dirp_->real_dir_);
430 
431  if (!dirp_->ent_.real_dirent_) return 0;
432 
433  ACE_OS::free(dirp_->ent_.d_name);
434  dirp_->ent_.d_name =
435  ACE_OS::strdup(ACE_TEXT_CHAR_TO_TCHAR(dirp_->ent_.real_dirent_->d_name));
436  return &dirp_->ent_;
437 }
438 
439 void DDS_Dirent::close()
440 {
441  if (dirp_->real_dir_) {
442  ACE_OS::closedir(dirp_->real_dir_);
443  ACE_OS::free(dirp_->ent_.d_name);
444  }
445 
446  dirp_->real_dir_ = 0;
447 }
448 
449 DDS_Dirent::~DDS_Dirent()
450 {
451  close();
452  delete dirp_;
453 }
454 
455 #endif // ACE_WIN32
456 
457 // File
458 
459 File::File(const ACE_TString& fname_phys, const ACE_TString& logical,
460  const Directory::Ptr& parent)
461  : physical_file_()
462  , physical_dir_()
463  , logical_relative_(logical)
464  , parent_(parent)
465 {
466  String_Index_t last_slash = fname_phys.rfind(ACE_TEXT('/'));
467 
468  if (last_slash == ACE_TString::npos) {
469  physical_file_ = fname_phys;
470  physical_dir_ = ACE_TEXT(".");
471 
472  } else {
473  physical_file_ = fname_phys.c_str() + last_slash + 1;
474  physical_dir_.set(fname_phys.c_str(), last_slash, true);
475  }
476 }
477 
478 bool File::write(std::ofstream& stream)
479 {
480  CwdGuard cg(physical_dir_);
482  ios::binary | ios::out);
483  return !stream.bad() && !stream.fail();
484 }
485 
486 bool File::read(std::ifstream& stream)
487 {
488  CwdGuard cg(physical_dir_);
490  ios::binary | ios::in);
491  return !stream.bad() && !stream.fail();
492 }
493 
495 {
496  int unlink_result = -1;
497  {
498  CwdGuard cg(physical_dir_);
499  unlink_result = ACE_OS::unlink(physical_file_.c_str());
500  }
501 
502  if (unlink_result != -1) {
503  parent_->removing(logical_relative_, true);
504  return true;
505  }
506 
507  return false;
508 }
509 
511 {
513 }
514 
515 // Directory
516 
517 /*static*/ Directory::Ptr Directory::create(const char* dirname)
518 {
519  return DCPS::make_rch<Directory>(ACE_TEXT_CHAR_TO_TCHAR(dirname), ACE_TEXT(""), Directory::Ptr ());
520 }
521 
523 {
524  return physical_dirname_ + relative;
525 }
526 
528 {
529  return FileIterator(files_.begin(), rchandle_from(this));
530 }
531 
533 {
534  return FileIterator(files_.end(), rchandle_from(this));
535 }
536 
538 {
539  if (std::strlen(name) >= FSS_MAX_FILE_NAME) {
540  throw std::runtime_error("file name too long");
541  }
542 
543  ACE_TString t_name(ACE_TEXT_CHAR_TO_TCHAR(name));
544  Map::iterator it = files_.find(t_name);
545 
546  if (it == files_.end()) {
547  return make_new_file(t_name);
548 
549  } else {
550  return DCPS::make_rch<File>(full_path(it->second), it->first, rchandle_from(this));
551  }
552 }
553 
555 {
556  if (dirs_.find(t_name) != dirs_.end()) {
557  throw std::runtime_error("Can't create a file with the same name as "
558  "an existing directory.");
559  }
560 
561  ACE_TString phys = add_entry() + b32h_encode(t_name.c_str());
562  files_[t_name] = phys;
563 
564  CwdGuard cg(physical_dirname_);
565  // touch the file since the user has asked to create it
566  std::FILE* fh = std::fopen(ACE_TEXT_ALWAYS_CHAR(phys.c_str()), "w");
567 
568  if (!fh) throw std::runtime_error("Can't create the file");
569 
570  std::fclose(fh);
571  return DCPS::make_rch<File>(physical_dirname_ + phys, t_name, rchandle_from(this));
572 }
573 
575 {
576  ACE_TString logical;
577 
578  if (files_.empty()) {
579  logical = FSS_DEFAULT_FILE_NAME;
580 
581  } else {
582  Map::iterator last = --files_.end();
583  logical = last->first;
584 
585  if (!increment(logical)) {
586  throw std::runtime_error("out of range for create_next_file");
587  }
588  }
589 
590  return make_new_file(logical);
591 }
592 
594 {
595  return DirectoryIterator(dirs_.begin(), rchandle_from(this));
596 }
597 
599 {
600  return DirectoryIterator(dirs_.end(), rchandle_from(this));
601 }
602 
604 {
605  Directory::Ptr dir = rchandle_from(this);
606  typedef OPENDDS_VECTOR(OPENDDS_STRING)::const_iterator iterator;
607 
608  for (iterator iter = path.begin(), end = path.end(); iter != end; ++iter) {
609  dir = dir->get_subdir(iter->c_str());
610  }
611 
612  return dir;
613 }
614 
616 {
617  ACE_TString t_name = ACE_TEXT_CHAR_TO_TCHAR(name);
618  Map::iterator it = dirs_.find(t_name);
619 
620  if (it == dirs_.end()) {
621  return make_new_subdir(t_name);
622 
623  } else {
624  return DCPS::make_rch<Directory>(full_path(it->second), it->first, rchandle_from(this));
625  }
626 }
627 
629 {
630  ACE_TString logical;
631 
632  if (dirs_.empty()) {
633  logical = FSS_DEFAULT_DIR_NAME;
634 
635  } else {
636  Map::iterator last = --dirs_.end();
637  logical = last->first;
638 
639  if (!increment(logical)) {
640  throw std::runtime_error("out of range for create_next_dir");
641  }
642  }
643 
644  return make_new_subdir(logical);
645 }
646 
648 {
649  if (files_.find(t_name) != files_.end()) {
650  throw std::runtime_error("Can't create a directory with the same "
651  "name as an existing file.");
652  }
653 
654  ACE_TString logical(t_name.c_str(),
655  (std::min)(FSS_MAX_FILE_NAME, t_name.length()));
656  ACE_TString phys_prefix = add_entry();
657  ACE_TString phys_base = b32h_encode(logical.c_str());
658 
659  if (t_name.length() >= FSS_MAX_FILE_NAME) {
660  unsigned int& counter = long_names_[phys_prefix + phys_base];
661 
662  if (counter == 99999) {
663  throw std::runtime_error("Long directory name out of range");
664  }
665 
666  phys_base += ACE_TEXT(". X"); // snprintf will clobber the X with a 0
667  ACE_TCHAR* buf = &phys_base[0] + phys_base.length() - 6;
668  ACE_OS::snprintf(buf, 6, ACE_TEXT("%05u"), counter++);
669  phys_base = phys_base.substr(0, phys_base.length() - 1); // trim the 0
670  }
671 
672  ACE_TString phys = phys_prefix + phys_base;
673  dirs_[t_name] = phys;
674  {
675  CwdGuard cg(physical_dirname_);
676 
677  if (dds_mkdir(phys.c_str()) == -1) {
678  throw std::runtime_error("Can't create directory");
679  }
680 
681  if ((phys_prefix.length() > 0 && dds_chdir(phys_prefix.c_str()) == -1)
682  || dds_chdir(phys_base.c_str()) == -1) {
683  dds_rmdir(phys.c_str());
684  throw std::runtime_error("Can't change to newly created directory");
685  }
686 
687  std::ofstream fn("_fullname");
688  fn << t_name << '\n';
689  }
690  return DCPS::make_rch<Directory>(physical_dirname_ + phys, t_name, rchandle_from(this));
691 }
692 
694 {
695  if (overflow_.empty()) {
696  overflow_[0] = 1;
697  return ACE_TEXT("");
698  }
699 
700  typedef OPENDDS_MAP(unsigned int, unsigned int)::iterator iterator;
701  // find existing overflow bucket with capacity
702  bool found_gap(false);
703  unsigned int last_seen(0), unused_bucket(0);
704 
705  for (iterator iter = overflow_.begin(), end = overflow_.end();
706  iter != end; ++iter) {
707  if (iter->second < OPENDDS_FILESYSTEMSTORAGE_MAX_FILES_PER_DIR) {
708  ++iter->second;
709 
710  if (iter->first == 0) return ACE_TEXT("");
711 
712  return overflow_dir_name(iter->first);
713  }
714 
715  if (!found_gap && iter->first > last_seen + 1) {
716  found_gap = true;
717  unused_bucket = last_seen + 1;
718  }
719 
720  last_seen = iter->first;
721  }
722 
723  if (!found_gap) {
724  if (last_seen == FSS_MAX_OVERFLOW_DIR) {
725  throw std::runtime_error("Overflow serial # out of range.");
726  }
727 
728  unused_bucket = last_seen + 1;
729  }
730 
731  overflow_[unused_bucket] = 1;
732  ACE_TString dir_name = overflow_dir_name(unused_bucket);
733  CwdGuard cg(physical_dirname_);
734 
735  if (dds_mkdir(dir_name.c_str()) == -1) {
736  throw std::runtime_error("Can't create overflow directory");
737  }
738 
739  return dir_name;
740 }
741 
743 {
744  if (!parent_.is_nil()) parent_->removing(logical_dirname_, false);
745 
746  parent_.reset();
747  recursive_remove(physical_dirname_);
748  overflow_.clear();
749  files_.clear();
750  dirs_.clear();
751  long_names_.clear();
752 }
753 
755 {
756  return ACE_TEXT_ALWAYS_CHAR(logical_dirname_.c_str());
757 }
758 
759 Directory::Directory(const ACE_TString& dirname, const ACE_TString& logical,
760  const Directory::Ptr& parent)
761  : parent_(parent)
762  , physical_dirname_(dirname)
763  , logical_dirname_(logical)
764 {
765  add_slash(physical_dirname_);
766 
767  bool ok(true);
768  DDS_Dirent dir;
769 
770  if (dir.open(physical_dirname_.c_str()) == -1) {
771  ok = false;
772 
773  if (errno == ENOENT && dds_mkdir(physical_dirname_.c_str()) != -1
774  && dir.open(physical_dirname_.c_str()) != -1) {
775  ok = true;
776  }
777  }
778 
779  if (!ok) throw std::runtime_error("Can't open or create directory");
780 
781  scan_dir(ACE_TEXT(""), dir, 0);
782 }
783 
784 void Directory::scan_dir(const ACE_TString& relative, DDS_Dirent& dir,
785  unsigned int overflow_index)
786 {
787  ACE_TString path = physical_dirname_ + relative;
788  add_slash(path);
789 
790  while (DDS_DIRENT* ent = dir.read()) {
791  if (ent->d_name[0] == ACE_TEXT('.') && (!ent->d_name[1] ||
792  (ent->d_name[1] == ACE_TEXT('.') && !ent->d_name[2]))) {
793  continue; // skip '.' and '..'
794  }
795 
796  ACE_TString file = path + ent->d_name;
797 
798  if (is_dir(file.c_str())) {
799  ACE_TString phys(relative);
800  add_slash(phys);
801  phys += ent->d_name;
802 
803  if (ACE_OS::strncmp(ent->d_name, ACE_TEXT("_overflow."), 10) == 0) {
804  unsigned int n = ACE_OS::atoi(ent->d_name + 10);
805  DDS_Dirent overflow(file.c_str());
806  scan_dir(ent->d_name, overflow, n);
807 
808  } else if (ACE_OS::strlen(ent->d_name) <= FSS_MAX_FILE_NAME_ENCODED) {
809  dirs_[b32h_decode(ent->d_name)] = phys;
810  ++overflow_[overflow_index];
811 
812  } else {
813  CwdGuard cg(file);
814  std::ifstream fn("_fullname");
815  OPENDDS_STRING fullname;
816 
817  if (!std::getline(fn, fullname)) {
818  throw std::runtime_error("Can't read .../_fullname");
819  }
820 
821  ACE_TString full_t(ACE_TEXT_CHAR_TO_TCHAR(fullname.c_str()));
822  dirs_[full_t] = phys;
823  ++overflow_[overflow_index];
824 
825  String_Index_t idx = phys.rfind(ACE_TEXT('.'));
826 
827  if (idx == ACE_TString::npos) {
828  throw std::runtime_error("Badly formatted long dir name");
829  }
830 
831  ACE_TString prefix(phys.c_str(), idx);
832  unsigned int serial = ACE_OS::atoi(&phys[idx + 1]);
833  unsigned int& counter = long_names_[prefix];
834 
835  if (serial >= counter) counter = serial + 1;
836  }
837 
838  } else { // regular file
839  if (ent->d_name[0] != ACE_TEXT('_')) {
840  files_[b32h_decode(ent->d_name)] = ent->d_name;
841  ++overflow_[overflow_index];
842  }
843  }
844  }
845 }
846 
847 void Directory::removing(const ACE_TString& child, bool file)
848 {
849  Map& m = file ? files_ : dirs_;
850  Map::iterator iter = m.find(child);
851 
852  if (iter == m.end()) return;
853 
854  const ACE_TString& phys = iter->second;
855  String_Index_t idx = phys.find(ACE_TEXT("_overflow."));
856  unsigned int bucket = (idx == 0 ? ACE_OS::atoi(&phys[idx + 10]) : 0);
857 
858  if (--overflow_[bucket] == 0 && bucket > 0) {
859  overflow_.erase(bucket);
860  idx = phys.find(ACE_TEXT('/'));
861  ACE_TString ov_dir = physical_dirname_ + ACE_TString(phys.c_str(), idx);
862  dds_rmdir(ov_dir.c_str());
863  }
864 
865  m.erase(iter);
866 }
867 
868 // Base32Hex
869 
871 {
872  static const ACE_TCHAR lookup[] =
873  ACE_TEXT("0123456789ABCDEFGHIJKLMNOPQRSTUV");
874  static const ACE_TCHAR padding[] = ACE_TEXT("======");
875  static const size_t enc[] = {0, 2, 4, 5, 7}; // #input -> #non-padded output
877 
878  for (size_t len = ACE_OS::strlen(decoded); *decoded; decoded += 5, len -= 5) {
879  ACE_UINT64 chunk = 0;
880 
881  for (size_t i(0); i < 5 && i < len; ++i) {
882  chunk |= static_cast<ACE_UINT64>(decoded[i] & 0xFF) << ((4 - i) * 8);
883  }
884 
885  size_t limit = (len < 5) ? enc[len] : 8;
886 
887  for (size_t i(0); i < limit; ++i) {
888  unsigned char val =
889  static_cast<unsigned char>(chunk >>((7 - i) * 5)) & 0x1F;
890  encoded += lookup[val];
891  }
892 
893  if (len < 5) {
894  encoded.append(padding, 8 - enc[len]);
895  return encoded;
896  }
897  }
898 
899  return encoded;
900 }
901 
903 {
904  // #before first '=' -> #output
905  static const size_t dec[] = {0, 0, 1, 0, 2, 3, 0, 4, 0};
906  ACE_TString decoded;
907 
908  for (; *encoded; encoded += 8) {
909  ACE_UINT64 chunk = 0;
910  size_t i = 0;
911 
912  for (; i < 8 && encoded[i] != ACE_TEXT('='); ++i) {
913  char idx = (encoded[i] <= ACE_TEXT('9'))
914  ? (encoded[i] - ACE_TEXT('0'))
915  : (10 + encoded[i] - ACE_TEXT('A'));
916  chunk |= static_cast<ACE_UINT64>(idx) << ((7 - i) * 5);
917  }
918 
919  size_t limit = (encoded[i] == ACE_TEXT('=')) ? dec[i] : 5;
920 
921  for (size_t j(0); j < limit; ++j) {
922  decoded += static_cast<ACE_TCHAR>(chunk >>((4 - j) * 8)) & 0xFF;
923  }
924  }
925 
926  return decoded;
927 }
928 
929 } // namespace FileSystemStorage
930 } // namespace OpenDDS
931 
933 
934 #endif
RcHandle< T > rchandle_from(T *pointer)
Definition: RcHandle_T.h:310
RcHandle< File > get_file(const char *name)
RcHandle< File > create_next_file()
assumes all files in this dir are created with this API
const ACE_CHAR_T * c_str(void) const
void free(void *)
int chdir(const char *path)
Directory::Ptr make_new_subdir(const ACE_TString &logical)
#define ACE_TEXT_ALWAYS_WCHAR(STRING)
Directory::Ptr get_subdir(const char *name)
ACE_TString b32h_encode(const ACE_TCHAR *decoded)
int snprintf(char *buf, size_t maxlen, const char *format,...) ACE_GCC_FORMAT_ATTRIBUTE(printf
int strncmp(const char *s, const char *t, size_t len)
size_t size(void) const
void scan_dir(const ACE_TString &relative, DDS_Dirent &dir, unsigned int overflow_index)
int ACE_DIR
#define ACE_TEXT_ALWAYS_CHAR(STRING)
static Ptr create(const char *root_path)
#define ACE_DIRENT
char * char_rep(void)
void closedir(ACE_DIR *)
static FilterEvaluator::AstNodeWrapper child(const FilterEvaluator::AstNodeWrapper &node, size_t idx)
int stat(const char *file, ACE_stat *)
size_type rfind(char c, size_type pos=npos) const
size_t strlen(const char *s)
int close(ACE_HANDLE handle)
size_t String_Index_t
static size_type const npos
void removing(const ACE_TString &logical_child, bool file)
int unlink(const char *path)
#define OPENDDS_STRING
struct ACE_DIRENT * readdir(ACE_DIR *)
char * getcwd(char *, size_t)
Directory(const ACE_TString &root_path, const ACE_TString &logical, const Directory::Ptr &parent)
ACE_HANDLE open(const char *filename, int mode, mode_t perms=ACE_DEFAULT_OPEN_PERMS, LPSECURITY_ATTRIBUTES sa=0)
char ACE_TCHAR
void set(const char *s, bool release=true)
int mkdir(const char *path, mode_t mode=ACE_DEFAULT_DIR_PERMS)
#define OPENDDS_FILESYSTEMSTORAGE_MAX_FILES_PER_DIR
ACE_String_Base< char > & append(const char *s, size_type slen)
const ACE_TCHAR * dirname(const ACE_TCHAR *pathname, ACE_TCHAR delim=ACE_DIRECTORY_SEPARATOR_CHAR)
static ACE_Allocator * instance(void)
ACE_TString full_path(const ACE_TString &relative) const
#define ACE_ADAPT_RETVAL(OP, RESULT)
#define ACE_TEXT_CHAR_TO_TCHAR(STRING)
Directory::Ptr create_next_dir()
assumes all subdirectories in this dir are created with this API
RcHandle< File > make_new_file(const ACE_TString &t_name)
ACE_TEXT("TCP_Factory")
int atoi(const char *s)
ACE_DIR * opendir(const ACE_TCHAR *filename)
#define OPENDDS_MAP(K, V)
int set_errno_to_last_error(void)
unsigned long long ACE_UINT64
Directory::Ptr get_dir(const OPENDDS_VECTOR(OPENDDS_STRING)&path)
#define OPENDDS_VECTOR(T)
size_type find(const ACE_String_Base< ACE_CHAR_T > &str, size_type pos=0) const
struct _stat ACE_stat
#define DDS_Dirent
ACE_TString b32h_decode(const ACE_TCHAR *encoded)
ACE_String_Base< ACE_CHAR_T > substr(size_type offset, size_type length=npos) const
#define OPENDDS_END_VERSIONED_NAMESPACE_DECL
char * strcat(char *s, const char *t)
int rmdir(const char *path)
char * strdup(const char *s)
#define ACE_NEW_RETURN(POINTER, CONSTRUCTOR, RET_VAL)
size_type length(void) const
sequence<<%SCOPED%><%TYPE%><%SEQ%> local interface<%TYPE%> out string encoded
Definition: IDLTemplate.txt:4
bool read(std::ifstream &stream)
void resize(const size_t new_size, const T &t)
The Internal API and Implementation of OpenDDS.
Definition: AddressCache.h:28
char * strcpy(char *s, const char *t)
#define ACE_TEXT_WCHAR_TO_TCHAR(STRING)
virtual void * malloc(size_type nbytes)=0
#define DDS_DIRENT
char * strncpy(char *s, const char *t, size_t len)
bool write(std::ofstream &stream)