LCOV - code coverage report
Current view: top level - DCPS - FileSystemStorage.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 319 0.0 %
Date: 2023-04-30 01:32:43 Functions: 0 38 0.0 %

          Line data    Source code
       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           0 : void add_slash(ACE_TString& str)
      37             : {
      38           0 :   if (str.length() == 0) return;
      39             : 
      40           0 :   if (str[str.length() - 1] == ACE_TEXT('\\')) {
      41           0 :     str[str.length() - 1] = ACE_TEXT('/');
      42             : 
      43           0 :   } else if (str[str.length() - 1] != ACE_TEXT('/')) {
      44           0 :     str += ACE_TEXT('/');
      45             :   }
      46             : }
      47             : 
      48             : // increment a filename in a lexicographical ordering
      49           0 : bool increment(ACE_TString& logical)
      50             : {
      51           0 :   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           0 :     if (logical[i] < 0x7F) {
      55           0 :       ++logical[i];
      56           0 :       return true;
      57             :     }
      58             :   }
      59             : 
      60           0 :   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           0 : inline int dds_mkdir(const ACE_TCHAR* path)
     155             : {
     156           0 :   return ACE_OS::mkdir(path);
     157             : }
     158             : 
     159           0 : inline int dds_chdir(const ACE_TCHAR* path)
     160             : {
     161           0 :   return ACE_OS::chdir(path);
     162             : }
     163             : 
     164           0 : inline bool is_dir(const ACE_TCHAR* path)
     165             : {
     166             :   ACE_stat st;
     167           0 :   return ACE_OS::stat(path, &st) != -1 && (st.st_mode & S_IFDIR);
     168             : }
     169             : 
     170           0 : inline int dds_rmdir(const ACE_TCHAR* path)
     171             : {
     172           0 :   return ACE_OS::rmdir(path);
     173             : }
     174             : 
     175             : #endif // ACE_WIN32
     176             : 
     177           0 : ACE_TString overflow_dir_name(unsigned int n)
     178             : {
     179           0 :   const size_t buf_size=32;
     180             :   ACE_TCHAR of_name[buf_size];
     181           0 :   ACE_OS::snprintf(of_name, buf_size, ACE_TEXT("_overflow.%04u/"), n);
     182           0 :   return of_name;
     183             : }
     184             : 
     185             : struct CwdGuard {
     186             :   ACE_TString cwd_;
     187             : 
     188           0 :   explicit CwdGuard(const ACE_TString& dir) {
     189           0 :     ACE_Vector<ACE_TCHAR> cwd_buf(128);
     190           0 :     cwd_buf.resize(128, ACE_TCHAR(0));
     191             : 
     192           0 :     while (ACE_OS::getcwd(&cwd_buf[0], cwd_buf.size()) == 0) {
     193           0 :       if (errno == ERANGE) {
     194           0 :         cwd_buf.resize(cwd_buf.size() * 2, ACE_TCHAR(0));
     195             : 
     196           0 :       } else break;
     197             :     }
     198             : 
     199           0 :     if (cwd_buf[0]) cwd_ = &cwd_buf[0];
     200             : 
     201           0 :     dds_chdir(dir.c_str());
     202           0 :   }
     203             : 
     204           0 :   ~CwdGuard() {
     205           0 :     dds_chdir(cwd_.c_str());
     206           0 :   }
     207             : };
     208             : 
     209           0 : void recursive_remove(const ACE_TString& dirname)
     210             : {
     211             :   using namespace OpenDDS::FileSystemStorage;
     212           0 :   DDS_Dirent dir(dirname.c_str());
     213             :   {
     214           0 :     CwdGuard cg(dirname);
     215             : 
     216           0 :     for (DDS_DIRENT* ent = dir.read(); ent; ent = dir.read()) {
     217           0 :       if (ent->d_name[0] == ACE_TEXT('.') && (!ent->d_name[1] ||
     218           0 :           (ent->d_name[1] == ACE_TEXT('.') && !ent->d_name[2]))) {
     219           0 :         continue; // skip '.' and '..'
     220             :       }
     221             : 
     222           0 :       if (is_dir(ent->d_name)) {
     223           0 :         recursive_remove(ent->d_name);
     224             : 
     225             :       } else { // regular file
     226           0 :         ACE_OS::unlink(ent->d_name);
     227             :       }
     228             :     }
     229           0 :   }
     230           0 :   dds_rmdir(dirname.c_str());
     231           0 : }
     232             : 
     233             : } // namespace
     234             : 
     235             : OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
     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             : 
     263             : DDS_Dirent::DDS_Dirent(const ACE_TCHAR* path)
     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) {
     288             :     ACE_OS::set_errno_to_last_error();
     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             : 
     413             : DDS_Dirent::DDS_Dirent(const ACE_TCHAR* path)
     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           0 : File::File(const ACE_TString& fname_phys, const ACE_TString& logical,
     460           0 :            const Directory::Ptr& parent)
     461           0 :   : physical_file_()
     462           0 :   , physical_dir_()
     463           0 :   , logical_relative_(logical)
     464           0 :   , parent_(parent)
     465             : {
     466           0 :   String_Index_t last_slash = fname_phys.rfind(ACE_TEXT('/'));
     467             : 
     468           0 :   if (last_slash == ACE_TString::npos) {
     469           0 :     physical_file_ = fname_phys;
     470           0 :     physical_dir_ = ACE_TEXT(".");
     471             : 
     472             :   } else {
     473           0 :     physical_file_ = fname_phys.c_str() + last_slash + 1;
     474           0 :     physical_dir_.set(fname_phys.c_str(), last_slash, true);
     475             :   }
     476           0 : }
     477             : 
     478           0 : bool File::write(std::ofstream& stream)
     479             : {
     480           0 :   CwdGuard cg(physical_dir_);
     481           0 :   stream.open(ACE_TEXT_ALWAYS_CHAR(physical_file_.c_str()),
     482             :               ios::binary | ios::out);
     483           0 :   return !stream.bad() && !stream.fail();
     484           0 : }
     485             : 
     486           0 : bool File::read(std::ifstream& stream)
     487             : {
     488           0 :   CwdGuard cg(physical_dir_);
     489           0 :   stream.open(ACE_TEXT_ALWAYS_CHAR(physical_file_.c_str()),
     490             :               ios::binary | ios::in);
     491           0 :   return !stream.bad() && !stream.fail();
     492           0 : }
     493             : 
     494           0 : bool File::remove()
     495             : {
     496           0 :   int unlink_result = -1;
     497             :   {
     498           0 :     CwdGuard cg(physical_dir_);
     499           0 :     unlink_result = ACE_OS::unlink(physical_file_.c_str());
     500           0 :   }
     501             : 
     502           0 :   if (unlink_result != -1) {
     503           0 :     parent_->removing(logical_relative_, true);
     504           0 :     return true;
     505             :   }
     506             : 
     507           0 :   return false;
     508             : }
     509             : 
     510           0 : OPENDDS_STRING File::name() const
     511             : {
     512           0 :   return ACE_TEXT_ALWAYS_CHAR(logical_relative_.c_str());
     513             : }
     514             : 
     515             : // Directory
     516             : 
     517           0 : /*static*/ Directory::Ptr Directory::create(const char* dirname)
     518             : {
     519           0 :   return DCPS::make_rch<Directory>(ACE_TEXT_CHAR_TO_TCHAR(dirname), ACE_TEXT(""), Directory::Ptr ());
     520             : }
     521             : 
     522           0 : ACE_TString Directory::full_path(const ACE_TString& relative) const
     523             : {
     524           0 :   return physical_dirname_ + relative;
     525             : }
     526             : 
     527           0 : Directory::FileIterator Directory::begin_files()
     528             : {
     529           0 :   return FileIterator(files_.begin(), rchandle_from(this));
     530             : }
     531             : 
     532           0 : Directory::FileIterator Directory::end_files()
     533             : {
     534           0 :   return FileIterator(files_.end(),  rchandle_from(this));
     535             : }
     536             : 
     537           0 : File::Ptr Directory::get_file(const char* name)
     538             : {
     539           0 :   if (std::strlen(name) >= FSS_MAX_FILE_NAME) {
     540           0 :     throw std::runtime_error("file name too long");
     541             :   }
     542             : 
     543           0 :   ACE_TString t_name(ACE_TEXT_CHAR_TO_TCHAR(name));
     544           0 :   Map::iterator it = files_.find(t_name);
     545             : 
     546           0 :   if (it == files_.end()) {
     547           0 :     return make_new_file(t_name);
     548             : 
     549             :   } else {
     550           0 :     return DCPS::make_rch<File>(full_path(it->second), it->first, rchandle_from(this));
     551             :   }
     552           0 : }
     553             : 
     554           0 : File::Ptr Directory::make_new_file(const ACE_TString& t_name)
     555             : {
     556           0 :   if (dirs_.find(t_name) != dirs_.end()) {
     557           0 :     throw std::runtime_error("Can't create a file with the same name as "
     558           0 :                              "an existing directory.");
     559             :   }
     560             : 
     561           0 :   ACE_TString phys = add_entry() + b32h_encode(t_name.c_str());
     562           0 :   files_[t_name] = phys;
     563             : 
     564           0 :   CwdGuard cg(physical_dirname_);
     565             :   // touch the file since the user has asked to create it
     566           0 :   std::FILE* fh = std::fopen(ACE_TEXT_ALWAYS_CHAR(phys.c_str()), "w");
     567             : 
     568           0 :   if (!fh) throw std::runtime_error("Can't create the file");
     569             : 
     570           0 :   std::fclose(fh);
     571           0 :   return DCPS::make_rch<File>(physical_dirname_ + phys, t_name,  rchandle_from(this));
     572           0 : }
     573             : 
     574           0 : File::Ptr Directory::create_next_file()
     575             : {
     576           0 :   ACE_TString logical;
     577             : 
     578           0 :   if (files_.empty()) {
     579           0 :     logical = FSS_DEFAULT_FILE_NAME;
     580             : 
     581             :   } else {
     582           0 :     Map::iterator last = --files_.end();
     583           0 :     logical = last->first;
     584             : 
     585           0 :     if (!increment(logical)) {
     586           0 :       throw std::runtime_error("out of range for create_next_file");
     587             :     }
     588             :   }
     589             : 
     590           0 :   return make_new_file(logical);
     591           0 : }
     592             : 
     593           0 : Directory::DirectoryIterator Directory::begin_dirs()
     594             : {
     595           0 :   return DirectoryIterator(dirs_.begin(),  rchandle_from(this));
     596             : }
     597             : 
     598           0 : Directory::DirectoryIterator Directory::end_dirs()
     599             : {
     600           0 :   return DirectoryIterator(dirs_.end(),  rchandle_from(this));
     601             : }
     602             : 
     603           0 : Directory::Ptr Directory::get_dir(const OPENDDS_VECTOR(OPENDDS_STRING)& path)
     604             : {
     605           0 :   Directory::Ptr dir = rchandle_from(this);
     606             :   typedef OPENDDS_VECTOR(OPENDDS_STRING)::const_iterator iterator;
     607             : 
     608           0 :   for (iterator iter = path.begin(), end = path.end(); iter != end; ++iter) {
     609           0 :     dir = dir->get_subdir(iter->c_str());
     610             :   }
     611             : 
     612           0 :   return dir;
     613           0 : }
     614             : 
     615           0 : Directory::Ptr Directory::get_subdir(const char* name)
     616             : {
     617           0 :   ACE_TString t_name = ACE_TEXT_CHAR_TO_TCHAR(name);
     618           0 :   Map::iterator it = dirs_.find(t_name);
     619             : 
     620           0 :   if (it == dirs_.end()) {
     621           0 :     return make_new_subdir(t_name);
     622             : 
     623             :   } else {
     624           0 :     return DCPS::make_rch<Directory>(full_path(it->second), it->first,  rchandle_from(this));
     625             :   }
     626           0 : }
     627             : 
     628           0 : Directory::Ptr Directory::create_next_dir()
     629             : {
     630           0 :   ACE_TString logical;
     631             : 
     632           0 :   if (dirs_.empty()) {
     633           0 :     logical = FSS_DEFAULT_DIR_NAME;
     634             : 
     635             :   } else {
     636           0 :     Map::iterator last = --dirs_.end();
     637           0 :     logical = last->first;
     638             : 
     639           0 :     if (!increment(logical)) {
     640           0 :       throw std::runtime_error("out of range for create_next_dir");
     641             :     }
     642             :   }
     643             : 
     644           0 :   return make_new_subdir(logical);
     645           0 : }
     646             : 
     647           0 : Directory::Ptr Directory::make_new_subdir(const ACE_TString& t_name)
     648             : {
     649           0 :   if (files_.find(t_name) != files_.end()) {
     650           0 :     throw std::runtime_error("Can't create a directory with the same "
     651           0 :                              "name as an existing file.");
     652             :   }
     653             : 
     654             :   ACE_TString logical(t_name.c_str(),
     655           0 :                       (std::min)(FSS_MAX_FILE_NAME, t_name.length()));
     656           0 :   ACE_TString phys_prefix = add_entry();
     657           0 :   ACE_TString phys_base = b32h_encode(logical.c_str());
     658             : 
     659           0 :   if (t_name.length() >= FSS_MAX_FILE_NAME) {
     660           0 :     unsigned int& counter = long_names_[phys_prefix + phys_base];
     661             : 
     662           0 :     if (counter == 99999) {
     663           0 :       throw std::runtime_error("Long directory name out of range");
     664             :     }
     665             : 
     666           0 :     phys_base += ACE_TEXT(".     X"); // snprintf will clobber the X with a 0
     667           0 :     ACE_TCHAR* buf = &phys_base[0] + phys_base.length() - 6;
     668           0 :     ACE_OS::snprintf(buf, 6, ACE_TEXT("%05u"), counter++);
     669           0 :     phys_base = phys_base.substr(0, phys_base.length() - 1); // trim the 0
     670             :   }
     671             : 
     672           0 :   ACE_TString phys = phys_prefix + phys_base;
     673           0 :   dirs_[t_name] = phys;
     674             :   {
     675           0 :     CwdGuard cg(physical_dirname_);
     676             : 
     677           0 :     if (dds_mkdir(phys.c_str()) == -1) {
     678           0 :       throw std::runtime_error("Can't create directory");
     679             :     }
     680             : 
     681           0 :     if ((phys_prefix.length() > 0 && dds_chdir(phys_prefix.c_str()) == -1)
     682           0 :         || dds_chdir(phys_base.c_str()) == -1) {
     683           0 :       dds_rmdir(phys.c_str());
     684           0 :       throw std::runtime_error("Can't change to newly created directory");
     685             :     }
     686             : 
     687           0 :     std::ofstream fn("_fullname");
     688           0 :     fn << t_name << '\n';
     689           0 :   }
     690           0 :   return DCPS::make_rch<Directory>(physical_dirname_ + phys, t_name,  rchandle_from(this));
     691           0 : }
     692             : 
     693           0 : ACE_TString Directory::add_entry()
     694             : {
     695           0 :   if (overflow_.empty()) {
     696           0 :     overflow_[0] = 1;
     697           0 :     return ACE_TEXT("");
     698             :   }
     699             : 
     700             :   typedef OPENDDS_MAP(unsigned int, unsigned int)::iterator iterator;
     701             :   // find existing overflow bucket with capacity
     702           0 :   bool found_gap(false);
     703           0 :   unsigned int last_seen(0), unused_bucket(0);
     704             : 
     705           0 :   for (iterator iter = overflow_.begin(), end = overflow_.end();
     706           0 :        iter != end; ++iter) {
     707           0 :     if (iter->second < OPENDDS_FILESYSTEMSTORAGE_MAX_FILES_PER_DIR) {
     708           0 :       ++iter->second;
     709             : 
     710           0 :       if (iter->first == 0) return ACE_TEXT("");
     711             : 
     712           0 :       return overflow_dir_name(iter->first);
     713             :     }
     714             : 
     715           0 :     if (!found_gap && iter->first > last_seen + 1) {
     716           0 :       found_gap = true;
     717           0 :       unused_bucket = last_seen + 1;
     718             :     }
     719             : 
     720           0 :     last_seen = iter->first;
     721             :   }
     722             : 
     723           0 :   if (!found_gap) {
     724           0 :     if (last_seen == FSS_MAX_OVERFLOW_DIR) {
     725           0 :       throw std::runtime_error("Overflow serial # out of range.");
     726             :     }
     727             : 
     728           0 :     unused_bucket = last_seen + 1;
     729             :   }
     730             : 
     731           0 :   overflow_[unused_bucket] = 1;
     732           0 :   ACE_TString dir_name = overflow_dir_name(unused_bucket);
     733           0 :   CwdGuard cg(physical_dirname_);
     734             : 
     735           0 :   if (dds_mkdir(dir_name.c_str()) == -1) {
     736           0 :     throw std::runtime_error("Can't create overflow directory");
     737             :   }
     738             : 
     739           0 :   return dir_name;
     740           0 : }
     741             : 
     742           0 : void Directory::remove()
     743             : {
     744           0 :   if (!parent_.is_nil()) parent_->removing(logical_dirname_, false);
     745             : 
     746           0 :   parent_.reset();
     747           0 :   recursive_remove(physical_dirname_);
     748           0 :   overflow_.clear();
     749           0 :   files_.clear();
     750           0 :   dirs_.clear();
     751           0 :   long_names_.clear();
     752           0 : }
     753             : 
     754           0 : OPENDDS_STRING Directory::name() const
     755             : {
     756           0 :   return ACE_TEXT_ALWAYS_CHAR(logical_dirname_.c_str());
     757             : }
     758             : 
     759           0 : Directory::Directory(const ACE_TString& dirname, const ACE_TString& logical,
     760           0 :                      const Directory::Ptr& parent)
     761           0 :   : parent_(parent)
     762           0 :   , physical_dirname_(dirname)
     763           0 :   , logical_dirname_(logical)
     764             : {
     765           0 :   add_slash(physical_dirname_);
     766             : 
     767           0 :   bool ok(true);
     768           0 :   DDS_Dirent dir;
     769             : 
     770           0 :   if (dir.open(physical_dirname_.c_str()) == -1) {
     771           0 :     ok = false;
     772             : 
     773           0 :     if (errno == ENOENT && dds_mkdir(physical_dirname_.c_str()) != -1
     774           0 :         && dir.open(physical_dirname_.c_str()) != -1) {
     775           0 :       ok = true;
     776             :     }
     777             :   }
     778             : 
     779           0 :   if (!ok) throw std::runtime_error("Can't open or create directory");
     780             : 
     781           0 :   scan_dir(ACE_TEXT(""), dir, 0);
     782           0 : }
     783             : 
     784           0 : void Directory::scan_dir(const ACE_TString& relative, DDS_Dirent& dir,
     785             :                          unsigned int overflow_index)
     786             : {
     787           0 :   ACE_TString path = physical_dirname_ + relative;
     788           0 :   add_slash(path);
     789             : 
     790           0 :   while (DDS_DIRENT* ent = dir.read()) {
     791           0 :     if (ent->d_name[0] == ACE_TEXT('.') && (!ent->d_name[1] ||
     792           0 :         (ent->d_name[1] == ACE_TEXT('.') && !ent->d_name[2]))) {
     793           0 :       continue; // skip '.' and '..'
     794             :     }
     795             : 
     796           0 :     ACE_TString file = path + ent->d_name;
     797             : 
     798           0 :     if (is_dir(file.c_str())) {
     799           0 :       ACE_TString phys(relative);
     800           0 :       add_slash(phys);
     801           0 :       phys += ent->d_name;
     802             : 
     803           0 :       if (ACE_OS::strncmp(ent->d_name, ACE_TEXT("_overflow."), 10) == 0) {
     804           0 :         unsigned int n = ACE_OS::atoi(ent->d_name + 10);
     805           0 :         DDS_Dirent overflow(file.c_str());
     806           0 :         scan_dir(ent->d_name, overflow, n);
     807             : 
     808           0 :       } else if (ACE_OS::strlen(ent->d_name) <= FSS_MAX_FILE_NAME_ENCODED) {
     809           0 :         dirs_[b32h_decode(ent->d_name)] = phys;
     810           0 :         ++overflow_[overflow_index];
     811             : 
     812             :       } else {
     813           0 :         CwdGuard cg(file);
     814           0 :         std::ifstream fn("_fullname");
     815           0 :         OPENDDS_STRING fullname;
     816             : 
     817           0 :         if (!std::getline(fn, fullname)) {
     818           0 :           throw std::runtime_error("Can't read .../_fullname");
     819             :         }
     820             : 
     821           0 :         ACE_TString full_t(ACE_TEXT_CHAR_TO_TCHAR(fullname.c_str()));
     822           0 :         dirs_[full_t] = phys;
     823           0 :         ++overflow_[overflow_index];
     824             : 
     825           0 :         String_Index_t idx = phys.rfind(ACE_TEXT('.'));
     826             : 
     827           0 :         if (idx == ACE_TString::npos) {
     828           0 :           throw std::runtime_error("Badly formatted long dir name");
     829             :         }
     830             : 
     831           0 :         ACE_TString prefix(phys.c_str(), idx);
     832           0 :         unsigned int serial = ACE_OS::atoi(&phys[idx + 1]);
     833           0 :         unsigned int& counter = long_names_[prefix];
     834             : 
     835           0 :         if (serial >= counter) counter = serial + 1;
     836           0 :       }
     837             : 
     838           0 :     } else { // regular file
     839           0 :       if (ent->d_name[0] != ACE_TEXT('_')) {
     840           0 :         files_[b32h_decode(ent->d_name)] = ent->d_name;
     841           0 :         ++overflow_[overflow_index];
     842             :       }
     843             :     }
     844           0 :   }
     845           0 : }
     846             : 
     847           0 : void Directory::removing(const ACE_TString& child, bool file)
     848             : {
     849           0 :   Map& m = file ? files_ : dirs_;
     850           0 :   Map::iterator iter = m.find(child);
     851             : 
     852           0 :   if (iter == m.end()) return;
     853             : 
     854           0 :   const ACE_TString& phys = iter->second;
     855           0 :   String_Index_t idx = phys.find(ACE_TEXT("_overflow."));
     856           0 :   unsigned int bucket = (idx == 0 ? ACE_OS::atoi(&phys[idx + 10]) : 0);
     857             : 
     858           0 :   if (--overflow_[bucket] == 0 && bucket > 0) {
     859           0 :     overflow_.erase(bucket);
     860           0 :     idx = phys.find(ACE_TEXT('/'));
     861           0 :     ACE_TString ov_dir = physical_dirname_ + ACE_TString(phys.c_str(), idx);
     862           0 :     dds_rmdir(ov_dir.c_str());
     863           0 :   }
     864             : 
     865           0 :   m.erase(iter);
     866             : }
     867             : 
     868             : // Base32Hex
     869             : 
     870           0 : ACE_TString b32h_encode(const ACE_TCHAR* decoded)
     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
     876           0 :   ACE_TString encoded;
     877             : 
     878           0 :   for (size_t len = ACE_OS::strlen(decoded); *decoded; decoded += 5, len -= 5) {
     879           0 :     ACE_UINT64 chunk = 0;
     880             : 
     881           0 :     for (size_t i(0); i < 5 && i < len; ++i) {
     882           0 :       chunk |= static_cast<ACE_UINT64>(decoded[i] & 0xFF) << ((4 - i) * 8);
     883             :     }
     884             : 
     885           0 :     size_t limit = (len < 5) ? enc[len] : 8;
     886             : 
     887           0 :     for (size_t i(0); i < limit; ++i) {
     888           0 :       unsigned char val =
     889           0 :         static_cast<unsigned char>(chunk >>((7 - i) * 5)) & 0x1F;
     890           0 :       encoded += lookup[val];
     891             :     }
     892             : 
     893           0 :     if (len < 5) {
     894           0 :       encoded.append(padding, 8 - enc[len]);
     895           0 :       return encoded;
     896             :     }
     897             :   }
     898             : 
     899           0 :   return encoded;
     900           0 : }
     901             : 
     902           0 : ACE_TString b32h_decode(const ACE_TCHAR* encoded)
     903             : {
     904             :   // #before first '=' -> #output
     905             :   static const size_t dec[] = {0, 0, 1, 0, 2, 3, 0, 4, 0};
     906           0 :   ACE_TString decoded;
     907             : 
     908           0 :   for (; *encoded; encoded += 8) {
     909           0 :     ACE_UINT64 chunk = 0;
     910           0 :     size_t i = 0;
     911             : 
     912           0 :     for (; i < 8 && encoded[i] != ACE_TEXT('='); ++i) {
     913           0 :       char idx = (encoded[i] <= ACE_TEXT('9'))
     914           0 :                  ? (encoded[i] - ACE_TEXT('0'))
     915           0 :                  : (10 + encoded[i] - ACE_TEXT('A'));
     916           0 :       chunk |= static_cast<ACE_UINT64>(idx) << ((7 - i) * 5);
     917             :     }
     918             : 
     919           0 :     size_t limit = (encoded[i] == ACE_TEXT('=')) ? dec[i] : 5;
     920             : 
     921           0 :     for (size_t j(0); j < limit; ++j) {
     922           0 :       decoded += static_cast<ACE_TCHAR>(chunk >>((4 - j) * 8)) & 0xFF;
     923             :     }
     924             :   }
     925             : 
     926           0 :   return decoded;
     927           0 : }
     928             : 
     929             : } // namespace FileSystemStorage
     930             : } // namespace OpenDDS
     931             : 
     932             : OPENDDS_END_VERSIONED_NAMESPACE_DECL
     933             : 
     934             : #endif

Generated by: LCOV version 1.16