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
|