FileSystemStorage.cpp

Go to the documentation of this file.
00001 /*
00002  *
00003  *
00004  * Distributed under the OpenDDS License.
00005  * See: http://www.opendds.org/license.html
00006  */
00007 
00008 #include "DCPS/DdsDcps_pch.h" //Only the _pch include should start with DCPS/
00009 
00010 #ifndef OPENDDS_SAFETY_PROFILE
00011 
00012 #include "FileSystemStorage.h"
00013 
00014 #include "ace/Dirent.h"
00015 #include "ace/Vector_T.h"
00016 #include "ace/OS_NS_sys_stat.h"
00017 #include "ace/OS_NS_macros.h"
00018 #include "ace/OS_NS_unistd.h"
00019 #include "ace/OS_NS_stdio.h"
00020 
00021 #include <cstdio>
00022 #include <cstring>
00023 #include <stdexcept>
00024 #include <fstream>
00025 
00026 typedef size_t String_Index_t;
00027 
00028 namespace {
00029 
00030 const size_t FSS_MAX_FILE_NAME = 150, FSS_MAX_FILE_NAME_ENCODED = 240,
00031   FSS_MAX_OVERFLOW_DIR = 9999;
00032 const ACE_TCHAR FSS_DEFAULT_FILE_NAME[] = ACE_TEXT("F00000000000000");
00033 const ACE_TCHAR FSS_DEFAULT_DIR_NAME[]  = ACE_TEXT("D00000000000000");
00034 
00035 void add_slash(ACE_TString& str)
00036 {
00037   if (str.length() == 0) return;
00038 
00039   if (str[str.length() - 1] == ACE_TEXT('\\')) {
00040     str[str.length() - 1] = ACE_TEXT('/');
00041 
00042   } else if (str[str.length() - 1] != ACE_TEXT('/')) {
00043     str += ACE_TEXT('/');
00044   }
00045 }
00046 
00047 // increment a filename in a lexicographical ordering
00048 bool increment(ACE_TString& logical)
00049 {
00050   for (size_t i = logical.length() - 1; i >= 1; --i) {
00051     //we could use the entire range of 255 values but we'll keep
00052     //these names to 7-bit ASCII
00053     if (logical[i] < 0x7F) {
00054       ++logical[i];
00055       return true;
00056     }
00057   }
00058 
00059   return false;
00060 }
00061 
00062 // support long paths (> 260 chars in absolute path) on Win32
00063 /* Windows Mobile / Windows CE (WinCE) porting notes:
00064  *   These platforms have no concept of "current directory"
00065  *   and the Win32-specific \\?\ syntax may or may not work.
00066  *   This file has been set up to compile under WinCE but we
00067  *   don't expect that persistent storage to work well at runtime
00068  *   so PERSISTENT_DURABILITY_QOS may not actually work.
00069  */
00070 #if defined ACE_WIN32 && !defined ACE_HAS_WINCE
00071 
00072 void fwd_slash_to_back_slash(ACE_WString& str)
00073 {
00074   for (String_Index_t idx = str.find(L'/'); idx != ACE_WString::npos;
00075        idx = str.find(L'/', idx + 1)) {
00076     str[idx] = L'\\';
00077   }
00078 }
00079 
00080 bool is_relative(const ACE_TCHAR* path)
00081 {
00082   return !path[0] ||
00083          (path[1] != ACE_TEXT(':') &&
00084           (ACE_OS::strncmp(ACE_TEXT("\\\\?\\"), path, 4) != 0));
00085 }
00086 
00087 /// @returns \\?<absolute_path>
00088 ACE_WString to_win32_long_path(const ACE_TCHAR* path)
00089 {
00090   ACE_WString wpath = ACE_TEXT_ALWAYS_WCHAR(path);
00091   fwd_slash_to_back_slash(wpath);
00092   ACE_WString dir;
00093 
00094   if (is_relative(path)) {
00095     DWORD sz = ::GetCurrentDirectoryW(0, 0);
00096     ACE_Vector<wchar_t> cur(sz);
00097     cur.resize(sz, L'\0');
00098     ::GetCurrentDirectoryW(sz, &cur[0]);
00099     dir = &cur[0];
00100 
00101     if (dir[dir.length() - 1] != L'\\') dir += L'\\';
00102   }
00103 
00104   if (dir.substr(0, 4) != L"\\\\?\\" && wpath.substr(0, 4) != L"\\\\?\\")
00105     dir = L"\\\\?\\" + dir;
00106 
00107   dir += wpath;
00108   return dir;
00109 }
00110 
00111 int dds_mkdir(const ACE_TCHAR* path)
00112 {
00113   ACE_WString wpath = to_win32_long_path(path);
00114   ACE_WIN32CALL_RETURN(ACE_ADAPT_RETVAL(::CreateDirectoryW(wpath.c_str(), 0),
00115                                         ace_result_), int, -1);
00116 }
00117 
00118 int dds_chdir(const ACE_TCHAR* path)
00119 {
00120   // In all the other cases we are going to \\?<absolute> long paths, but
00121   // SetCurrentDirectory() doesn't allow this workaround.
00122   ACE_WString wpath = to_win32_long_path(path);
00123   wchar_t spath[MAX_PATH];
00124 
00125   if (0 == ::GetShortPathNameW(wpath.c_str(), spath, MAX_PATH)) {
00126     throw std::runtime_error("GetShortPathNameW failed.");
00127   }
00128 
00129   ACE_OSCALL_RETURN(::_wchdir(spath), int, -1);
00130 }
00131 
00132 bool is_dir(const ACE_TCHAR* path)
00133 {
00134   ACE_WString wpath = to_win32_long_path(path);
00135   DWORD attrib = ::GetFileAttributesW(wpath.c_str());
00136 
00137   if (attrib != INVALID_FILE_ATTRIBUTES) {
00138     return attrib & FILE_ATTRIBUTE_DIRECTORY;
00139   }
00140 
00141   return false;
00142 }
00143 
00144 int dds_rmdir(const ACE_TCHAR* path)
00145 {
00146   ACE_WString wpath = to_win32_long_path(path);
00147   ACE_WIN32CALL_RETURN(ACE_ADAPT_RETVAL(::RemoveDirectoryW(wpath.c_str()),
00148                                         ace_result_), int, -1);
00149 }
00150 
00151 #else // !ACE_WIN32
00152 
00153 inline int dds_mkdir(const ACE_TCHAR* path)
00154 {
00155   return ACE_OS::mkdir(path);
00156 }
00157 
00158 inline int dds_chdir(const ACE_TCHAR* path)
00159 {
00160   return ACE_OS::chdir(path);
00161 }
00162 
00163 inline bool is_dir(const ACE_TCHAR* path)
00164 {
00165   ACE_stat st;
00166   return ACE_OS::stat(path, &st) != -1 && (st.st_mode & S_IFDIR);
00167 }
00168 
00169 inline int dds_rmdir(const ACE_TCHAR* path)
00170 {
00171   return ACE_OS::rmdir(path);
00172 }
00173 
00174 #endif // ACE_WIN32
00175 
00176 ACE_TString overflow_dir_name(unsigned int n)
00177 {
00178   ACE_TString of_name = ACE_TEXT("_overflow.    /");
00179   ACE_TCHAR* buf = &of_name[10];
00180   ACE_OS::snprintf(buf, 5, ACE_TEXT("%04u"), n);
00181   of_name[14] = L'/'; // snprintf has clobbered / with a null
00182   return of_name;
00183 }
00184 
00185 struct CwdGuard {
00186   ACE_TString cwd_;
00187 
00188   explicit CwdGuard(const ACE_TString& dir) {
00189     ACE_Vector<ACE_TCHAR> cwd_buf(128);
00190     cwd_buf.resize(128, ACE_TCHAR(0));
00191 
00192     while (ACE_OS::getcwd(&cwd_buf[0], cwd_buf.size()) == 0) {
00193       if (errno == ERANGE) {
00194         cwd_buf.resize(cwd_buf.size() * 2, ACE_TCHAR(0));
00195 
00196       } else break;
00197     }
00198 
00199     if (cwd_buf[0]) cwd_ = &cwd_buf[0];
00200 
00201     dds_chdir(dir.c_str());
00202   }
00203 
00204   ~CwdGuard() {
00205     dds_chdir(cwd_.c_str());
00206   }
00207 };
00208 
00209 void recursive_remove(const ACE_TString& dirname)
00210 {
00211   using namespace OpenDDS::FileSystemStorage;
00212   DDS_Dirent dir(dirname.c_str());
00213   {
00214     CwdGuard cg(dirname);
00215 
00216     for (DDS_DIRENT* ent = dir.read(); ent; ent = dir.read()) {
00217       if (ent->d_name[0] == ACE_TEXT('.') && (!ent->d_name[1] ||
00218           (ent->d_name[1] == ACE_TEXT('.') && !ent->d_name[2]))) {
00219         continue; // skip '.' and '..'
00220       }
00221 
00222       if (is_dir(ent->d_name)) {
00223         recursive_remove(ent->d_name);
00224 
00225       } else { // regular file
00226         ACE_OS::unlink(ent->d_name);
00227       }
00228     }
00229   }
00230   dds_rmdir(dirname.c_str());
00231 }
00232 
00233 } // namespace
00234 
00235 namespace OpenDDS {
00236 namespace FileSystemStorage {
00237 
00238 #ifdef ACE_WIN32
00239 
00240 // DDS_DIR
00241 
00242 struct DDS_DIR {
00243   /// The name of the directory we are looking into
00244   wchar_t* directory_name_;
00245 
00246   /// Remember the handle between calls.
00247   HANDLE current_handle_;
00248 
00249   /// The struct for the results
00250   ACE_DIRENT* dirent_;
00251 
00252   /// The struct for intermediate results.
00253   WIN32_FIND_DATAW fdata_;
00254 
00255   /// A flag to remember if we started reading already.
00256   int started_reading_;
00257 };
00258 
00259 // DDS_Dirent
00260 
00261 DDS_Dirent::DDS_Dirent(const ACE_TCHAR* path)
00262   : dirp_(0)
00263 {
00264   if (path) open(path);
00265 }
00266 
00267 int DDS_Dirent::open(const ACE_TCHAR* path)
00268 {
00269   close();
00270   ACE_WString wpath =
00271 #ifdef ACE_HAS_WINCE
00272     path;
00273 #else
00274     to_win32_long_path(path);
00275 #endif
00276 
00277   // taken from ACE_OS::opendir_emulation() and translated to wchar
00278 
00279   wchar_t extra[3] = {0, 0, 0};
00280   const wchar_t* filename = wpath.c_str();
00281 
00282   // Check if filename is a directory.
00283   DWORD fileAttribute = ::GetFileAttributesW(filename);
00284 
00285   if (fileAttribute == INVALID_FILE_ATTRIBUTES) {
00286     ACE_OS::set_errno_to_last_error();
00287     return -1;
00288   }
00289 
00290   if (!(fileAttribute & FILE_ATTRIBUTE_DIRECTORY)) {
00291     errno = ENOTDIR;
00292     return -1;
00293   }
00294 
00295   size_t const lastchar = ACE_OS::strlen(filename);
00296 
00297   if (lastchar > 0) {
00298     if (filename[lastchar-1] != L'*') {
00299       if (filename[lastchar-1] != L'/' && filename[lastchar-1] != L'\\')
00300         ACE_OS::strcpy(extra, L"\\*");
00301 
00302       else
00303         ACE_OS::strcpy(extra, L"*");
00304     }
00305   }
00306 
00307   ACE_NEW_RETURN(dirp_, DDS_DIR, -1);
00308   ACE_NEW_RETURN(dirp_->directory_name_,
00309                  wchar_t[lastchar + ACE_OS::strlen(extra) + 1],
00310                  -1);
00311   ACE_OS::strcpy(dirp_->directory_name_, filename);
00312 
00313   if (extra[0])
00314     ACE_OS::strcat(dirp_->directory_name_, extra);
00315 
00316   dirp_->current_handle_ = INVALID_HANDLE_VALUE;
00317   dirp_->started_reading_ = 0;
00318   dirp_->dirent_ = 0;
00319   return 0;
00320 }
00321 
00322 DDS_DIRENT* DDS_Dirent::read()
00323 {
00324   if (!dirp_) return 0;
00325 
00326   // taken from ACE_OS::readdir_emulation() and translated to wchar
00327   //   note that the file name inside ACE_DIRENT is still in ACE_TCHARs as long
00328   //   as ACE_HAS_TCHAR_DIRENT is defined (true for MSVC, false for GCC/MinGW)
00329 
00330   if (dirp_->dirent_ != 0) {
00331 #ifdef ACE_HAS_TCHAR_DIRENT
00332     ACE_OS::free(dirp_->dirent_->d_name);
00333 #endif
00334     ACE_OS::free(dirp_->dirent_);
00335     dirp_->dirent_ = 0;
00336   }
00337 
00338   if (!dirp_->started_reading_) {
00339     dirp_->current_handle_ = ::FindFirstFileW(dirp_->directory_name_,
00340                                               &dirp_->fdata_);
00341     dirp_->started_reading_ = 1;
00342 
00343   } else {
00344     int retval = ::FindNextFileW(dirp_->current_handle_, &dirp_->fdata_);
00345 
00346     if (retval == 0) {
00347       // Make sure to close the handle explicitly to avoid a leak!
00348       ::FindClose(dirp_->current_handle_);
00349       dirp_->current_handle_ = INVALID_HANDLE_VALUE;
00350     }
00351   }
00352 
00353   if (dirp_->current_handle_ != INVALID_HANDLE_VALUE) {
00354     dirp_->dirent_ = (DDS_DIRENT*) ACE_Allocator::instance()->malloc(sizeof(DDS_DIRENT));
00355 
00356     if (dirp_->dirent_ != 0) {
00357 #ifdef ACE_HAS_TCHAR_DIRENT
00358       dirp_->dirent_->d_name =
00359         (ACE_TCHAR*) ACE_Allocator::instance()->malloc((ACE_OS::strlen(dirp_->fdata_.cFileName)
00360                                      + 1) * sizeof(ACE_TCHAR));
00361       ACE_OS::strcpy(dirp_->dirent_->d_name,
00362                      ACE_TEXT_WCHAR_TO_TCHAR(dirp_->fdata_.cFileName));
00363 #else // MinGW: d_name is a fixed-size char array
00364       ACE_OS::strncpy(dirp_->dirent_->d_name,
00365                       ACE_Wide_To_Ascii(dirp_->fdata_.cFileName).char_rep(),
00366                       sizeof(dirp_->dirent_->d_name));
00367 #endif
00368       dirp_->dirent_->d_reclen = sizeof(DDS_DIRENT);
00369     }
00370 
00371     return dirp_->dirent_;
00372 
00373   } else
00374     return 0;
00375 }
00376 
00377 void DDS_Dirent::close()
00378 {
00379   if (dirp_) {
00380     if (dirp_->current_handle_ != INVALID_HANDLE_VALUE)
00381       ::FindClose(dirp_->current_handle_);
00382 
00383     dirp_->current_handle_ = INVALID_HANDLE_VALUE;
00384     dirp_->started_reading_ = 0;
00385 
00386     if (dirp_->dirent_ != 0) {
00387 #ifdef ACE_HAS_TCHAR_DIRENT
00388       ACE_OS::free(dirp_->dirent_->d_name);
00389 #endif
00390       ACE_OS::free(dirp_->dirent_);
00391     }
00392 
00393     dirp_ = 0;
00394   }
00395 }
00396 
00397 DDS_Dirent::~DDS_Dirent()
00398 {
00399   close();
00400 }
00401 
00402 #elif defined ACE_USES_WCHAR // non-Win32 uses-WChar
00403 
00404 struct DDS_DIR {
00405   ACE_DIR* real_dir_;
00406   DDS_DIRENT ent_;
00407 
00408   DDS_DIR() : real_dir_(), ent_() {}
00409 };
00410 
00411 DDS_Dirent::DDS_Dirent(const ACE_TCHAR* path)
00412   : dirp_(new DDS_DIR)
00413 {
00414   if (path) open(path);
00415 }
00416 
00417 int DDS_Dirent::open(const ACE_TCHAR* path)
00418 {
00419   close();
00420   return (dirp_->real_dir_ = ACE_OS::opendir(path)) == 0 ? -1 : 0;
00421 }
00422 
00423 DDS_DIRENT* DDS_Dirent::read()
00424 {
00425   if (!dirp_->real_dir_) return 0;
00426 
00427   dirp_->ent_.real_dirent_ = ACE_OS::readdir(dirp_->real_dir_);
00428 
00429   if (!dirp_->ent_.real_dirent_) return 0;
00430 
00431   ACE_OS::free(dirp_->ent_.d_name);
00432   dirp_->ent_.d_name =
00433     ACE_OS::strdup(ACE_TEXT_CHAR_TO_TCHAR(dirp_->ent_.real_dirent_->d_name));
00434   return &dirp_->ent_;
00435 }
00436 
00437 void DDS_Dirent::close()
00438 {
00439   if (dirp_->real_dir_) {
00440     ACE_OS::closedir(dirp_->real_dir_);
00441     ACE_OS::free(dirp_->ent_.d_name);
00442   }
00443 
00444   dirp_->real_dir_ = 0;
00445 }
00446 
00447 DDS_Dirent::~DDS_Dirent()
00448 {
00449   close();
00450   delete dirp_;
00451 }
00452 
00453 #endif // ACE_WIN32
00454 
00455 // File
00456 
00457 File::File(const ACE_TString& fname_phys, const ACE_TString& logical,
00458            Directory* parent)
00459   : physical_file_()
00460   , physical_dir_()
00461   , logical_relative_(logical)
00462   , parent_(parent, false)
00463 {
00464   String_Index_t last_slash = fname_phys.rfind(ACE_TEXT('/'));
00465 
00466   if (last_slash == ACE_TString::npos) {
00467     physical_file_ = fname_phys;
00468     physical_dir_ = ACE_TEXT(".");
00469 
00470   } else {
00471     physical_file_ = fname_phys.c_str() + last_slash + 1;
00472     physical_dir_.set(fname_phys.c_str(), last_slash, true);
00473   }
00474 }
00475 
00476 bool File::write(std::ofstream& stream)
00477 {
00478   CwdGuard cg(physical_dir_);
00479   stream.open(ACE_TEXT_ALWAYS_CHAR(physical_file_.c_str()),
00480               ios::binary | ios::out);
00481   return !stream.bad() && !stream.fail();
00482 }
00483 
00484 bool File::read(std::ifstream& stream)
00485 {
00486   CwdGuard cg(physical_dir_);
00487   stream.open(ACE_TEXT_ALWAYS_CHAR(physical_file_.c_str()),
00488               ios::binary | ios::in);
00489   return !stream.bad() && !stream.fail();
00490 }
00491 
00492 bool File::remove()
00493 {
00494   int unlink_result = -1;
00495   {
00496     CwdGuard cg(physical_dir_);
00497     unlink_result = ACE_OS::unlink(physical_file_.c_str());
00498   }
00499 
00500   if (unlink_result != -1) {
00501     parent_->removing(logical_relative_, true);
00502     return true;
00503   }
00504 
00505   return false;
00506 }
00507 
00508 OPENDDS_STRING File::name() const
00509 {
00510   return ACE_TEXT_ALWAYS_CHAR(logical_relative_.c_str());
00511 }
00512 
00513 // Directory
00514 
00515 /*static*/ Directory::Ptr Directory::create(const char* dirname)
00516 {
00517   return new Directory(ACE_TEXT_CHAR_TO_TCHAR(dirname), ACE_TEXT(""), 0);
00518 }
00519 
00520 ACE_TString Directory::full_path(const ACE_TString& relative) const
00521 {
00522   return physical_dirname_ + relative;
00523 }
00524 
00525 Directory::FileIterator Directory::begin_files()
00526 {
00527   return FileIterator(files_.begin(), this);
00528 }
00529 
00530 Directory::FileIterator Directory::end_files()
00531 {
00532   return FileIterator(files_.end(), this);
00533 }
00534 
00535 File::Ptr Directory::get_file(const char* name)
00536 {
00537   if (std::strlen(name) >= FSS_MAX_FILE_NAME) {
00538     throw std::runtime_error("file name too long");
00539   }
00540 
00541   ACE_TString t_name(ACE_TEXT_CHAR_TO_TCHAR(name));
00542   Map::iterator it = files_.find(t_name);
00543 
00544   if (it == files_.end()) {
00545     return make_new_file(t_name);
00546 
00547   } else {
00548     return new File(full_path(it->second), it->first, this);
00549   }
00550 }
00551 
00552 File::Ptr Directory::make_new_file(const ACE_TString& t_name)
00553 {
00554   if (dirs_.find(t_name) != dirs_.end()) {
00555     throw std::runtime_error("Can't create a file with the same name as "
00556                              "an existing directory.");
00557   }
00558 
00559   ACE_TString phys = add_entry() + b32h_encode(t_name.c_str());
00560   files_[t_name] = phys;
00561 
00562   CwdGuard cg(physical_dirname_);
00563   // touch the file since the user has asked to create it
00564   std::FILE* fh = std::fopen(ACE_TEXT_ALWAYS_CHAR(phys.c_str()), "w");
00565 
00566   if (!fh) throw std::runtime_error("Can't create the file");
00567 
00568   std::fclose(fh);
00569   return new File(physical_dirname_ + phys, t_name, this);
00570 }
00571 
00572 File::Ptr Directory::create_next_file()
00573 {
00574   ACE_TString logical;
00575 
00576   if (files_.empty()) {
00577     logical = FSS_DEFAULT_FILE_NAME;
00578 
00579   } else {
00580     Map::iterator last = --files_.end();
00581     logical = last->first;
00582 
00583     if (!increment(logical)) {
00584       throw std::runtime_error("out of range for create_next_file");
00585     }
00586   }
00587 
00588   return make_new_file(logical);
00589 }
00590 
00591 Directory::DirectoryIterator Directory::begin_dirs()
00592 {
00593   return DirectoryIterator(dirs_.begin(), this);
00594 }
00595 
00596 Directory::DirectoryIterator Directory::end_dirs()
00597 {
00598   return DirectoryIterator(dirs_.end(), this);
00599 }
00600 
00601 Directory::Ptr Directory::get_dir(const OPENDDS_VECTOR(OPENDDS_STRING)& path)
00602 {
00603   Directory::Ptr dir(this, false);
00604   typedef OPENDDS_VECTOR(OPENDDS_STRING)::const_iterator iterator;
00605 
00606   for (iterator iter = path.begin(), end = path.end(); iter != end; ++iter) {
00607     dir = dir->get_subdir(iter->c_str());
00608   }
00609 
00610   return dir;
00611 }
00612 
00613 Directory::Ptr Directory::get_subdir(const char* name)
00614 {
00615   ACE_TString t_name = ACE_TEXT_CHAR_TO_TCHAR(name);
00616   Map::iterator it = dirs_.find(t_name);
00617 
00618   if (it == dirs_.end()) {
00619     return make_new_subdir(t_name);
00620 
00621   } else {
00622     return new Directory(full_path(it->second), it->first, this);
00623   }
00624 }
00625 
00626 Directory::Ptr Directory::create_next_dir()
00627 {
00628   ACE_TString logical;
00629 
00630   if (dirs_.empty()) {
00631     logical = FSS_DEFAULT_DIR_NAME;
00632 
00633   } else {
00634     Map::iterator last = --dirs_.end();
00635     logical = last->first;
00636 
00637     if (!increment(logical)) {
00638       throw std::runtime_error("out of range for create_next_dir");
00639     }
00640   }
00641 
00642   return make_new_subdir(logical);
00643 }
00644 
00645 Directory::Ptr Directory::make_new_subdir(const ACE_TString& t_name)
00646 {
00647   if (files_.find(t_name) != files_.end()) {
00648     throw std::runtime_error("Can't create a directory with the same "
00649                              "name as an existing file.");
00650   }
00651 
00652   ACE_TString logical(t_name.c_str(),
00653                       (std::min)(FSS_MAX_FILE_NAME, t_name.length()));
00654   ACE_TString phys_prefix = add_entry();
00655   ACE_TString phys_base = b32h_encode(logical.c_str());
00656 
00657   if (t_name.length() >= FSS_MAX_FILE_NAME) {
00658     unsigned int& counter = long_names_[phys_prefix + phys_base];
00659 
00660     if (counter == 99999) {
00661       throw std::runtime_error("Long directory name out of range");
00662     }
00663 
00664     phys_base += ACE_TEXT(".     X"); // snprintf will clobber the X with a 0
00665     ACE_TCHAR* buf = &phys_base[0] + phys_base.length() - 6;
00666     ACE_OS::snprintf(buf, 6, ACE_TEXT("%05u"), counter++);
00667     phys_base = phys_base.substr(0, phys_base.length() - 1); // trim the 0
00668   }
00669 
00670   ACE_TString phys = phys_prefix + phys_base;
00671   dirs_[t_name] = phys;
00672   {
00673     CwdGuard cg(physical_dirname_);
00674 
00675     if (dds_mkdir(phys.c_str()) == -1) {
00676       throw std::runtime_error("Can't create directory");
00677     }
00678 
00679     if ((phys_prefix.length() > 0 && dds_chdir(phys_prefix.c_str()) == -1)
00680         || dds_chdir(phys_base.c_str()) == -1) {
00681       dds_rmdir(phys.c_str());
00682       throw std::runtime_error("Can't change to newly created directory");
00683     }
00684 
00685     std::ofstream fn("_fullname");
00686     fn << t_name << '\n';
00687   }
00688   return new Directory(physical_dirname_ + phys, t_name, this);
00689 }
00690 
00691 ACE_TString Directory::add_entry()
00692 {
00693   if (overflow_.empty()) {
00694     overflow_[0] = 1;
00695     return ACE_TEXT("");
00696   }
00697 
00698   typedef OPENDDS_MAP(unsigned int, unsigned int)::iterator iterator;
00699   // find existing overflow bucket with capacity
00700   bool found_gap(false);
00701   unsigned int last_seen(0), unused_bucket(0);
00702 
00703   for (iterator iter = overflow_.begin(), end = overflow_.end();
00704        iter != end; ++iter) {
00705     if (iter->second < OPENDDS_FILESYSTEMSTORAGE_MAX_FILES_PER_DIR) {
00706       ++iter->second;
00707 
00708       if (iter->first == 0) return ACE_TEXT("");
00709 
00710       return overflow_dir_name(iter->first);
00711     }
00712 
00713     if (!found_gap && iter->first > last_seen + 1) {
00714       found_gap = true;
00715       unused_bucket = last_seen + 1;
00716     }
00717 
00718     last_seen = iter->first;
00719   }
00720 
00721   if (!found_gap) {
00722     if (last_seen == FSS_MAX_OVERFLOW_DIR) {
00723       throw std::runtime_error("Overflow serial # out of range.");
00724     }
00725 
00726     unused_bucket = last_seen + 1;
00727   }
00728 
00729   overflow_[unused_bucket] = 1;
00730   ACE_TString dir_name = overflow_dir_name(unused_bucket);
00731   CwdGuard cg(physical_dirname_);
00732 
00733   if (dds_mkdir(dir_name.c_str()) == -1) {
00734     throw std::runtime_error("Can't create overflow directory");
00735   }
00736 
00737   return dir_name;
00738 }
00739 
00740 void Directory::remove()
00741 {
00742   if (!parent_.is_nil()) parent_->removing(logical_dirname_, false);
00743 
00744   parent_ = 0;
00745   recursive_remove(physical_dirname_);
00746   overflow_.clear();
00747   files_.clear();
00748   dirs_.clear();
00749   long_names_.clear();
00750 }
00751 
00752 OPENDDS_STRING Directory::name() const
00753 {
00754   return ACE_TEXT_ALWAYS_CHAR(logical_dirname_.c_str());
00755 }
00756 
00757 Directory::Directory(const ACE_TString& dirname, const ACE_TString& logical,
00758                      Directory* parent)
00759   : parent_(parent, false)
00760   , physical_dirname_(dirname)
00761   , logical_dirname_(logical)
00762 {
00763   add_slash(physical_dirname_);
00764 
00765   bool ok(true);
00766   DDS_Dirent dir;
00767 
00768   if (dir.open(physical_dirname_.c_str()) == -1) {
00769     ok = false;
00770 
00771     if (errno == ENOENT && dds_mkdir(physical_dirname_.c_str()) != -1
00772         && dir.open(physical_dirname_.c_str()) != -1) {
00773       ok = true;
00774     }
00775   }
00776 
00777   if (!ok) throw std::runtime_error("Can't open or create directory");
00778 
00779   scan_dir(ACE_TEXT(""), dir, 0);
00780 }
00781 
00782 void Directory::scan_dir(const ACE_TString& relative, DDS_Dirent& dir,
00783                          unsigned int overflow_index)
00784 {
00785   ACE_TString path = physical_dirname_ + relative;
00786   add_slash(path);
00787 
00788   while (DDS_DIRENT* ent = dir.read()) {
00789     if (ent->d_name[0] == ACE_TEXT('.') && (!ent->d_name[1] ||
00790         (ent->d_name[1] == ACE_TEXT('.') && !ent->d_name[2]))) {
00791       continue; // skip '.' and '..'
00792     }
00793 
00794     ACE_TString file = path + ent->d_name;
00795 
00796     if (is_dir(file.c_str())) {
00797       ACE_TString phys(relative);
00798       add_slash(phys);
00799       phys += ent->d_name;
00800 
00801       if (ACE_OS::strncmp(ent->d_name, ACE_TEXT("_overflow."), 10) == 0) {
00802         unsigned int n = ACE_OS::atoi(ent->d_name + 10);
00803         DDS_Dirent overflow(file.c_str());
00804         scan_dir(ent->d_name, overflow, n);
00805 
00806       } else if (ACE_OS::strlen(ent->d_name) <= FSS_MAX_FILE_NAME_ENCODED) {
00807         dirs_[b32h_decode(ent->d_name)] = phys;
00808         ++overflow_[overflow_index];
00809 
00810       } else {
00811         CwdGuard cg(file);
00812         std::ifstream fn("_fullname");
00813         OPENDDS_STRING fullname;
00814 
00815         if (!std::getline(fn, fullname)) {
00816           throw std::runtime_error("Can't read .../_fullname");
00817         }
00818 
00819         ACE_TString full_t(ACE_TEXT_CHAR_TO_TCHAR(fullname.c_str()));
00820         dirs_[full_t] = phys;
00821         ++overflow_[overflow_index];
00822 
00823         String_Index_t idx = phys.rfind(ACE_TEXT('.'));
00824 
00825         if (idx == ACE_TString::npos) {
00826           throw std::runtime_error("Badly formatted long dir name");
00827         }
00828 
00829         ACE_TString prefix(phys.c_str(), idx);
00830         unsigned int serial = ACE_OS::atoi(&phys[idx + 1]);
00831         unsigned int& counter = long_names_[prefix];
00832 
00833         if (serial >= counter) counter = serial + 1;
00834       }
00835 
00836     } else { // regular file
00837       if (ent->d_name[0] != ACE_TEXT('_')) {
00838         files_[b32h_decode(ent->d_name)] = ent->d_name;
00839         ++overflow_[overflow_index];
00840       }
00841     }
00842   }
00843 }
00844 
00845 void Directory::removing(const ACE_TString& child, bool file)
00846 {
00847   Map& m = file ? files_ : dirs_;
00848   Map::iterator iter = m.find(child);
00849 
00850   if (iter == m.end()) return;
00851 
00852   const ACE_TString& phys = iter->second;
00853   String_Index_t idx = phys.find(ACE_TEXT("_overflow."));
00854   unsigned int bucket = (idx == 0 ? ACE_OS::atoi(&phys[idx + 10]) : 0);
00855 
00856   if (--overflow_[bucket] == 0 && bucket > 0) {
00857     overflow_.erase(bucket);
00858     idx = phys.find(ACE_TEXT('/'));
00859     ACE_TString ov_dir = physical_dirname_ + ACE_TString(phys.c_str(), idx);
00860     dds_rmdir(ov_dir.c_str());
00861   }
00862 
00863   m.erase(iter);
00864 }
00865 
00866 // Base32Hex
00867 
00868 ACE_TString b32h_encode(const ACE_TCHAR* decoded)
00869 {
00870   static const ACE_TCHAR lookup[] =
00871     ACE_TEXT("0123456789ABCDEFGHIJKLMNOPQRSTUV");
00872   static const ACE_TCHAR padding[] = ACE_TEXT("======");
00873   static const size_t enc[] = {0, 2, 4, 5, 7}; // #input -> #non-padded output
00874   ACE_TString encoded;
00875 
00876   for (size_t len = ACE_OS::strlen(decoded); *decoded; decoded += 5, len -= 5) {
00877     ACE_UINT64 chunk = 0;
00878 
00879     for (size_t i(0); i < 5 && i < len; ++i) {
00880       chunk |= static_cast<ACE_UINT64>(decoded[i] & 0xFF) << ((4 - i) * 8);
00881     }
00882 
00883     size_t limit = (len < 5) ? enc[len] : 8;
00884 
00885     for (size_t i(0); i < limit; ++i) {
00886       unsigned char val =
00887         static_cast<unsigned char>(chunk >>((7 - i) * 5)) & 0x1F;
00888       encoded += lookup[val];
00889     }
00890 
00891     if (len < 5) {
00892       encoded.append(padding, 8 - enc[len]);
00893       return encoded;
00894     }
00895   }
00896 
00897   return encoded;
00898 }
00899 
00900 ACE_TString b32h_decode(const ACE_TCHAR* encoded)
00901 {
00902   // #before first '=' -> #output
00903   static const size_t dec[] = {0, 0, 1, 0, 2, 3, 0, 4};
00904   ACE_TString decoded;
00905 
00906   for (; *encoded; encoded += 8) {
00907     ACE_UINT64 chunk = 0;
00908     size_t i = 0;
00909 
00910     for (; i < 8 && encoded[i] != ACE_TEXT('='); ++i) {
00911       char idx = (encoded[i] <= ACE_TEXT('9'))
00912                  ? (encoded[i] - ACE_TEXT('0'))
00913                  : (10 + encoded[i] - ACE_TEXT('A'));
00914       chunk |= static_cast<ACE_UINT64>(idx) << ((7 - i) * 5);
00915     }
00916 
00917     size_t limit = (encoded[i] == ACE_TEXT('=')) ? dec[i] : 5;
00918 
00919     for (size_t j(0); j < limit; ++j) {
00920       decoded += static_cast<ACE_TCHAR>(chunk >>((4 - j) * 8)) & 0xFF;
00921     }
00922   }
00923 
00924   return decoded;
00925 }
00926 
00927 } // namespace FileSystemStorage
00928 } // namespace OpenDDS
00929 
00930 #endif

Generated on Fri Feb 12 20:05:23 2016 for OpenDDS by  doxygen 1.4.7