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   const size_t buf_size=32;
00179   ACE_TCHAR of_name[buf_size];
00180   ACE_OS::snprintf(of_name, buf_size, ACE_TEXT("_overflow.%04u/"), n);
00181   return of_name;
00182 }
00183 
00184 struct CwdGuard {
00185   ACE_TString cwd_;
00186 
00187   explicit CwdGuard(const ACE_TString& dir) {
00188     ACE_Vector<ACE_TCHAR> cwd_buf(128);
00189     cwd_buf.resize(128, ACE_TCHAR(0));
00190 
00191     while (ACE_OS::getcwd(&cwd_buf[0], cwd_buf.size()) == 0) {
00192       if (errno == ERANGE) {
00193         cwd_buf.resize(cwd_buf.size() * 2, ACE_TCHAR(0));
00194 
00195       } else break;
00196     }
00197 
00198     if (cwd_buf[0]) cwd_ = &cwd_buf[0];
00199 
00200     dds_chdir(dir.c_str());
00201   }
00202 
00203   ~CwdGuard() {
00204     dds_chdir(cwd_.c_str());
00205   }
00206 };
00207 
00208 void recursive_remove(const ACE_TString& dirname)
00209 {
00210   using namespace OpenDDS::FileSystemStorage;
00211   DDS_Dirent dir(dirname.c_str());
00212   {
00213     CwdGuard cg(dirname);
00214 
00215     for (DDS_DIRENT* ent = dir.read(); ent; ent = dir.read()) {
00216       if (ent->d_name[0] == ACE_TEXT('.') && (!ent->d_name[1] ||
00217           (ent->d_name[1] == ACE_TEXT('.') && !ent->d_name[2]))) {
00218         continue; // skip '.' and '..'
00219       }
00220 
00221       if (is_dir(ent->d_name)) {
00222         recursive_remove(ent->d_name);
00223 
00224       } else { // regular file
00225         ACE_OS::unlink(ent->d_name);
00226       }
00227     }
00228   }
00229   dds_rmdir(dirname.c_str());
00230 }
00231 
00232 } // namespace
00233 
00234 OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
00235 
00236 namespace OpenDDS {
00237 namespace FileSystemStorage {
00238 
00239 #ifdef ACE_WIN32
00240 
00241 // DDS_DIR
00242 
00243 struct DDS_DIR {
00244   /// The name of the directory we are looking into
00245   wchar_t* directory_name_;
00246 
00247   /// Remember the handle between calls.
00248   HANDLE current_handle_;
00249 
00250   /// The struct for the results
00251   ACE_DIRENT* dirent_;
00252 
00253   /// The struct for intermediate results.
00254   WIN32_FIND_DATAW fdata_;
00255 
00256   /// A flag to remember if we started reading already.
00257   int started_reading_;
00258 };
00259 
00260 // DDS_Dirent
00261 
00262 DDS_Dirent::DDS_Dirent(const ACE_TCHAR* path)
00263   : dirp_(0)
00264 {
00265   if (path) open(path);
00266 }
00267 
00268 int DDS_Dirent::open(const ACE_TCHAR* path)
00269 {
00270   close();
00271   ACE_WString wpath =
00272 #ifdef ACE_HAS_WINCE
00273     path;
00274 #else
00275     to_win32_long_path(path);
00276 #endif
00277 
00278   // taken from ACE_OS::opendir_emulation() and translated to wchar
00279 
00280   wchar_t extra[3] = {0, 0, 0};
00281   const wchar_t* filename = wpath.c_str();
00282 
00283   // Check if filename is a directory.
00284   DWORD fileAttribute = ::GetFileAttributesW(filename);
00285 
00286   if (fileAttribute == INVALID_FILE_ATTRIBUTES) {
00287     ACE_OS::set_errno_to_last_error();
00288     return -1;
00289   }
00290 
00291   if (!(fileAttribute & FILE_ATTRIBUTE_DIRECTORY)) {
00292     errno = ENOTDIR;
00293     return -1;
00294   }
00295 
00296   size_t const lastchar = ACE_OS::strlen(filename);
00297 
00298   if (lastchar > 0) {
00299     if (filename[lastchar-1] != L'*') {
00300       if (filename[lastchar-1] != L'/' && filename[lastchar-1] != L'\\')
00301         ACE_OS::strcpy(extra, L"\\*");
00302 
00303       else
00304         ACE_OS::strcpy(extra, L"*");
00305     }
00306   }
00307 
00308   ACE_NEW_RETURN(dirp_, DDS_DIR, -1);
00309   ACE_NEW_RETURN(dirp_->directory_name_,
00310                  wchar_t[lastchar + ACE_OS::strlen(extra) + 1],
00311                  -1);
00312   ACE_OS::strcpy(dirp_->directory_name_, filename);
00313 
00314   if (extra[0])
00315     ACE_OS::strcat(dirp_->directory_name_, extra);
00316 
00317   dirp_->current_handle_ = INVALID_HANDLE_VALUE;
00318   dirp_->started_reading_ = 0;
00319   dirp_->dirent_ = 0;
00320   return 0;
00321 }
00322 
00323 DDS_DIRENT* DDS_Dirent::read()
00324 {
00325   if (!dirp_) return 0;
00326 
00327   // taken from ACE_OS::readdir_emulation() and translated to wchar
00328   //   note that the file name inside ACE_DIRENT is still in ACE_TCHARs as long
00329   //   as ACE_HAS_TCHAR_DIRENT is defined (true for MSVC, false for GCC/MinGW)
00330 
00331   if (dirp_->dirent_ != 0) {
00332 #ifdef ACE_HAS_TCHAR_DIRENT
00333     ACE_OS::free(dirp_->dirent_->d_name);
00334 #endif
00335     ACE_OS::free(dirp_->dirent_);
00336     dirp_->dirent_ = 0;
00337   }
00338 
00339   if (!dirp_->started_reading_) {
00340     dirp_->current_handle_ = ::FindFirstFileW(dirp_->directory_name_,
00341                                               &dirp_->fdata_);
00342     dirp_->started_reading_ = 1;
00343 
00344   } else {
00345     int retval = ::FindNextFileW(dirp_->current_handle_, &dirp_->fdata_);
00346 
00347     if (retval == 0) {
00348       // Make sure to close the handle explicitly to avoid a leak!
00349       ::FindClose(dirp_->current_handle_);
00350       dirp_->current_handle_ = INVALID_HANDLE_VALUE;
00351     }
00352   }
00353 
00354   if (dirp_->current_handle_ != INVALID_HANDLE_VALUE) {
00355     dirp_->dirent_ = (DDS_DIRENT*) ACE_Allocator::instance()->malloc(sizeof(DDS_DIRENT));
00356 
00357     if (dirp_->dirent_ != 0) {
00358 #ifdef ACE_HAS_TCHAR_DIRENT
00359       dirp_->dirent_->d_name =
00360         (ACE_TCHAR*) ACE_Allocator::instance()->malloc((ACE_OS::strlen(dirp_->fdata_.cFileName)
00361                                      + 1) * sizeof(ACE_TCHAR));
00362       ACE_OS::strcpy(dirp_->dirent_->d_name,
00363                      ACE_TEXT_WCHAR_TO_TCHAR(dirp_->fdata_.cFileName));
00364 #else // MinGW: d_name is a fixed-size char array
00365       ACE_OS::strncpy(dirp_->dirent_->d_name,
00366                       ACE_Wide_To_Ascii(dirp_->fdata_.cFileName).char_rep(),
00367                       sizeof(dirp_->dirent_->d_name));
00368 #endif
00369       dirp_->dirent_->d_reclen = sizeof(DDS_DIRENT);
00370     }
00371 
00372     return dirp_->dirent_;
00373 
00374   } else
00375     return 0;
00376 }
00377 
00378 void DDS_Dirent::close()
00379 {
00380   if (dirp_) {
00381     if (dirp_->current_handle_ != INVALID_HANDLE_VALUE)
00382       ::FindClose(dirp_->current_handle_);
00383 
00384     dirp_->current_handle_ = INVALID_HANDLE_VALUE;
00385     dirp_->started_reading_ = 0;
00386 
00387     if (dirp_->dirent_ != 0) {
00388 #ifdef ACE_HAS_TCHAR_DIRENT
00389       ACE_OS::free(dirp_->dirent_->d_name);
00390 #endif
00391       ACE_OS::free(dirp_->dirent_);
00392     }
00393 
00394     dirp_ = 0;
00395   }
00396 }
00397 
00398 DDS_Dirent::~DDS_Dirent()
00399 {
00400   close();
00401 }
00402 
00403 #elif defined ACE_USES_WCHAR // non-Win32 uses-WChar
00404 
00405 struct DDS_DIR {
00406   ACE_DIR* real_dir_;
00407   DDS_DIRENT ent_;
00408 
00409   DDS_DIR() : real_dir_(), ent_() {}
00410 };
00411 
00412 DDS_Dirent::DDS_Dirent(const ACE_TCHAR* path)
00413   : dirp_(new DDS_DIR)
00414 {
00415   if (path) open(path);
00416 }
00417 
00418 int DDS_Dirent::open(const ACE_TCHAR* path)
00419 {
00420   close();
00421   return (dirp_->real_dir_ = ACE_OS::opendir(path)) == 0 ? -1 : 0;
00422 }
00423 
00424 DDS_DIRENT* DDS_Dirent::read()
00425 {
00426   if (!dirp_->real_dir_) return 0;
00427 
00428   dirp_->ent_.real_dirent_ = ACE_OS::readdir(dirp_->real_dir_);
00429 
00430   if (!dirp_->ent_.real_dirent_) return 0;
00431 
00432   ACE_OS::free(dirp_->ent_.d_name);
00433   dirp_->ent_.d_name =
00434     ACE_OS::strdup(ACE_TEXT_CHAR_TO_TCHAR(dirp_->ent_.real_dirent_->d_name));
00435   return &dirp_->ent_;
00436 }
00437 
00438 void DDS_Dirent::close()
00439 {
00440   if (dirp_->real_dir_) {
00441     ACE_OS::closedir(dirp_->real_dir_);
00442     ACE_OS::free(dirp_->ent_.d_name);
00443   }
00444 
00445   dirp_->real_dir_ = 0;
00446 }
00447 
00448 DDS_Dirent::~DDS_Dirent()
00449 {
00450   close();
00451   delete dirp_;
00452 }
00453 
00454 #endif // ACE_WIN32
00455 
00456 // File
00457 
00458 File::File(const ACE_TString& fname_phys, const ACE_TString& logical,
00459            const Directory::Ptr& parent)
00460   : physical_file_()
00461   , physical_dir_()
00462   , logical_relative_(logical)
00463   , parent_(parent)
00464 {
00465   String_Index_t last_slash = fname_phys.rfind(ACE_TEXT('/'));
00466 
00467   if (last_slash == ACE_TString::npos) {
00468     physical_file_ = fname_phys;
00469     physical_dir_ = ACE_TEXT(".");
00470 
00471   } else {
00472     physical_file_ = fname_phys.c_str() + last_slash + 1;
00473     physical_dir_.set(fname_phys.c_str(), last_slash, true);
00474   }
00475 }
00476 
00477 bool File::write(std::ofstream& stream)
00478 {
00479   CwdGuard cg(physical_dir_);
00480   stream.open(ACE_TEXT_ALWAYS_CHAR(physical_file_.c_str()),
00481               ios::binary | ios::out);
00482   return !stream.bad() && !stream.fail();
00483 }
00484 
00485 bool File::read(std::ifstream& stream)
00486 {
00487   CwdGuard cg(physical_dir_);
00488   stream.open(ACE_TEXT_ALWAYS_CHAR(physical_file_.c_str()),
00489               ios::binary | ios::in);
00490   return !stream.bad() && !stream.fail();
00491 }
00492 
00493 bool File::remove()
00494 {
00495   int unlink_result = -1;
00496   {
00497     CwdGuard cg(physical_dir_);
00498     unlink_result = ACE_OS::unlink(physical_file_.c_str());
00499   }
00500 
00501   if (unlink_result != -1) {
00502     parent_->removing(logical_relative_, true);
00503     return true;
00504   }
00505 
00506   return false;
00507 }
00508 
00509 OPENDDS_STRING File::name() const
00510 {
00511   return ACE_TEXT_ALWAYS_CHAR(logical_relative_.c_str());
00512 }
00513 
00514 // Directory
00515 
00516 /*static*/ Directory::Ptr Directory::create(const char* dirname)
00517 {
00518   return DCPS::make_rch<Directory>(ACE_TEXT_CHAR_TO_TCHAR(dirname), ACE_TEXT(""), Directory::Ptr ());
00519 }
00520 
00521 ACE_TString Directory::full_path(const ACE_TString& relative) const
00522 {
00523   return physical_dirname_ + relative;
00524 }
00525 
00526 Directory::FileIterator Directory::begin_files()
00527 {
00528   return FileIterator(files_.begin(), rchandle_from(this));
00529 }
00530 
00531 Directory::FileIterator Directory::end_files()
00532 {
00533   return FileIterator(files_.end(),  rchandle_from(this));
00534 }
00535 
00536 File::Ptr Directory::get_file(const char* name)
00537 {
00538   if (std::strlen(name) >= FSS_MAX_FILE_NAME) {
00539     throw std::runtime_error("file name too long");
00540   }
00541 
00542   ACE_TString t_name(ACE_TEXT_CHAR_TO_TCHAR(name));
00543   Map::iterator it = files_.find(t_name);
00544 
00545   if (it == files_.end()) {
00546     return make_new_file(t_name);
00547 
00548   } else {
00549     return DCPS::make_rch<File>(full_path(it->second), it->first, rchandle_from(this));
00550   }
00551 }
00552 
00553 File::Ptr Directory::make_new_file(const ACE_TString& t_name)
00554 {
00555   if (dirs_.find(t_name) != dirs_.end()) {
00556     throw std::runtime_error("Can't create a file with the same name as "
00557                              "an existing directory.");
00558   }
00559 
00560   ACE_TString phys = add_entry() + b32h_encode(t_name.c_str());
00561   files_[t_name] = phys;
00562 
00563   CwdGuard cg(physical_dirname_);
00564   // touch the file since the user has asked to create it
00565   std::FILE* fh = std::fopen(ACE_TEXT_ALWAYS_CHAR(phys.c_str()), "w");
00566 
00567   if (!fh) throw std::runtime_error("Can't create the file");
00568 
00569   std::fclose(fh);
00570   return DCPS::make_rch<File>(physical_dirname_ + phys, t_name,  rchandle_from(this));
00571 }
00572 
00573 File::Ptr Directory::create_next_file()
00574 {
00575   ACE_TString logical;
00576 
00577   if (files_.empty()) {
00578     logical = FSS_DEFAULT_FILE_NAME;
00579 
00580   } else {
00581     Map::iterator last = --files_.end();
00582     logical = last->first;
00583 
00584     if (!increment(logical)) {
00585       throw std::runtime_error("out of range for create_next_file");
00586     }
00587   }
00588 
00589   return make_new_file(logical);
00590 }
00591 
00592 Directory::DirectoryIterator Directory::begin_dirs()
00593 {
00594   return DirectoryIterator(dirs_.begin(),  rchandle_from(this));
00595 }
00596 
00597 Directory::DirectoryIterator Directory::end_dirs()
00598 {
00599   return DirectoryIterator(dirs_.end(),  rchandle_from(this));
00600 }
00601 
00602 Directory::Ptr Directory::get_dir(const OPENDDS_VECTOR(OPENDDS_STRING)& path)
00603 {
00604   Directory::Ptr dir = rchandle_from(this);
00605   typedef OPENDDS_VECTOR(OPENDDS_STRING)::const_iterator iterator;
00606 
00607   for (iterator iter = path.begin(), end = path.end(); iter != end; ++iter) {
00608     dir = dir->get_subdir(iter->c_str());
00609   }
00610 
00611   return dir;
00612 }
00613 
00614 Directory::Ptr Directory::get_subdir(const char* name)
00615 {
00616   ACE_TString t_name = ACE_TEXT_CHAR_TO_TCHAR(name);
00617   Map::iterator it = dirs_.find(t_name);
00618 
00619   if (it == dirs_.end()) {
00620     return make_new_subdir(t_name);
00621 
00622   } else {
00623     return DCPS::make_rch<Directory>(full_path(it->second), it->first,  rchandle_from(this));
00624   }
00625 }
00626 
00627 Directory::Ptr Directory::create_next_dir()
00628 {
00629   ACE_TString logical;
00630 
00631   if (dirs_.empty()) {
00632     logical = FSS_DEFAULT_DIR_NAME;
00633 
00634   } else {
00635     Map::iterator last = --dirs_.end();
00636     logical = last->first;
00637 
00638     if (!increment(logical)) {
00639       throw std::runtime_error("out of range for create_next_dir");
00640     }
00641   }
00642 
00643   return make_new_subdir(logical);
00644 }
00645 
00646 Directory::Ptr Directory::make_new_subdir(const ACE_TString& t_name)
00647 {
00648   if (files_.find(t_name) != files_.end()) {
00649     throw std::runtime_error("Can't create a directory with the same "
00650                              "name as an existing file.");
00651   }
00652 
00653   ACE_TString logical(t_name.c_str(),
00654                       (std::min)(FSS_MAX_FILE_NAME, t_name.length()));
00655   ACE_TString phys_prefix = add_entry();
00656   ACE_TString phys_base = b32h_encode(logical.c_str());
00657 
00658   if (t_name.length() >= FSS_MAX_FILE_NAME) {
00659     unsigned int& counter = long_names_[phys_prefix + phys_base];
00660 
00661     if (counter == 99999) {
00662       throw std::runtime_error("Long directory name out of range");
00663     }
00664 
00665     phys_base += ACE_TEXT(".     X"); // snprintf will clobber the X with a 0
00666     ACE_TCHAR* buf = &phys_base[0] + phys_base.length() - 6;
00667     ACE_OS::snprintf(buf, 6, ACE_TEXT("%05u"), counter++);
00668     phys_base = phys_base.substr(0, phys_base.length() - 1); // trim the 0
00669   }
00670 
00671   ACE_TString phys = phys_prefix + phys_base;
00672   dirs_[t_name] = phys;
00673   {
00674     CwdGuard cg(physical_dirname_);
00675 
00676     if (dds_mkdir(phys.c_str()) == -1) {
00677       throw std::runtime_error("Can't create directory");
00678     }
00679 
00680     if ((phys_prefix.length() > 0 && dds_chdir(phys_prefix.c_str()) == -1)
00681         || dds_chdir(phys_base.c_str()) == -1) {
00682       dds_rmdir(phys.c_str());
00683       throw std::runtime_error("Can't change to newly created directory");
00684     }
00685 
00686     std::ofstream fn("_fullname");
00687     fn << t_name << '\n';
00688   }
00689   return DCPS::make_rch<Directory>(physical_dirname_ + phys, t_name,  rchandle_from(this));
00690 }
00691 
00692 ACE_TString Directory::add_entry()
00693 {
00694   if (overflow_.empty()) {
00695     overflow_[0] = 1;
00696     return ACE_TEXT("");
00697   }
00698 
00699   typedef OPENDDS_MAP(unsigned int, unsigned int)::iterator iterator;
00700   // find existing overflow bucket with capacity
00701   bool found_gap(false);
00702   unsigned int last_seen(0), unused_bucket(0);
00703 
00704   for (iterator iter = overflow_.begin(), end = overflow_.end();
00705        iter != end; ++iter) {
00706     if (iter->second < OPENDDS_FILESYSTEMSTORAGE_MAX_FILES_PER_DIR) {
00707       ++iter->second;
00708 
00709       if (iter->first == 0) return ACE_TEXT("");
00710 
00711       return overflow_dir_name(iter->first);
00712     }
00713 
00714     if (!found_gap && iter->first > last_seen + 1) {
00715       found_gap = true;
00716       unused_bucket = last_seen + 1;
00717     }
00718 
00719     last_seen = iter->first;
00720   }
00721 
00722   if (!found_gap) {
00723     if (last_seen == FSS_MAX_OVERFLOW_DIR) {
00724       throw std::runtime_error("Overflow serial # out of range.");
00725     }
00726 
00727     unused_bucket = last_seen + 1;
00728   }
00729 
00730   overflow_[unused_bucket] = 1;
00731   ACE_TString dir_name = overflow_dir_name(unused_bucket);
00732   CwdGuard cg(physical_dirname_);
00733 
00734   if (dds_mkdir(dir_name.c_str()) == -1) {
00735     throw std::runtime_error("Can't create overflow directory");
00736   }
00737 
00738   return dir_name;
00739 }
00740 
00741 void Directory::remove()
00742 {
00743   if (!parent_.is_nil()) parent_->removing(logical_dirname_, false);
00744 
00745   parent_.reset();
00746   recursive_remove(physical_dirname_);
00747   overflow_.clear();
00748   files_.clear();
00749   dirs_.clear();
00750   long_names_.clear();
00751 }
00752 
00753 OPENDDS_STRING Directory::name() const
00754 {
00755   return ACE_TEXT_ALWAYS_CHAR(logical_dirname_.c_str());
00756 }
00757 
00758 Directory::Directory(const ACE_TString& dirname, const ACE_TString& logical,
00759                      const Directory::Ptr& parent)
00760   : parent_(parent)
00761   , physical_dirname_(dirname)
00762   , logical_dirname_(logical)
00763 {
00764   add_slash(physical_dirname_);
00765 
00766   bool ok(true);
00767   DDS_Dirent dir;
00768 
00769   if (dir.open(physical_dirname_.c_str()) == -1) {
00770     ok = false;
00771 
00772     if (errno == ENOENT && dds_mkdir(physical_dirname_.c_str()) != -1
00773         && dir.open(physical_dirname_.c_str()) != -1) {
00774       ok = true;
00775     }
00776   }
00777 
00778   if (!ok) throw std::runtime_error("Can't open or create directory");
00779 
00780   scan_dir(ACE_TEXT(""), dir, 0);
00781 }
00782 
00783 void Directory::scan_dir(const ACE_TString& relative, DDS_Dirent& dir,
00784                          unsigned int overflow_index)
00785 {
00786   ACE_TString path = physical_dirname_ + relative;
00787   add_slash(path);
00788 
00789   while (DDS_DIRENT* ent = dir.read()) {
00790     if (ent->d_name[0] == ACE_TEXT('.') && (!ent->d_name[1] ||
00791         (ent->d_name[1] == ACE_TEXT('.') && !ent->d_name[2]))) {
00792       continue; // skip '.' and '..'
00793     }
00794 
00795     ACE_TString file = path + ent->d_name;
00796 
00797     if (is_dir(file.c_str())) {
00798       ACE_TString phys(relative);
00799       add_slash(phys);
00800       phys += ent->d_name;
00801 
00802       if (ACE_OS::strncmp(ent->d_name, ACE_TEXT("_overflow."), 10) == 0) {
00803         unsigned int n = ACE_OS::atoi(ent->d_name + 10);
00804         DDS_Dirent overflow(file.c_str());
00805         scan_dir(ent->d_name, overflow, n);
00806 
00807       } else if (ACE_OS::strlen(ent->d_name) <= FSS_MAX_FILE_NAME_ENCODED) {
00808         dirs_[b32h_decode(ent->d_name)] = phys;
00809         ++overflow_[overflow_index];
00810 
00811       } else {
00812         CwdGuard cg(file);
00813         std::ifstream fn("_fullname");
00814         OPENDDS_STRING fullname;
00815 
00816         if (!std::getline(fn, fullname)) {
00817           throw std::runtime_error("Can't read .../_fullname");
00818         }
00819 
00820         ACE_TString full_t(ACE_TEXT_CHAR_TO_TCHAR(fullname.c_str()));
00821         dirs_[full_t] = phys;
00822         ++overflow_[overflow_index];
00823 
00824         String_Index_t idx = phys.rfind(ACE_TEXT('.'));
00825 
00826         if (idx == ACE_TString::npos) {
00827           throw std::runtime_error("Badly formatted long dir name");
00828         }
00829 
00830         ACE_TString prefix(phys.c_str(), idx);
00831         unsigned int serial = ACE_OS::atoi(&phys[idx + 1]);
00832         unsigned int& counter = long_names_[prefix];
00833 
00834         if (serial >= counter) counter = serial + 1;
00835       }
00836 
00837     } else { // regular file
00838       if (ent->d_name[0] != ACE_TEXT('_')) {
00839         files_[b32h_decode(ent->d_name)] = ent->d_name;
00840         ++overflow_[overflow_index];
00841       }
00842     }
00843   }
00844 }
00845 
00846 void Directory::removing(const ACE_TString& child, bool file)
00847 {
00848   Map& m = file ? files_ : dirs_;
00849   Map::iterator iter = m.find(child);
00850 
00851   if (iter == m.end()) return;
00852 
00853   const ACE_TString& phys = iter->second;
00854   String_Index_t idx = phys.find(ACE_TEXT("_overflow."));
00855   unsigned int bucket = (idx == 0 ? ACE_OS::atoi(&phys[idx + 10]) : 0);
00856 
00857   if (--overflow_[bucket] == 0 && bucket > 0) {
00858     overflow_.erase(bucket);
00859     idx = phys.find(ACE_TEXT('/'));
00860     ACE_TString ov_dir = physical_dirname_ + ACE_TString(phys.c_str(), idx);
00861     dds_rmdir(ov_dir.c_str());
00862   }
00863 
00864   m.erase(iter);
00865 }
00866 
00867 // Base32Hex
00868 
00869 ACE_TString b32h_encode(const ACE_TCHAR* decoded)
00870 {
00871   static const ACE_TCHAR lookup[] =
00872     ACE_TEXT("0123456789ABCDEFGHIJKLMNOPQRSTUV");
00873   static const ACE_TCHAR padding[] = ACE_TEXT("======");
00874   static const size_t enc[] = {0, 2, 4, 5, 7}; // #input -> #non-padded output
00875   ACE_TString encoded;
00876 
00877   for (size_t len = ACE_OS::strlen(decoded); *decoded; decoded += 5, len -= 5) {
00878     ACE_UINT64 chunk = 0;
00879 
00880     for (size_t i(0); i < 5 && i < len; ++i) {
00881       chunk |= static_cast<ACE_UINT64>(decoded[i] & 0xFF) << ((4 - i) * 8);
00882     }
00883 
00884     size_t limit = (len < 5) ? enc[len] : 8;
00885 
00886     for (size_t i(0); i < limit; ++i) {
00887       unsigned char val =
00888         static_cast<unsigned char>(chunk >>((7 - i) * 5)) & 0x1F;
00889       encoded += lookup[val];
00890     }
00891 
00892     if (len < 5) {
00893       encoded.append(padding, 8 - enc[len]);
00894       return encoded;
00895     }
00896   }
00897 
00898   return encoded;
00899 }
00900 
00901 ACE_TString b32h_decode(const ACE_TCHAR* encoded)
00902 {
00903   // #before first '=' -> #output
00904   static const size_t dec[] = {0, 0, 1, 0, 2, 3, 0, 4, 0};
00905   ACE_TString decoded;
00906 
00907   for (; *encoded; encoded += 8) {
00908     ACE_UINT64 chunk = 0;
00909     size_t i = 0;
00910 
00911     for (; i < 8 && encoded[i] != ACE_TEXT('='); ++i) {
00912       char idx = (encoded[i] <= ACE_TEXT('9'))
00913                  ? (encoded[i] - ACE_TEXT('0'))
00914                  : (10 + encoded[i] - ACE_TEXT('A'));
00915       chunk |= static_cast<ACE_UINT64>(idx) << ((7 - i) * 5);
00916     }
00917 
00918     size_t limit = (encoded[i] == ACE_TEXT('=')) ? dec[i] : 5;
00919 
00920     for (size_t j(0); j < limit; ++j) {
00921       decoded += static_cast<ACE_TCHAR>(chunk >>((4 - j) * 8)) & 0xFF;
00922     }
00923   }
00924 
00925   return decoded;
00926 }
00927 
00928 } // namespace FileSystemStorage
00929 } // namespace OpenDDS
00930 
00931 OPENDDS_END_VERSIONED_NAMESPACE_DECL
00932 
00933 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated on 10 Aug 2018 for OpenDDS by  doxygen 1.6.1