NCBI C++ ToolKit
ncbifile.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: ncbifile.cpp 102274 2024-04-15 14:13:11Z ivanov $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Vladimir Ivanov
27  *
28  * File Description: Files and directories accessory functions
29  *
30  */
31 
32 #include <ncbi_pch.hpp>
33 #include <corelib/ncbifile.hpp>
34 #include <corelib/ncbi_limits.h>
35 #include <corelib/ncbi_limits.hpp>
37 #include <corelib/error_codes.hpp>
38 #include <corelib/ncbierror.hpp>
39 #include <atomic>
40 #include <stdio.h>
41 
42 #if defined(NCBI_OS_MSWIN)
43 # include "ncbi_os_mswin_p.hpp"
44 # include <direct.h>
45 # include <io.h>
46 # include <fcntl.h> // for _O_* flags
47 # include <sys/utime.h>
48 
49 #elif defined(NCBI_OS_UNIX)
50 # include "ncbi_os_unix_p.hpp"
51 # include <corelib/impl/ncbi_panfs.h>
52 # include <dirent.h>
53 # include <fcntl.h>
54 # include <unistd.h>
55 # include <utime.h>
56 # include <sys/mman.h>
57 # include <sys/time.h>
58 # ifdef HAVE_SYS_STATVFS_H
59 # include <sys/statvfs.h>
60 # endif
61 # include <sys/param.h>
62 # ifdef HAVE_SYS_MOUNT_H
63 # include <sys/mount.h>
64 # endif
65 # ifdef HAVE_SYS_VFS_H
66 # include <sys/vfs.h>
67 # endif
68 # if !defined(MAP_FAILED)
69 # define MAP_FAILED ((void *)(-1L))
70 # endif
71 # include <sys/ioctl.h>
72 
73 #else
74 # error "File API defined for MS Windows and UNIX platforms only"
75 
76 #endif /* NCBI_OS_MSWIN, NCBI_OS_UNIX */
77 
78 
79 // Define platforms on which we support PANFS
80 #if defined(NCBI_OS_UNIX) && !defined(NCBI_OS_CYGWIN)
81 # define SUPPORT_PANFS
82 # include <sys/types.h>
83 # include <sys/wait.h>
84 #endif
85 
86 
87 #define NCBI_USE_ERRCODE_X Corelib_File
88 
89 
91 
92 
93 // Path separators
94 
95 #undef DIR_SEPARATOR
96 #undef DIR_SEPARATOR_ALT
97 #undef DIR_SEPARATORS
98 #undef DISK_SEPARATOR
99 #undef ALL_SEPARATORS
100 #undef ALL_OS_SEPARATORS
101 
102 #define DIR_PARENT ".."
103 #define DIR_CURRENT "."
104 #define ALL_OS_SEPARATORS ":/\\"
105 
106 #if defined(NCBI_OS_MSWIN)
107 # define DIR_SEPARATOR '\\'
108 # define DIR_SEPARATOR_ALT '/'
109 # define DISK_SEPARATOR ':'
110 # define DIR_SEPARATORS "/\\"
111 # define ALL_SEPARATORS ":/\\"
112 #elif defined(NCBI_OS_UNIX)
113 # define DIR_SEPARATOR '/'
114 # define DIR_SEPARATORS "/"
115 # define ALL_SEPARATORS "/"
116 #endif
117 
118 // Macro to check bits
119 #define F_ISSET(flags, mask) ((flags & (mask)) == (mask))
120 
121 // Default buffer size, used to read/write files
122 const size_t kDefaultBufferSize = 64*1024;
123 
124 // List of files for CFileDeleteAtExit class
126 
127 
128 // Declare the parameter to get directory for temporary files.
129 // Registry file:
130 // [NCBI]
131 // TmpDir = ...
132 // Environment variable:
133 // NCBI_CONFIG__NCBI__TmpDir
134 //
135 NCBI_PARAM_DECL(string, NCBI, TmpDir);
136 NCBI_PARAM_DEF (string, NCBI, TmpDir, "");
137 
138 
139 // Define how read-only files are treated on Windows.
140 // Registry file:
141 // [NCBI]
142 // DeleteReadOnlyFiles = true/false
143 // Environment variable:
144 // NCBI_CONFIG__DELETEREADONLYFILES
145 //
146 NCBI_PARAM_DECL(bool, NCBI, DeleteReadOnlyFiles);
147 NCBI_PARAM_DEF_EX(bool, NCBI, DeleteReadOnlyFiles, false,
148  eParam_NoThread, NCBI_CONFIG__DELETEREADONLYFILES);
149 
150 
151 // Declare how umask settings on Unix affect creating files/directories
152 // in the File API.
153 // Registry file:
154 // [NCBI]
155 // FileAPIHonorUmask = true/false
156 // Environment variable:
157 // NCBI_CONFIG__FILEAPIHONORUMASK
158 //
159 // On WINDOWS: umask affect only CRT function, the part of API that
160 // use Windows API directly just ignore umask setting.
161 #define DEFAULT_HONOR_UMASK_VALUE false
162 
163 NCBI_PARAM_DECL(bool, NCBI, FileAPIHonorUmask);
165  eParam_NoThread, NCBI_CONFIG__FILEAPIHONORUMASK);
166 
167 
168 // Declare the parameter to turn on logging from CFile,
169 // CDirEntry, etc. classes.
170 // Registry file:
171 // [NCBI]
172 // FileAPILogging = true/false
173 // Environment variable:
174 // NCBI_CONFIG__FILEAPILOGGING
175 //
176 #define DEFAULT_LOGGING_VALUE false
177 
178 NCBI_PARAM_DECL(bool, NCBI, FileAPILogging);
180  eParam_NoThread, NCBI_CONFIG__FILEAPILOGGING);
181 
182 
183 // NOTE:
184 //
185 // Some common and very often used methods like
186 // CDirEntry::GetType()
187 // CDirEntry::Exists()
188 // don't use LOG_ERROR* to report errors, and just set an error codes to CNcbiError.
189 
190 // Report error without error code.
191 // Used when error code is unknown, or in conjunction with methods
192 // that already report errors and codes itself.
193 #define LOG_ERROR(subcode, log_message) \
194  { \
195  if (NCBI_PARAM_TYPE(NCBI, FileAPILogging)::GetDefault()) { \
196  ERR_POST_X(subcode, log_message); \
197  } \
198  }
199 
200 // Report general NCBI error
201 #define LOG_ERROR_NCBI(subcode, log_message, ncbierr) \
202  { \
203  CNcbiError::Set(ncbierr, log_message); \
204  if (NCBI_PARAM_TYPE(NCBI, FileAPILogging)::GetDefault()) { \
205  ERR_POST_X(subcode, log_message); \
206  } \
207  }
208 
209 // Report Windows API error.
210 // Should be called immediately after failed Windows API call.
211 #define LOG_ERROR_WIN(subcode, log_message) \
212  { \
213  CNcbiError::SetFromWindowsError(log_message); \
214  if (NCBI_PARAM_TYPE(NCBI, FileAPILogging)::GetDefault()) { \
215  ERR_POST_X(subcode, log_message); \
216  } \
217  }
218 
219 // Report errno-based error.
220 // Should be called immediately after system call.
221 #define LOG_ERROR_ERRNO(subcode, log_message) \
222  { \
223  int saved_error = errno; \
224  CNcbiError::SetErrno(saved_error, log_message); \
225  if (NCBI_PARAM_TYPE(NCBI, FileAPILogging)::GetDefault()) { \
226  ERR_POST_X(subcode, log_message << ": " << _T_STDSTRING(NcbiSys_strerror(saved_error))); \
227  } \
228  errno = saved_error; \
229  }
230 
231 // Macro to silence GCC's __wur (warn unused result)
232 #define _no_warning(expr) while ( expr ) break
233 
234 // Get an error string for last error on Windows
235 #define WIN_LAST_ERROR_STR CLastErrorAdapt::GetErrCodeString(::GetLastError())
236 
237 // tv_usec part for struct timeval have an 'int' type on Darwin, so we need a conversion from 'long' to avoid warnings
238 #if defined(NCBI_OS_DARWIN)
239 # define TV_USEC(x) (int)(x)
240 #else
241 # define TV_USEC(x) x
242 #endif
243 
244 
245 
246 //////////////////////////////////////////////////////////////////////////////
247 //
248 // CDirEntry
249 //
250 
251 CDirEntry::CDirEntry(const string& name)
252 {
253  Reset(name);
258 }
259 
260 
262  : m_Path(other.m_Path)
263 {
268 }
269 
270 
272 {
273  return;
274 }
275 
276 
278 {
279  CDirEntry *ptr;
280  switch ( type ) {
281  case eFile:
282  ptr = new CFile(path);
283  break;
284  case eDir:
285  ptr = new CDir(path);
286  break;
287  case eLink:
288  ptr = new CSymLink(path);
289  break;
290  default:
291  ptr = new CDirEntry(path);
292  break;
293  }
294  return ptr;
295 }
296 
297 
298 void CDirEntry::Reset(const string& path)
299 {
300  m_Path = path;
301  size_t len = path.length();
302  // Root dir
303  if ((len == 1) && IsPathSeparator(path[0])) {
304  return;
305  }
306  // Disk name
307 # if defined(DISK_SEPARATOR)
308  if ( (len == 2 || len == 3) && (path[1] == DISK_SEPARATOR) ) {
309  return;
310  }
311 # endif
313 }
314 
315 
317 {
318  // eFile
320  // eDir
322  // ePipe
324  // eLink
326  // eSocket
328  // eDoor
330  // eBlockSpecial
332  // eCharSpecial
334 };
335 
336 
337 // Default backup suffix
338 const char* CDirEntry::m_BackupSuffix = ".bak";
339 
340 // Part of the temporary name used for "safe copy"
341 static const char* kTmpSafeSuffix = ".tmp.";
342 
343 
344 
346 {
347  if (this != &other) {
348  m_Path = other.m_Path;
353  }
354  return *this;
355 }
356 
357 
358 void CDirEntry::SplitPath(const string& path, string* dir,
359  string* base, string* ext)
360 {
361  // Get file name
362  size_t pos = path.find_last_of(ALL_SEPARATORS);
363  string filename = (pos == NPOS) ? path : path.substr(pos+1);
364 
365  // Get dir
366  if ( dir ) {
367  *dir = (pos == NPOS) ? kEmptyStr : path.substr(0, pos+1);
368  }
369  // Split file name to base and extension
370  pos = filename.rfind('.');
371  if ( base ) {
372  *base = (pos == NPOS) ? filename : filename.substr(0, pos);
373  }
374  if ( ext ) {
375  *ext = (pos == NPOS) ? kEmptyStr : filename.substr(pos);
376  }
377 }
378 
379 
380 void CDirEntry::SplitPathEx(const string& path,
381  string* disk, string* dir,
382  string* base, string* ext)
383 {
384  size_t start_pos = 0;
385 
386  // Get disk
387  if ( disk ) {
388  if ( isalpha((unsigned char)path[0]) && path[1] == ':' ) {
389  *disk = path.substr(0, 2);
390  start_pos = 2;
391  } else {
392  *disk = kEmptyStr;
393  }
394  }
395  // Get file name
396  size_t pos = path.find_last_of(ALL_OS_SEPARATORS);
397  string filename = (pos == NPOS) ? path : path.substr(pos+1);
398  // Get dir
399  if ( dir ) {
400  *dir = (pos == NPOS) ? kEmptyStr : path.substr(start_pos, pos - start_pos + 1);
401  }
402  // Split file name to base and extension
403  pos = filename.rfind('.');
404  if ( base ) {
405  *base = (pos == NPOS) ? filename : filename.substr(0, pos);
406  }
407  if ( ext ) {
408  *ext = (pos == NPOS) ? kEmptyStr : filename.substr(pos);
409  }
410 }
411 
412 
413 string CDirEntry::MakePath(const string& dir, const string& base,
414  const string& ext)
415 {
416  string path;
417 
418  // Adding "dir" and file base
419  if ( dir.length() ) {
420  path = AddTrailingPathSeparator(dir);
421  }
422  path += base;
423  // Adding extension
424  if ( ext.length() && ext.at(0) != '.' ) {
425  path += '.';
426  }
427  path += ext;
428  // Return result
429  return path;
430 }
431 
432 
434 {
435  return DIR_SEPARATOR;
436 }
437 
438 
439 bool CDirEntry::IsPathSeparator(const char c)
440 {
441 #if defined(DISK_SEPARATOR)
442  if ( c == DISK_SEPARATOR ) {
443  return true;
444  }
445 #endif
446 #if defined(DIR_SEPARATOR_ALT)
447  if ( c == DIR_SEPARATOR_ALT ) {
448  return true;
449  }
450 #endif
451  return c == DIR_SEPARATOR;
452 }
453 
454 
455 string CDirEntry::AddTrailingPathSeparator(const string& path)
456 {
457  size_t len = path.length();
458  if (len && string(ALL_SEPARATORS).rfind(path.at(len - 1)) == NPOS) {
459  return path + GetPathSeparator();
460  }
461  return path;
462 }
463 
464 
465 string CDirEntry::DeleteTrailingPathSeparator(const string& path)
466 {
467  size_t pos = path.find_last_not_of(DIR_SEPARATORS);
468  if (pos + 1 < path.length()) {
469  return path.substr(0, pos + 1);
470  }
471  return path;
472 }
473 
474 
476 {
477  string dir;
478  SplitPath(GetPath(), &dir);
479  if ( dir.empty() && mode == eIfEmptyPath_Current && !GetPath().empty() ) {
480  return string(DIR_CURRENT) + DIR_SEPARATOR;
481  }
482  return dir;
483 }
484 
485 
486 // Windows:
487 inline bool s_Win_IsDiskPath(const string& path)
488 {
489  if ((isalpha((unsigned char)path[0]) && path[1] == ':') &&
490  (path[2] == '/' || path[2] == '\\')) {
491  return true;
492  }
493  return false;
494 }
495 
496 // Windows:
497 inline bool s_Win_IsNetworkPath(const string& path)
498 {
499  // Any combination of slashes in 2 first symbols
500  if ((path[0] == '\\' || path[0] == '/') &&
501  (path[1] == '\\' || path[1] == '/')) {
502  return true;
503  }
504  return false;
505 }
506 
507 
508 bool CDirEntry::IsAbsolutePath(const string& path)
509 {
510  if (path.empty()) {
511  return false;
512  }
513 #if defined(NCBI_OS_MSWIN)
514  if (s_Win_IsDiskPath(path) || s_Win_IsNetworkPath(path)) {
515  return true;
516  }
517 #elif defined(NCBI_OS_UNIX)
518  if ( path[0] == '/' ) {
519  return true;
520  }
521 #endif
522  return false;
523 }
524 
525 
526 bool CDirEntry::IsAbsolutePathEx(const string& path)
527 {
528  if ( path.empty() )
529  return false;
530 
531  // Windows:
532  if (s_Win_IsDiskPath(path) || s_Win_IsNetworkPath(path)) {
533  return true;
534  }
535  // Unix
536  if (path[0] == '/') {
537  // FIXME:
538  // This is an Unix absolute path or MS Windows relative.
539  // But Unix have favor here, because '/' is native dir separator.
540  return true;
541  }
542  // Else - relative
543  return false;
544 }
545 
546 
547 string CDirEntry::GetNearestExistingParentDir(const string& path)
548 {
549  CDirEntry entry(NormalizePath(path));
550 
551  // Find closest existing directory
552  while (!entry.Exists()) {
553  string dir = entry.GetDir();
554  if (dir.empty()) {
555  NCBI_THROW(CFileException, eNotExists,
556  "Failed to find existing containing directory for: " + entry.GetPath());
557  }
558  entry.Reset(dir);
559  }
560  return entry.GetPath();
561 }
562 
563 
564 /// Helper -- strips dir to parts:
565 /// c:\a\b\ will be <c:><a><b>
566 /// /usr/bin/ will be </><usr><bin>
567 static void s_StripDir(const string& dir, vector<string> * dir_parts)
568 {
569  dir_parts->clear();
570  if (dir.empty()) {
571  return;
572  }
573  const char sep = CDirEntry::GetPathSeparator();
574 
575  size_t sep_pos = 0;
576  size_t last_ind = dir.length() - 1;
577  size_t part_start = 0;
578  for (;;) {
579  sep_pos = dir.find(sep, sep_pos);
580  if (sep_pos == NPOS) {
581  dir_parts->push_back(string(dir, part_start, dir.length() - part_start));
582  break;
583  }
584  // If path starts from '/' - it's a root directory
585  if (sep_pos == 0) {
586  dir_parts->push_back(string(1, sep));
587  } else {
588  dir_parts->push_back(string(dir, part_start, sep_pos - part_start));
589  }
590  sep_pos++;
591  part_start = sep_pos;
592  if (sep_pos >= last_ind) {
593  break;
594  }
595  }
596 }
597 
598 
599 string CDirEntry::CreateRelativePath( const string& path_from,
600  const string& path_to )
601 {
602  string path; // the result
603 
604  if ( !IsAbsolutePath(path_from) ) {
605  NCBI_THROW(CFileException, eRelativePath, "path_from is not absolute path");
606  }
607 
608  if ( !IsAbsolutePath(path_to) ) {
609  NCBI_THROW(CFileException, eRelativePath, "path_to is not absolute path");
610  }
611 
612  // Split and strip FROM
613  string dir_from;
614  SplitPath(AddTrailingPathSeparator(path_from), &dir_from);
615  vector<string> dir_from_parts;
616  s_StripDir(dir_from, &dir_from_parts);
617  if ( dir_from_parts.empty() ) {
618  NCBI_THROW(CFileException, eRelativePath, "path_from is empty path");
619  }
620 
621  // Split and strip TO
622  string dir_to, base_to, ext_to;
623  SplitPath(path_to, &dir_to, &base_to, &ext_to);
624  vector<string> dir_to_parts;
625  s_StripDir(dir_to, &dir_to_parts);
626  if ( dir_to_parts.empty() ) {
627  NCBI_THROW(CFileException, eRelativePath, "path_to is empty path");
628  }
629 
630  // Platform-dependent compare mode
631 #ifdef NCBI_OS_MSWIN
632 # define DIR_PARTS_CMP_MODE NStr::eNocase
633 #else /*NCBI_OS_UNIX*/
634 # define DIR_PARTS_CMP_MODE NStr::eCase
635 #endif
636  // Roots must be the same to create relative path from one to another
637 
638  if (NStr::Compare(dir_from_parts.front(),
639  dir_to_parts.front(),
640  DIR_PARTS_CMP_MODE) != 0) {
641  NCBI_THROW(CFileException, eRelativePath, "roots of input paths are different");
642  }
643 
644  size_t min_parts = min(dir_from_parts.size(), dir_to_parts.size());
645  size_t common_length = min_parts;
646  for (size_t i = 0; i < min_parts; i++) {
647  if (NStr::Compare(dir_from_parts[i], dir_to_parts[i], DIR_PARTS_CMP_MODE) != 0) {
648  common_length = i;
649  break;
650  }
651  }
652  for (size_t i = common_length; i < dir_from_parts.size(); i++) {
653  path += "..";
654  path += GetPathSeparator();
655  }
656  for (size_t i = common_length; i < dir_to_parts.size(); i++) {
657  path += dir_to_parts[i];
658  path += GetPathSeparator();
659  }
660 
661  return path + base_to + ext_to;
662 }
663 
664 
665 string CDirEntry::CreateAbsolutePath(const string& path, ERelativeToWhat rtw)
666 {
667  if ( IsAbsolutePath(path) ) {
668  return NormalizePath(path);
669  }
670  string result;
671 
672 #if defined(NCBI_OS_MSWIN)
673  if ( path.find(DISK_SEPARATOR) != NPOS ) {
674  NCBI_THROW(CFileException, eRelativePath,
675  "Path must not contain disk separator: " + path);
676  }
677  // Path started with slash
678  if (!path.empty() && (path[0] == '/' || path[0] == '\\')) {
679  // network path ?
680  if ( s_Win_IsNetworkPath(path) ) {
681  NCBI_THROW(CFileException, eRelativePath,
682  "Cannot use network path: " + path);
683  }
684  // relative to current drive only
685  if (rtw != eRelativeToCwd) {
686  NCBI_THROW(CFileException, eRelativePath,
687  "Path can be used as relative to current drive only: " + path);
688  }
689  return CDir::GetCwd().substr(0,3) + path;
690  }
691 #endif
692 
693  switch (rtw) {
694  case eRelativeToCwd:
695  result = ConcatPath(CDir::GetCwd(), path);
696  break;
697  case eRelativeToExe:
698  {
699  string dir;
701  result = ConcatPath(dir, path);
702  if ( !CDirEntry(result).Exists() ) {
704  result = ConcatPath(dir, path);
705  }
706  break;
707  }
708  }
709  return NormalizePath(result);
710 }
711 
712 
713 string CDirEntry::CreateAbsolutePath(const string& path, const string& rtw)
714 {
715  if ( IsAbsolutePath(path) ) {
716  return NormalizePath(path);
717  }
718 
719 #if defined(NCBI_OS_MSWIN)
720  if ( path.find(DISK_SEPARATOR) != NPOS ) {
721  NCBI_THROW(CFileException, eRelativePath,
722  "Path must not contain disk separator: " + path);
723  }
724  // Path started with slash
725  if (!path.empty() && (path[0] == '/' || path[0] == '\\')) {
726  // network path ?
727  if ( s_Win_IsNetworkPath(path) ) {
728  NCBI_THROW(CFileException, eRelativePath,
729  "Cannot use network path: " + path);
730  }
731  // relative to current drive only -- error
732  NCBI_THROW(CFileException, eRelativePath,
733  "Path can be used as relative to current drive only: " + path);
734  }
735 #endif
736 
737  if ( !IsAbsolutePath(rtw) ) {
738  NCBI_THROW(CFileException, eRelativePath,
739  "2nd parameter must represent absolute path: " + rtw);
740  }
741  return NormalizePath(ConcatPath(rtw, path));
742 }
743 
744 
745 string CDirEntry::ConvertToOSPath(const string& path)
746 {
747  // Not process empty or absolute path
748  if ( path.empty() || IsAbsolutePathEx(path)) {
749  return NormalizePath(path);
750  }
751  // Now we have relative "path"
752  string xpath = path;
753  // Add trailing separator if path ends with DIR_PARENT or DIR_CURRENT
754 #if defined(DIR_PARENT)
755  if ( NStr::EndsWith(xpath, DIR_PARENT) ) {
756  xpath += DIR_SEPARATOR;
757  }
758 #endif
759 #if defined(DIR_CURRENT)
760  if ( NStr::EndsWith(xpath, DIR_CURRENT) ) {
761  xpath += DIR_SEPARATOR;
762  }
763 #endif
764  // Replace each path separator with the current OS separator character
765  for (size_t i = 0; i < xpath.length(); i++) {
766  char c = xpath[i];
767  if ( c == '\\' || c == '/' ) {
768  xpath[i] = DIR_SEPARATOR;
769  }
770  }
771  xpath = NormalizePath(xpath);
772  return xpath;
773 }
774 
775 
776 string CDirEntry::ConcatPath(const string& first, const string& second)
777 {
778  // Prepare first part of path
780  // Remove leading separator in "second" part
781  string part = NStr::TruncateSpaces(second);
782  if ( !path.empty() && part.length() > 0 && part[0] == DIR_SEPARATOR ) {
783  part.erase(0,1);
784  }
785  // Add second part
786  path += part;
787  return path;
788 }
789 
790 
791 string CDirEntry::ConcatPathEx(const string& first, const string& second)
792 {
793  // Prepare first part of path
794  string path = NStr::TruncateSpaces(first);
795 
796  // Add trailing path separator to first part (OS independence)
797 
798  size_t pos = path.length();
799  if ( pos && string(ALL_OS_SEPARATORS).find(path.at(pos-1)) == NPOS ) {
800  // Find used path separator
801  char sep = GetPathSeparator();
802  size_t sep_pos = path.find_last_of(ALL_OS_SEPARATORS);
803  if ( sep_pos != NPOS ) {
804  sep = path.at(sep_pos);
805  }
806  path += sep;
807  }
808  // Remove leading separator in "second" part
809  string part = NStr::TruncateSpaces(second);
810  if ( part.length() > 0 &&
811  string(ALL_OS_SEPARATORS).find(part[0]) != NPOS ) {
812  part.erase(0,1);
813  }
814  // Add second part
815  path += part;
816  return path;
817 }
818 
819 
820 string CDirEntry::NormalizePath(const string& path, EFollowLinks follow_links)
821 {
822  if ( path.empty() ) {
823  return path;
824  }
825  static const char kSep[] = { DIR_SEPARATOR, '\0' };
826 
827  std::list<string> head; // already resolved to our satisfaction
828  std::list<string> tail; // to resolve afterwards
829  string current; // to resolve next
830  int link_depth = 0;
831 
832  // Delete trailing slash for all paths except similar to 'd:\'
833 # ifdef DISK_SEPARATOR
834  if ( path.find(DISK_SEPARATOR) == NPOS ) {
835  current = DeleteTrailingPathSeparator(path);
836  } else {
837  current = path;
838  }
839 # else
840  current = DeleteTrailingPathSeparator(path);
841 # endif
842 
843  if ( current.empty() ) {
844  // root dir
845  return string(1, DIR_SEPARATOR);
846  }
847 
848 #ifdef NCBI_OS_MSWIN
849  NStr::ReplaceInPlace(current, "/", "\\");
850 #endif
851 
852  while ( !current.empty() || !tail.empty() ) {
853  std::list<string> pretail;
854  if ( !current.empty() ) {
855  NStr::Split(current, kSep, pretail);
856  current.erase();
857  if (pretail.front().empty()
858 #ifdef DISK_SEPARATOR
859  || pretail.front().find(DISK_SEPARATOR) != NPOS
860 #endif
861  ) {
862  // Absolute path
863  head.clear();
864 #ifdef NCBI_OS_MSWIN
865  // Remove leading "\\?\". Replace leading "\\?\UNC\" with "\\".
866  static const char* const kUNC[] = { "", "", "?", "UNC" };
867  std::list<string>::iterator it = pretail.begin();
868  unsigned int matched = 0;
869  while (matched < 4 && it != pretail.end()
870  && !NStr::CompareNocase(*it, kUNC[matched])) {
871  ++it;
872  ++matched;
873  }
874  pretail.erase(pretail.begin(), it);
875  switch (matched) {
876  case 2: case 4: // got a UNC path (\\... or \\?\UNC\...)
877  head.push_back(kEmptyStr);
878  // fall through
879  case 1: // normal volume-less absolute path
880  head.push_back(kEmptyStr);
881  break;
882  case 3/*?*/: // normal path, absolute or relative
883  break;
884  }
885 #endif
886  }
887  tail.splice(tail.begin(), pretail);
888  }
889 
890  string next;
891  if (!tail.empty()) {
892  next = tail.front();
893  tail.pop_front();
894  }
895  if ( !head.empty() ) { // empty heads should accept anything
896  string& last = head.back();
897  if (last == DIR_CURRENT) {
898  if (!next.empty()) {
899  head.pop_back();
900  }
901 #ifdef DISK_SEPARATOR
902  } else if (!last.empty() && last[last.size()-1] == DISK_SEPARATOR) {
903  // Allow almost anything right after a volume specification
904 #endif
905  } else if (next == DIR_CURRENT) {
906  // Leave out, since we already have content
907  continue;
908  } else if (next.empty()) {
909  continue; // leave out empty components in most cases
910  } else if (next == DIR_PARENT) {
911 #ifdef DISK_SEPARATOR
912  SIZE_TYPE pos;
913 #endif
914  // Back up if possible, assuming existing path to be "physical"
915  if (last.empty()) {
916  // Already at the root; .. is a no-op
917  continue;
918 #ifdef DISK_SEPARATOR
919  } else if ((pos = last.find(DISK_SEPARATOR) != NPOS)) {
920  last.erase(pos + 1);
921 #endif
922  } else if (last != DIR_PARENT) {
923  head.pop_back();
924  continue;
925  }
926  }
927  }
928 #ifdef NCBI_OS_UNIX
929  // Is there a Windows equivalent for readlink?
930  if ( follow_links ) {
931  string s(head.empty() ? next : NStr::Join(head, string(1, DIR_SEPARATOR)) + DIR_SEPARATOR + next);
932  char buf[PATH_MAX];
933  int length = (int)readlink(s.c_str(), buf, sizeof(buf));
934  if (length > 0) {
935  current.assign(buf, length);
936  if (++link_depth >= 1024) {
937  ERR_POST_X(1, Warning << "CDirEntry::NormalizePath(): "
938  "Reached symlink depth limit " <<
939  link_depth << " when resolving " << path);
941  follow_links = eIgnoreLinks;
942  }
943  continue;
944  }
945  }
946 #endif
947  // Normal case: just append the next element to head
948  head.push_back(next);
949  }
950 
951  // Special cases
952  if ( (head.size() == 0) ||
953  (head.size() == 2 && head.front() == DIR_CURRENT && head.back().empty()) ) {
954  // current dir
955  return DIR_CURRENT;
956  }
957  if (head.size() == 1 && head.front().empty()) {
958  // root dir
959  return string(1, DIR_SEPARATOR);
960  }
961 #ifdef DISK_SEPARATOR
962  if (head.front().find(DISK_SEPARATOR) != NPOS) {
963  if ((head.size() == 2 && head.back() == DIR_CURRENT) ||
964  (head.size() == 3 && *(++head.begin()) == DIR_CURRENT && head.back().empty()) ) {
965  // root dir on drive X:
966  return head.front() + DIR_SEPARATOR;
967  }
968  }
969 #endif
970  // Compose path
971  return NStr::Join(head, string(1, DIR_SEPARATOR));
972 }
973 
974 
975 bool CDirEntry::GetMode(TMode* user_mode, TMode* group_mode,
976  TMode* other_mode, TSpecialModeBits* special) const
977 {
978  TNcbiSys_stat st;
979  if (NcbiSys_stat(_T_XCSTRING(GetPath()), &st) != 0) {
980  LOG_ERROR_ERRNO(5, "CDirEntry::GetMode(): stat() failed for: " + GetPath());
981  return false;
982  }
983  ModeFromModeT(st.st_mode, user_mode, group_mode, other_mode, special);
984  return true;
985 }
986 
987 
988 // Auxiliary macro to set/clear/replace current permissions
989 
990 #define UPDATE_PERMS(mode, perms) \
991  { \
992  _ASSERT( !F_ISSET(perms, fModeNoChange | fModeAdd) ); \
993  _ASSERT( !F_ISSET(perms, fModeNoChange | fModeRemove) ); \
994  _ASSERT( !F_ISSET(perms, fModeAdd | fModeRemove) ); \
995  \
996  if ( perms & fModeNoChange ) { \
997  \
998  } else \
999  if ( perms & fModeAdd ) { \
1000  mode |= perms; \
1001  } else \
1002  if ( perms & fModeRemove ) { \
1003  mode &= ~perms; \
1004  } \
1005  else { \
1006  mode = perms; \
1007  } \
1008  /* clear auxiliary bits */ \
1009  mode &= ~(fDefault | static_cast<TMode>( \
1010  fModeAdd | fModeRemove | fModeNoChange)); \
1011  }
1012 
1013 
1014 bool CDirEntry::SetMode(TMode user_mode, TMode group_mode,
1015  TMode other_mode, TSpecialModeBits special_mode,
1016  TSetModeFlags flags) const
1017 {
1018  // Assumption
1020 
1021  // Is this a directory ? (and processing not entry only)
1022  if ( (flags & (fDir_All | fDir_Recursive)) != eEntryOnly && IsDir(eIgnoreLinks) ) {
1023  return CDir(GetPath()).SetMode(user_mode, group_mode, other_mode, special_mode, flags);
1024  }
1025  // Other entries
1026  return SetModeEntry(user_mode, group_mode, other_mode, special_mode, flags);
1027 }
1028 
1029 
1030 bool CDirEntry::SetModeEntry(TMode user_mode, TMode group_mode,
1031  TMode other_mode, TSpecialModeBits special_mode,
1032  TSetModeFlags flags) const
1033 {
1034  // Check on defaults modes
1035  if (user_mode & fDefault) {
1036  user_mode = m_DefaultMode[eUser];
1037  }
1038  if (group_mode & fDefault) {
1039  group_mode = m_DefaultMode[eGroup];
1040  }
1041  if (other_mode & fDefault) {
1042  other_mode = m_DefaultMode[eOther];
1043  }
1044  if (special_mode == 0) {
1045  special_mode = m_DefaultMode[eSpecial];
1046  }
1047 
1048  TMode user = 0;
1049  TMode group = 0;
1050  TMode other = 0;
1051  TSpecialModeBits special = 0;
1052  TMode relative_mask = fModeNoChange | fModeAdd | fModeRemove;
1053 
1054  // relative permissions
1055 
1056  if ( (user_mode & relative_mask) ||
1057  (group_mode & relative_mask) ||
1058  (other_mode & relative_mask) ||
1059  (special_mode & relative_mask) ) {
1060 
1061  TNcbiSys_stat st;
1062  if (NcbiSys_stat(_T_XCSTRING(GetPath()), &st) != 0) {
1063  if ( (flags & fIgnoreMissing) && (errno == ENOENT) ) {
1064  return true;
1065  }
1066  LOG_ERROR_ERRNO(6, "CDirEntry::SetModeEntry(): stat() failed for: " + GetPath());
1067  return false;
1068  }
1069  ModeFromModeT(st.st_mode, &user, &group, &other);
1070  }
1071 
1072  UPDATE_PERMS(user, user_mode);
1073  UPDATE_PERMS(group, group_mode);
1074  UPDATE_PERMS(other, other_mode);
1075  UPDATE_PERMS(special, special_mode);
1076 
1077  // change permissions
1078 
1079  mode_t mode = MakeModeT(user, group, other, special);
1080 
1081  if ( NcbiSys_chmod(_T_XCSTRING(GetPath()), mode) != 0 ) {
1082  if ( (flags & fIgnoreMissing) && (errno == ENOENT) ) {
1083  return true;
1084  }
1085  LOG_ERROR_ERRNO(7, "CDirEntry::SetModeEntry(): chmod() failed: set mode " +
1086  CDirEntry::ModeToString(user, group, other, special) +
1087  " for: " + GetPath());
1088  return false;
1089  }
1090  return true;
1091 }
1092 
1093 
1094 void CDirEntry::SetDefaultModeGlobal(EType entry_type, TMode user_mode,
1095  TMode group_mode, TMode other_mode,
1096  TSpecialModeBits special)
1097 {
1098  if ( entry_type >= eUnknown ) {
1099  return;
1100  }
1101  if ( entry_type == eDir ) {
1102  if ( user_mode == fDefault ) {
1103  user_mode = fDefaultDirUser;
1104  }
1105  if ( group_mode == fDefault ) {
1106  group_mode = fDefaultDirGroup;
1107  }
1108  if ( other_mode == fDefault ) {
1109  other_mode = fDefaultDirOther;
1110  }
1111  } else {
1112  if ( user_mode == fDefault ) {
1113  user_mode = fDefaultUser;
1114  }
1115  if ( group_mode == fDefault ) {
1116  group_mode = fDefaultGroup;
1117  }
1118  if ( other_mode == fDefault ) {
1119  other_mode = fDefaultOther;
1120  }
1121  }
1122  if ( special == 0 ) {
1123  special = m_DefaultModeGlobal[entry_type][eSpecial];
1124  }
1125  m_DefaultModeGlobal[entry_type][eUser] = user_mode;
1126  m_DefaultModeGlobal[entry_type][eGroup] = group_mode;
1127  m_DefaultModeGlobal[entry_type][eOther] = other_mode;
1128  m_DefaultModeGlobal[entry_type][eSpecial] = special;
1129 }
1130 
1131 
1132 void CDirEntry::SetDefaultMode(EType entry_type, TMode user_mode,
1133  TMode group_mode, TMode other_mode,
1134  TSpecialModeBits special)
1135 {
1136  if ( user_mode == fDefault ) {
1137  user_mode = m_DefaultModeGlobal[entry_type][eUser];
1138  }
1139  if ( group_mode == fDefault ) {
1140  group_mode = m_DefaultModeGlobal[entry_type][eGroup];
1141  }
1142  if ( other_mode == fDefault ) {
1143  other_mode = m_DefaultModeGlobal[entry_type][eOther];
1144  }
1145  if ( special == 0 ) {
1146  special = m_DefaultModeGlobal[entry_type][eSpecial];
1147  }
1148  m_DefaultMode[eUser] = user_mode;
1149  m_DefaultMode[eGroup] = group_mode;
1150  m_DefaultMode[eOther] = other_mode;
1151  m_DefaultMode[eSpecial] = special;
1152 }
1153 
1154 
1155 void CDirEntry::GetDefaultModeGlobal(EType entry_type, TMode* user_mode,
1156  TMode* group_mode, TMode* other_mode,
1157  TSpecialModeBits* special)
1158 {
1159  if ( user_mode ) {
1160  *user_mode = m_DefaultModeGlobal[entry_type][eUser];
1161  }
1162  if ( group_mode ) {
1163  *group_mode = m_DefaultModeGlobal[entry_type][eGroup];
1164  }
1165  if ( other_mode ) {
1166  *other_mode = m_DefaultModeGlobal[entry_type][eOther];
1167  }
1168  if ( special ) {
1169  *special = m_DefaultModeGlobal[entry_type][eSpecial];
1170  }
1171 }
1172 
1173 
1174 void CDirEntry::GetDefaultMode(TMode* user_mode, TMode* group_mode,
1175  TMode* other_mode,
1176  TSpecialModeBits* special) const
1177 {
1178  if ( user_mode ) {
1179  *user_mode = m_DefaultMode[eUser];
1180  }
1181  if ( group_mode ) {
1182  *group_mode = m_DefaultMode[eGroup];
1183  }
1184  if ( other_mode ) {
1185  *other_mode = m_DefaultMode[eOther];
1186  }
1187  if ( special ) {
1188  *special = m_DefaultMode[eSpecial];
1189  }
1190 }
1191 
1192 
1193 mode_t CDirEntry::MakeModeT(TMode user_mode, TMode group_mode,
1194  TMode other_mode, TSpecialModeBits special)
1195 {
1196  mode_t mode = (
1197  // special bits
1198 #ifdef S_ISUID
1199  (special & fSetUID ? S_ISUID : 0) |
1200 #endif
1201 #ifdef S_ISGID
1202  (special & fSetGID ? S_ISGID : 0) |
1203 #endif
1204 #ifdef S_ISVTX
1205  (special & fSticky ? S_ISVTX : 0) |
1206 #endif
1207  // modes
1208 #if defined(S_IRUSR)
1209  (user_mode & fRead ? S_IRUSR : 0) |
1210 #elif defined(S_IREAD)
1211  (user_mode & fRead ? S_IREAD : 0) |
1212 #endif
1213 #if defined(S_IWUSR)
1214  (user_mode & fWrite ? S_IWUSR : 0) |
1215 #elif defined(S_IWRITE)
1216  (user_mode & fWrite ? S_IWRITE : 0) |
1217 #endif
1218 #if defined(S_IXUSR)
1219  (user_mode & fExecute ? S_IXUSR : 0) |
1220 #elif defined(S_IEXEC)
1221  (user_mode & fExecute ? S_IEXEC : 0) |
1222 #endif
1223 #ifdef S_IRGRP
1224  (group_mode & fRead ? S_IRGRP : 0) |
1225 #endif
1226 #ifdef S_IWGRP
1227  (group_mode & fWrite ? S_IWGRP : 0) |
1228 #endif
1229 #ifdef S_IXGRP
1230  (group_mode & fExecute ? S_IXGRP : 0) |
1231 #endif
1232 #ifdef S_IROTH
1233  (other_mode & fRead ? S_IROTH : 0) |
1234 #endif
1235 #ifdef S_IWOTH
1236  (other_mode & fWrite ? S_IWOTH : 0) |
1237 #endif
1238 #ifdef S_IXOTH
1239  (other_mode & fExecute ? S_IXOTH : 0) |
1240 #endif
1241  0);
1242  return mode;
1243 }
1244 
1245 
1247  TMode* user_mode, TMode* group_mode,
1248  TMode* other_mode, TSpecialModeBits* special)
1249 {
1250  // Owner
1251  if (user_mode) {
1252  *user_mode = (
1253 #if defined(S_IRUSR)
1254  (mode & S_IRUSR ? fRead : 0) |
1255 #elif defined(S_IREAD)
1256  (mode & S_IREAD ? fRead : 0) |
1257 #endif
1258 #if defined(S_IWUSR)
1259  (mode & S_IWUSR ? fWrite : 0) |
1260 #elif defined(S_IWRITE)
1261  (mode & S_IWRITE ? fWrite : 0) |
1262 #endif
1263 #if defined(S_IXUSR)
1264  (mode & S_IXUSR ? fExecute : 0) |
1265 #elif defined(S_IEXEC)
1266  (mode & S_IEXEC ? fExecute : 0) |
1267 #endif
1268  0);
1269  }
1270 
1271 #ifdef NCBI_OS_MSWIN
1272  if (group_mode) *group_mode = 0;
1273  if (other_mode) *other_mode = 0;
1274  if (special) *special = 0;
1275 
1276 #else
1277  // Group
1278  if (group_mode) {
1279  *group_mode = (
1280 #ifdef S_IRGRP
1281  (mode & S_IRGRP ? fRead : 0) |
1282 #endif
1283 #ifdef S_IWGRP
1284  (mode & S_IWGRP ? fWrite : 0) |
1285 #endif
1286 #ifdef S_IXGRP
1287  (mode & S_IXGRP ? fExecute : 0) |
1288 #endif
1289  0);
1290  }
1291  // Others
1292  if (other_mode) {
1293  *other_mode = (
1294 #ifdef S_IROTH
1295  (mode & S_IROTH ? fRead : 0) |
1296 #endif
1297 #ifdef S_IWOTH
1298  (mode & S_IWOTH ? fWrite : 0) |
1299 #endif
1300 #ifdef S_IXOTH
1301  (mode & S_IXOTH ? fExecute : 0) |
1302 #endif
1303  0);
1304  }
1305  // Special bits
1306  if (special) {
1307  *special = (
1308 #ifdef S_ISUID
1309  (mode & S_ISUID ? fSetUID : 0) |
1310 #endif
1311 #ifdef S_ISGID
1312  (mode & S_ISGID ? fSetGID : 0) |
1313 #endif
1314 #ifdef S_ISVTX
1315  (mode & S_ISVTX ? fSticky : 0) |
1316 #endif
1317  0);
1318  }
1319 #endif // NCBI_OS_MSWIN
1320 }
1321 
1322 
1323 // Convert permission mode to "rw[xsStT]" string.
1324 string CDirEntry::x_ModeToSymbolicString(CDirEntry::EWho who, CDirEntry::TMode mode, bool special_bit, char filler)
1325 {
1326  string out;
1327  out.reserve(3);
1328 
1329  char c;
1330  c = (mode & CDirEntry::fRead ? 'r' : filler);
1331  if (c) {
1332  out += c;
1333  }
1334  c = (mode & CDirEntry::fWrite ? 'w' : filler);
1335  if (c) {
1336  out += c;
1337  }
1338  c = filler;
1339  if ( special_bit ) {
1340  if (who == CDirEntry::eOther) {
1341  c = (mode & CDirEntry::fExecute) ? 't' : 'T';
1342  } else {
1343  c = (mode & CDirEntry::fExecute) ? 's' : 'S';
1344  }
1345  } else if (mode & CDirEntry::fExecute) {
1346  c = 'x';
1347  }
1348  if (c) {
1349  out += c;
1350  }
1351  return out;
1352 }
1353 
1354 
1355 string CDirEntry::ModeToString(TMode user_mode, TMode group_mode,
1356  TMode other_mode, TSpecialModeBits special,
1358 {
1359  string out;
1360  switch (format) {
1361  case eModeFormat_Octal:
1362  {
1363  int i = 0;
1364  if (special > 0) {
1365  out = "0000";
1366  out[0] = char(special + '0');
1367  i++;
1368  } else {
1369  out = "000";
1370  }
1371  out[i++] = char(user_mode + '0');
1372  out[i++] = char(group_mode + '0');
1373  out[i++] = char(other_mode + '0');
1374  }
1375  break;
1376  case eModeFormat_Symbolic:
1377  {
1378  out.reserve(17);
1379  out = "u=" + x_ModeToSymbolicString(eUser, user_mode, (special & fSetUID) > 0, '\0');
1380  out += ",g=" + x_ModeToSymbolicString(eGroup, group_mode, (special & fSetGID) > 0, '\0');
1381  out += ",o=" + x_ModeToSymbolicString(eOther, other_mode, (special & fSticky) > 0, '\0');
1382  }
1383  break;
1384  case eModeFormat_List:
1385  {
1386  out.reserve(9);
1387  out = x_ModeToSymbolicString(eUser, user_mode, (special & fSetUID) > 0, '-');
1388  out += x_ModeToSymbolicString(eGroup, group_mode, (special & fSetGID) > 0, '-');
1389  out += x_ModeToSymbolicString(eOther, other_mode, (special & fSticky) > 0, '-');
1390  }
1391  break;
1392  default:
1393  _TROUBLE;
1394  }
1395 
1396  return out;
1397 }
1398 
1399 
1401  TMode* user_mode, TMode* group_mode,
1402  TMode* other_mode, TSpecialModeBits* special)
1403 {
1404  if ( mode.empty() ) {
1406  return false;
1407  }
1408  if ( isdigit((unsigned char)(mode[0])) ) {
1409  // eModeFormat_Octal
1410  unsigned int oct = NStr::StringToUInt(mode, NStr::fConvErr_NoThrow, 8);
1411  if ((oct > 07777) || (!oct && errno != 0)) {
1413  return false;
1414  }
1415  if (other_mode) {
1416  *other_mode = TMode(oct & 7);
1417  }
1418  oct >>= 3;
1419  if (group_mode) {
1420  *group_mode = TMode(oct & 7);
1421  }
1422  oct >>= 3;
1423  if (user_mode) {
1424  *user_mode = TMode(oct & 7);
1425  }
1426  if (special) {
1427  oct >>= 3;
1428  *special = TSpecialModeBits(oct);
1429  }
1430 
1431  } else {
1432 
1433  if (user_mode)
1434  *user_mode = 0;
1435  if (group_mode)
1436  *group_mode = 0;
1437  if (other_mode)
1438  *other_mode = 0;
1439  if (special)
1440  *special = 0;
1441 
1442  // eModeFormat_List:
1443  if (mode.find('=') == NPOS && mode.length() == 9) {
1444  for (int i = 0; i < 3; i++) {
1445  TMode m = 0;
1446  bool is_special = false;
1447 
1448  switch (mode[i*3]) {
1449  case 'r':
1450  m |= fRead;
1451  break;
1452  case '-':
1453  break;
1454  default:
1456  return false;
1457  }
1458  switch (mode[i*3 + 1]) {
1459  case 'w':
1460  m |= fWrite;
1461  break;
1462  case '-':
1463  break;
1464  default:
1466  return false;
1467  }
1468  switch (mode[i*3 + 2]) {
1469  case 'S':
1470  case 'T':
1471  is_special = true;
1472  break;
1473  case 's':
1474  case 't':
1475  is_special = true;
1476  // fall through
1477  case 'x':
1478  m |= fExecute;
1479  break;
1480  case '-':
1481  break;
1482  default:
1484  return false;
1485  }
1486  switch (i) {
1487  case 0: // user
1488  if (user_mode)
1489  *user_mode = m;
1490  if (is_special && special)
1491  *special |= fSetUID;
1492  break;
1493  case 1: // group
1494  if (group_mode)
1495  *group_mode = m;
1496  if (is_special && special)
1497  *special |= fSetGID;
1498  break;
1499  case 2: // other
1500  if (other_mode)
1501  *other_mode = m;
1502  if (is_special && special)
1503  *special |= fSticky;
1504  break;
1505  }
1506  }
1507 
1508  // eModeFormat_Symbolic
1509  } else {
1510  std::list<string> parts;
1512  if ( parts.empty() ) {
1514  return false;
1515  }
1516  bool have_user = false;
1517  bool have_group = false;
1518  bool have_other = false;
1519 
1520  ITERATE(std::list<string>, it, parts) {
1521  string accessor, perm;
1522  if ( !NStr::SplitInTwo(*it, "=", accessor, perm) ) {
1524  return false;
1525  }
1526  TMode m = 0;
1527  bool is_special = false;
1528  // Permission mode(s) (rwx)
1529  ITERATE(string, s, perm) {
1530  switch(char(*s)) {
1531  case 'r':
1532  m |= fRead;
1533  break;
1534  case 'w':
1535  m |= fWrite;
1536  break;
1537  case 'S':
1538  case 'T':
1539  is_special = true;
1540  break;
1541  case 's':
1542  case 't':
1543  is_special = true;
1544  // fall through
1545  case 'x':
1546  m |= fExecute;
1547  break;
1548  default:
1550  return false;
1551  }
1552  }
1553  // Permission group category (ugoa)
1554  ITERATE(string, s, accessor) {
1555  switch(char(*s)) {
1556  case 'u':
1557  if (have_user) {
1559  return false;
1560  }
1561  if (user_mode)
1562  *user_mode = m;
1563  if (is_special && special)
1564  *special |= fSetUID;
1565  have_user = true;
1566  break;
1567  case 'g':
1568  if (have_group) {
1570  return false;
1571  }
1572  if (group_mode)
1573  *group_mode = m;
1574  if (is_special && special)
1575  *special |= fSetGID;
1576  have_group = true;
1577  break;
1578  case 'o':
1579  if (have_other) {
1581  return false;
1582  }
1583  if (other_mode)
1584  *other_mode = m;
1585  if (is_special && special)
1586  *special |= fSticky;
1587  have_other = true;
1588  break;
1589  case 'a':
1590  if (is_special || have_user || have_group || have_other) {
1592  return false;
1593  }
1594  have_user = true;
1595  have_group = true;
1596  have_other = true;
1597  if (user_mode)
1598  *user_mode = m;
1599  if (group_mode)
1600  *group_mode = m;
1601  if (other_mode)
1602  *other_mode = m;
1603  break;
1604  default:
1606  return false;
1607  }
1608  }
1609  }
1610  }
1611 
1612  }
1613  return true;
1614 }
1615 
1616 
1617 void CDirEntry::GetUmask(TMode* user_mode, TMode* group_mode,
1618  TMode* other_mode, TSpecialModeBits* special)
1619 {
1620 #ifdef HAVE_GETUMASK
1621  mode_t mode = getumask();
1622 #else
1623  mode_t mode = NcbiSys_umask(0);
1625 #endif //HAVE_GETUMASK
1626  ModeFromModeT(mode, user_mode, group_mode, other_mode, special);
1627 }
1628 
1629 
1630 void CDirEntry::SetUmask(TMode user_mode, TMode group_mode,
1631  TMode other_mode, TSpecialModeBits special)
1632 {
1633  mode_t mode = MakeModeT((user_mode == fDefault) ? 0 : user_mode,
1634  (group_mode == fDefault) ? 0 : group_mode,
1635  (other_mode == fDefault) ? 0 : other_mode,
1636  special);
1638 }
1639 
1640 
1641 #if defined(NCBI_OS_UNIX) && !defined(HAVE_EUIDACCESS) && !defined(EFF_ONLY_OK)
1642 
1643 static bool s_CheckAccessStat(struct stat* p, int mode)
1644 {
1645  const struct stat& st = *p;
1646  uid_t uid = geteuid();
1647 
1648  // Check user permissions
1649  if (uid == st.st_uid) {
1650  return (!(mode & R_OK) || (st.st_mode & S_IRUSR)) &&
1651  (!(mode & W_OK) || (st.st_mode & S_IWUSR)) &&
1652  (!(mode & X_OK) || (st.st_mode & S_IXUSR));
1653  }
1654 
1655  // Initialize list of group IDs for effective user
1656  int ngroups = 0;
1657  gid_t gids[NGROUPS_MAX + 1];
1658  gids[0] = getegid();
1659  ngroups = getgroups((int)(sizeof(gids)/sizeof(gids[0])) - 1, gids + 1);
1660  if (ngroups < 0) {
1661  ngroups = 1;
1662  } else {
1663  ngroups++;
1664  }
1665  for (int i = 1; i < ngroups; i++) {
1666  if (gids[i] == uid) {
1667  if (i < --ngroups) {
1668  memmove(&gids[i], &gids[i + 1], sizeof(gids[0])*(ngroups-i));
1669  }
1670  break;
1671  }
1672  }
1673  // Check group permissions
1674  for (int i = 0; i < ngroups; i++) {
1675  if (gids[i] == st.st_gid) {
1676  return (!(mode & R_OK) || (st.st_mode & S_IRGRP)) &&
1677  (!(mode & W_OK) || (st.st_mode & S_IWGRP)) &&
1678  (!(mode & X_OK) || (st.st_mode & S_IXGRP));
1679  }
1680  }
1681  // Check other permissions
1682  if ( (!(mode & R_OK) || (st.st_mode & S_IROTH)) &&
1683  (!(mode & W_OK) || (st.st_mode & S_IWOTH)) &&
1684  (!(mode & X_OK) || (st.st_mode & S_IXOTH)) ) {
1685  return true;
1686  }
1687 
1688  // Permissions not granted
1689  return false;
1690 }
1691 
1692 
1693 static bool s_CheckAccessPath(const char* path, int mode)
1694 {
1695  if (!path) {
1696  errno = 0;
1698  return false;
1699  }
1700  if (!*path) {
1701  CNcbiError::SetErrno(errno = ENOENT, path);
1702  return false;
1703  }
1704  struct stat st;
1705  if (stat(path, &st) != 0) {
1707  return false;
1708  }
1709  if (!s_CheckAccessStat(&st, mode)) {
1710  CNcbiError::SetErrno(errno = EACCES, path);
1711  return false;
1712  }
1713  // Permissions granted
1714  return true;
1715 }
1716 
1717 #endif // defined(NCBI_OS_UNIX)
1718 
1719 
1720 bool CDirEntry::CheckAccess(TMode access_mode) const
1721 {
1722 #if defined(NCBI_OS_MSWIN)
1723  // Try to get effective access rights on this file object for
1724  // the current process owner.
1725  ACCESS_MASK mask = 0;
1727  TMode perm = ( (mask & FILE_READ_DATA ? fRead : 0) |
1728  (mask & FILE_WRITE_DATA ? fWrite : 0) |
1729  (mask & FILE_EXECUTE ? fExecute : 0) );
1730  return (access_mode & perm) > 0;
1731  }
1732  return false;
1733 
1734 #elif defined(NCBI_OS_UNIX)
1735  const char* path = GetPath().c_str();
1736  int mode = F_OK;
1737 
1738  if ( access_mode & fRead) mode |= R_OK;
1739  if ( access_mode & fWrite) mode |= W_OK;
1740  if ( access_mode & fExecute) mode |= X_OK;
1741 
1742  // Use euidaccess() where possible
1743 # if defined(HAVE_EUIDACCESS)
1744  if (euidaccess(path, mode) != 0) {
1746  return false;
1747  }
1748  return true;
1749 
1750 # elif defined(EFF_ONLY_OK)
1751  // Some Unix have special flag for access() to use effective user ID.
1752  mode |= EFF_ONLY_OK;
1753  if (access(path, mode) != 0) {
1755  return false;
1756  }
1757  return true;
1758 
1759 # else
1760  // We can use access() only if effective and real user/group IDs are equal.
1761  // access() operate with real IDs only, but we should check access
1762  // for effective IDs.
1763  if (getuid() == geteuid() && getgid() == getegid()) {
1764  if (access(path, mode) != 0) {
1766  return false;
1767  }
1768  return true;
1769  }
1770  // Otherwise, try to check permissions itself.
1771  // Note, that this function is not perfect, it doesn't work with ACL,
1772  // which implementation can differ for each platform.
1773  // But in most cases it works.
1774  return s_CheckAccessPath(path, mode);
1775 
1776 # endif
1777 #endif // NCBI_OS
1778 }
1779 
1780 
1781 #ifdef NCBI_OS_MSWIN
1782 
1783 bool s_FileTimeToCTime(const FILETIME& filetime, CTime& t)
1784 {
1785  // Clear time object
1786  t.Clear();
1787 
1788  if ( !filetime.dwLowDateTime && !filetime.dwHighDateTime ) {
1789  // File time is undefined, just return "empty" time
1790  return true;
1791  }
1792  SYSTEMTIME system;
1793  FILETIME local;
1794 
1795  // Convert the file time to local time
1796  if ( !::FileTimeToLocalFileTime(&filetime, &local) ) {
1798  return false;
1799  }
1800  // Convert the local file time from UTC to system time.
1801  if ( !::FileTimeToSystemTime(&local, &system) ) {
1803  return false;
1804  }
1805 
1806  // Construct new time
1807  CTime newtime(system.wYear,
1808  system.wMonth,
1809  system.wDay,
1810  system.wHour,
1811  system.wMinute,
1812  system.wSecond,
1813  system.wMilliseconds * (kNanoSecondsPerSecond / kMilliSecondsPerSecond),
1814  CTime::eLocal,
1815  t.GetTimeZonePrecision());
1816 
1817  // And assign it
1818  if ( t.GetTimeZone() == CTime::eLocal ) {
1819  t = newtime;
1820  } else {
1821  t = newtime.GetGmtTime();
1822  }
1823  return true;
1824 }
1825 
1826 
1827 void s_UnixTimeToFileTime(time_t t, long nanosec, FILETIME& filetime)
1828 {
1829  // Note that LONGLONG is a 64-bit value
1830  LONGLONG res;
1831  // This algorithm was found in MSDN
1832  res = Int32x32To64(t, 10000000) + 116444736000000000 + nanosec/100;
1833  filetime.dwLowDateTime = (DWORD)res;
1834  filetime.dwHighDateTime = (DWORD)(res >> 32);
1835 }
1836 
1837 #endif // NCBI_OS_MSWIN
1838 
1839 
1840 bool CDirEntry::GetTime(CTime* modification,
1841  CTime* last_access,
1842  CTime* creation) const
1843 {
1844 #ifdef NCBI_OS_MSWIN
1845  HANDLE handle;
1846  WIN32_FIND_DATA buf;
1847 
1848  // Get file times using FindFile
1849  handle = ::FindFirstFile(_T_XCSTRING(GetPath()), &buf);
1850  if ( handle == INVALID_HANDLE_VALUE ) {
1851  LOG_ERROR_WIN(8, "CDirEntry::GetTime(): Cannot find: " + GetPath());
1852  return false;
1853  }
1854  ::FindClose(handle);
1855 
1856  // Convert file UTC times into CTime format
1857  if ( modification &&
1858  !s_FileTimeToCTime(buf.ftLastWriteTime, *modification) ) {
1859  LOG_ERROR(9, "CDirEntry::GetTime(): Cannot get modification time for: " + GetPath());
1860  return false;
1861  }
1862  if ( last_access &&
1863  !s_FileTimeToCTime(buf.ftLastAccessTime, *last_access) ) {
1864  LOG_ERROR(9, "CDirEntry::GetTime(): Cannot get access time for: " + GetPath());
1865  return false;
1866  }
1867  if ( creation &&
1868  !s_FileTimeToCTime(buf.ftCreationTime, *creation) ) {
1869  LOG_ERROR(9, "CDirEntry::GetTime(): Cannot get creation time for: " + GetPath());
1870  return false;
1871  }
1872  return true;
1873 
1874 #else // NCBI_OS_UNIX
1875 
1876  struct SStat st;
1877  if ( !Stat(&st) ) {
1878  LOG_ERROR(8, "CDirEntry::GetTime(): Cannot get time for: " + GetPath());
1879  return false;
1880  }
1881  if ( modification ) {
1882  modification->SetTimeT(st.orig.st_mtime);
1883  if ( st.mtime_nsec )
1884  modification->SetNanoSecond(st.mtime_nsec);
1885  }
1886  if ( last_access ) {
1887  last_access->SetTimeT(st.orig.st_atime);
1888  if ( st.atime_nsec )
1889  last_access->SetNanoSecond(st.atime_nsec);
1890  }
1891  if ( creation ) {
1892  creation->SetTimeT(st.orig.st_ctime);
1893  if ( st.ctime_nsec )
1894  creation->SetNanoSecond(st.ctime_nsec);
1895  }
1896  return true;
1897 #endif
1898 }
1899 
1900 
1901 bool CDirEntry::SetTime(const CTime* modification,
1902  const CTime* last_access,
1903  const CTime* creation) const
1904 {
1905 #ifdef NCBI_OS_MSWIN
1906  if ( !modification && !last_access && !creation ) {
1907  return true;
1908  }
1909 
1910  FILETIME x_modification, x_last_access, x_creation;
1911  LPFILETIME p_modification = NULL, p_last_access = NULL, p_creation = NULL;
1912 
1913  // Convert times to FILETIME format
1914  if ( modification ) {
1915  s_UnixTimeToFileTime(modification->GetTimeT(), modification->NanoSecond(), x_modification);
1916  p_modification = &x_modification;
1917  }
1918  if ( last_access ) {
1919  s_UnixTimeToFileTime(last_access->GetTimeT(), last_access->NanoSecond(), x_last_access);
1920  p_last_access = &x_last_access;
1921  }
1922  if ( creation ) {
1923  s_UnixTimeToFileTime(creation->GetTimeT(), creation->NanoSecond(), x_creation);
1924  p_creation = &x_creation;
1925  }
1926 
1927  // Change times
1928  HANDLE h = ::CreateFile(_T_XCSTRING(GetPath()), FILE_WRITE_ATTRIBUTES,
1929  FILE_SHARE_READ, NULL, OPEN_EXISTING,
1930  FILE_FLAG_BACKUP_SEMANTICS /*for dirs*/, NULL);
1931  if ( h == INVALID_HANDLE_VALUE ) {
1932  LOG_ERROR_WIN(10, "CDirEntry::SetTime(): Cannot open: " + GetPath());
1933  return false;
1934  }
1935  if ( !::SetFileTime(h, p_creation, p_last_access, p_modification) ) {
1936  LOG_ERROR_WIN(11, "CDirEntry::SetTime(): Cannot set new time for: " + GetPath());
1937  ::CloseHandle(h);
1938  return false;
1939  }
1940  ::CloseHandle(h);
1941 
1942  return true;
1943 
1944 #else // NCBI_OS_UNIX
1945 
1946  // Creation time doesn't used on Unix
1947  creation = NULL; /* DUMMY, to avoid warnings */
1948 
1949  if ( !modification && !last_access /*&& !creation*/ ) {
1950  return true;
1951  }
1952 
1953 # ifdef HAVE_UTIMES
1954  // Get current times
1955  CTime x_modification, x_last_access;
1956 
1957  if ( !modification || !last_access ) {
1958  if ( !GetTime(modification ? NULL : &x_modification,
1959  last_access ? NULL : &x_last_access,
1960  NULL /* creation */) ) {
1961  return false;
1962  }
1963  if (!modification) {
1964  modification = &x_modification;
1965  } else {
1966  last_access = &x_last_access;
1967  }
1968  }
1969 
1970  // Change times
1971  struct timeval tvp[2];
1972  tvp[0].tv_sec = last_access->GetTimeT();
1973  tvp[0].tv_usec = TV_USEC(last_access->NanoSecond() / (kNanoSecondsPerSecond / kMicroSecondsPerSecond));
1974  tvp[1].tv_sec = modification->GetTimeT();
1975  tvp[1].tv_usec = TV_USEC(modification->NanoSecond() / (kNanoSecondsPerSecond / kMicroSecondsPerSecond));
1976 
1977 # ifdef HAVE_LUTIMES
1978  bool ut_res = lutimes(GetPath().c_str(), tvp) == 0;
1979 # else
1980  bool ut_res = utimes(GetPath().c_str(), tvp) == 0;
1981 # endif
1982  if ( !ut_res ) {
1983  LOG_ERROR_ERRNO(12, "CDirEntry::SetTime(): Cannot change time for: " + GetPath());
1984  return false;
1985  }
1986  return true;
1987 
1988 # else
1989  // utimes() does not exist on current platform,
1990  // so use less accurate utime().
1991 
1992  // Get current times
1993  time_t x_modification, x_last_access;
1994 
1995  if ((!modification || !last_access)
1996  && !GetTimeT(&x_modification, &x_last_access, NULL /* creation */)) {
1997  LOG_ERROR(12, "CDirEntry::SetTime(): Cannot get current time for: " + GetPath());
1998  return false;
1999  }
2000 
2001  // Change times to new
2002  struct utimbuf times;
2003  times.modtime = modification ? modification->GetTimeT() : x_modification;
2004  times.actime = last_access ? last_access->GetTimeT() : x_last_access;
2005  if ( utime(GetPath().c_str(), &times) != 0 ) {
2006  LOG_ERROR_ERRNO(12, "CDirEntry::SetTime(): Cannot change time for: " + GetPath());
2007  return false;
2008  }
2009  return true;
2010 
2011 # endif // HAVE_UTIMES
2012 
2013 #endif
2014 }
2015 
2016 
2017 bool CDirEntry::GetTimeT(time_t* modification,
2018  time_t* last_access,
2019  time_t* creation) const
2020 {
2021  TNcbiSys_stat st;
2022  if (NcbiSys_stat(_T_XCSTRING(GetPath()), &st) != 0) {
2023  LOG_ERROR_ERRNO(13, "CDirEntry::GetTimeT(): stat() failed for: " + GetPath());
2024  return false;
2025  }
2026  if ( modification ) {
2027  *modification = st.st_mtime;
2028  }
2029  if ( last_access ) {
2030  *last_access = st.st_atime;
2031  }
2032  if ( creation ) {
2033  *creation = st.st_ctime;
2034  }
2035  return true;
2036 }
2037 
2038 
2039 bool CDirEntry::SetTimeT(const time_t* modification,
2040  const time_t* last_access,
2041  const time_t* creation) const
2042 {
2043 #ifdef NCBI_OS_MSWIN
2044  if ( !modification && !last_access && !creation ) {
2045  return true;
2046  }
2047 
2048  FILETIME x_modification, x_last_access, x_creation;
2049  LPFILETIME p_modification = NULL, p_last_access = NULL, p_creation = NULL;
2050 
2051  // Convert times to FILETIME format
2052  if ( modification ) {
2053  s_UnixTimeToFileTime(*modification, 0, x_modification);
2054  p_modification = &x_modification;
2055  }
2056  if ( last_access ) {
2057  s_UnixTimeToFileTime(*last_access, 0, x_last_access);
2058  p_last_access = &x_last_access;
2059  }
2060  if ( creation ) {
2061  s_UnixTimeToFileTime(*creation, 0, x_creation);
2062  p_creation = &x_creation;
2063  }
2064 
2065  // Change times
2066  HANDLE h = ::CreateFile(_T_XCSTRING(GetPath()), FILE_WRITE_ATTRIBUTES,
2067  FILE_SHARE_READ, NULL, OPEN_EXISTING,
2068  FILE_FLAG_BACKUP_SEMANTICS /*for dirs*/, NULL);
2069  if ( h == INVALID_HANDLE_VALUE ) {
2070  LOG_ERROR_WIN(14, "CDirEntry::SetTimeT(): Cannot open: " + GetPath());
2071  return false;
2072  }
2073  if ( !::SetFileTime(h, p_creation, p_last_access, p_modification) ) {
2074  LOG_ERROR_WIN(15, "CDirEntry::SetTimeT(): Cannot change time for: " + GetPath());
2075  ::CloseHandle(h);
2076  return false;
2077  }
2078  ::CloseHandle(h);
2079 
2080  return true;
2081 
2082 #else // NCBI_OS_UNIX
2083 
2084  // Creation time doesn't used on Unix
2085  creation = NULL; /* DUMMY, to avoid warnings */
2086 
2087  if ( !modification && !last_access /*&& !creation*/ )
2088  return true;
2089 
2090  time_t x_modification, x_last_access;
2091  if ((!modification || !last_access)
2092  && !GetTimeT(&x_modification, &x_last_access, NULL /* creation */) ) {
2093  LOG_ERROR(15, "CDirEntry::SetTimeT(): Cannot get current time for: " + GetPath());
2094  return false;
2095  }
2096 
2097  // Change times to new
2098  struct utimbuf times;
2099  times.modtime = modification ? *modification : x_modification;
2100  times.actime = last_access ? *last_access : x_last_access;
2101  if ( utime(GetPath().c_str(), &times) != 0 ) {
2102  LOG_ERROR_ERRNO(15, "CDirEntry::SetTimeT(): Cannot change time for: " + GetPath());
2103  return false;
2104  }
2105  return true;
2106 
2107 #endif
2108 }
2109 
2110 
2111 bool CDirEntry::Stat(struct SStat *buffer, EFollowLinks follow_links) const
2112 {
2113  if ( !buffer ) {
2114  errno = EFAULT;
2115  LOG_ERROR_ERRNO(16, "CDirEntry::Stat(): NULL stat buffer passed for: " + GetPath());
2116  return false;
2117  }
2118  int errcode;
2119 #ifdef NCBI_OS_MSWIN
2120  errcode = NcbiSys_stat(_T_XCSTRING(GetPath()), &buffer->orig);
2121 #else // NCBI_OS_UNIX
2122  if (follow_links == eFollowLinks) {
2123  errcode = stat(GetPath().c_str(), &buffer->orig);
2124  } else {
2125  errcode = lstat(GetPath().c_str(), &buffer->orig);
2126  }
2127 #endif
2128  if (errcode != 0) {
2129  LOG_ERROR_ERRNO(16, "CDirEntry::Stat(): stat() failed for: " + GetPath());
2130  return false;
2131  }
2132 
2133  // Assign additional fields
2134  buffer->atime_nsec = 0;
2135  buffer->mtime_nsec = 0;
2136  buffer->ctime_nsec = 0;
2137 
2138 #ifdef NCBI_OS_UNIX
2139  // UNIX:
2140  // Some systems have additional fields in the stat structure to store
2141  // nanoseconds. If you know one more platform which have nanoseconds
2142  // support for file times, add it here.
2143 
2144 # if !defined(__GLIBC_PREREQ)
2145 # define __GLIBC_PREREQ(x, y) 0
2146 # endif
2147 
2148 # if defined(NCBI_OS_LINUX) && __GLIBC_PREREQ(2,3)
2149 # if defined(__USE_MISC)
2150  buffer->atime_nsec = buffer->orig.st_atim.tv_nsec;
2151  buffer->mtime_nsec = buffer->orig.st_mtim.tv_nsec;
2152  buffer->ctime_nsec = buffer->orig.st_ctim.tv_nsec;
2153 # else
2154  buffer->atime_nsec = buffer->orig.st_atimensec;
2155  buffer->mtime_nsec = buffer->orig.st_mtimensec;
2156  buffer->ctime_nsec = buffer->orig.st_ctimensec;
2157 # endif
2158 # endif
2159 
2160 # if defined(NCBI_OS_SOLARIS)
2161 # if !defined(_XOPEN_SOURCE) && !defined(_POSIX_C_SOURCE) || \
2162  defined(__EXTENSIONS__)
2163  buffer->atime_nsec = buffer->orig.st_atim.tv_nsec;
2164  buffer->mtime_nsec = buffer->orig.st_mtim.tv_nsec;
2165  buffer->ctime_nsec = buffer->orig.st_ctim.tv_nsec;
2166 # else
2167  buffer->atime_nsec = buffer->orig.st_atim.__tv_nsec;
2168  buffer->mtime_nsec = buffer->orig.st_mtim.__tv_nsec;
2169  buffer->ctime_nsec = buffer->orig.st_ctim.__tv_nsec;
2170 # endif
2171 # endif
2172 
2173 # if defined(NCBI_OS_BSD) || defined(NCBI_OS_DARWIN)
2174 # if defined(_POSIX_SOURCE)
2175  buffer->atime_nsec = buffer->orig.st_atimensec;
2176  buffer->mtime_nsec = buffer->orig.st_mtimensec;
2177  buffer->ctime_nsec = buffer->orig.st_ctimensec;
2178 # else
2179  buffer->atime_nsec = buffer->orig.st_atimespec.tv_nsec;
2180  buffer->mtime_nsec = buffer->orig.st_mtimespec.tv_nsec;
2181  buffer->ctime_nsec = buffer->orig.st_ctimespec.tv_nsec;
2182 # endif
2183 # endif
2184 
2185 # if defined(NCBI_OS_IRIX)
2186 # if defined(tv_sec)
2187  buffer->atime_nsec = buffer->orig.st_atim.__tv_nsec;
2188  buffer->mtime_nsec = buffer->orig.st_mtim.__tv_nsec;
2189  buffer->ctime_nsec = buffer->orig.st_ctim.__tv_nsec;
2190 # else
2191  buffer->atime_nsec = buffer->orig.st_atim.tv_nsec;
2192  buffer->mtime_nsec = buffer->orig.st_mtim.tv_nsec;
2193  buffer->ctime_nsec = buffer->orig.st_ctim.tv_nsec;
2194 # endif
2195 # endif
2196 
2197 #endif // NCBI_OS_UNIX
2198 
2199  return true;
2200 }
2201 
2202 
2204 {
2205  TNcbiSys_stat st;
2206  int errcode;
2207 
2208 #if defined(NCBI_OS_MSWIN)
2209  errcode = NcbiSys_stat(_T_XCSTRING(GetPath()), &st);
2210  if (errcode != 0) {
2211  // Make additional checks for UNC paths, because
2212  // stat() cannot handle path that looks like \\Server\Share.
2213  if (s_Win_IsNetworkPath(GetPath())) {
2214  DWORD attr = ::GetFileAttributes(_T_XCSTRING(GetPath()));
2215  if (attr == INVALID_FILE_ATTRIBUTES) {
2216  // Don't report an error here, just set CNcbiError.
2217  // eUnknown is a legit return value.
2219  return eUnknown;
2220  }
2221  if ( F_ISSET(attr, FILE_ATTRIBUTE_DIRECTORY) ) {
2222  return eDir;
2223  }
2224  return eFile;
2225  }
2226  }
2227 #else // NCBI_OS_UNIX
2228  if (follow == eFollowLinks) {
2229  errcode = stat(GetPath().c_str(), &st);
2230  } else {
2231  errcode = lstat(GetPath().c_str(), &st);
2232  }
2233 #endif
2234  if (errcode != 0) {
2235  // Don't report an error here, just set CNcbiError.
2236  // eUnknown is a legit return value.
2238  return eUnknown;
2239  }
2240  return GetType(st);
2241 }
2242 
2243 
2244 /// Test macro for file types
2245 #define NCBI_IS_TYPE(mode, mask) (((mode) & S_IFMT) == (mask))
2246 
2248 {
2249  unsigned int mode = (unsigned int)st.st_mode;
2250 
2251 #ifdef S_ISDIR
2252  if (S_ISDIR(mode))
2253 #else
2254  if (NCBI_IS_TYPE(mode, S_IFDIR))
2255 #endif
2256  return eDir;
2257 
2258 #ifdef S_ISCHR
2259  if (S_ISCHR(mode))
2260 #else
2261  if (NCBI_IS_TYPE(mode, S_IFCHR))
2262 #endif
2263  return eCharSpecial;
2264 
2265 #ifdef NCBI_OS_MSWIN
2266  if (NCBI_IS_TYPE(mode, _S_IFIFO))
2267  return ePipe;
2268 #else
2269  // NCBI_OS_UNIX
2270 # ifdef S_ISFIFO
2271  if (S_ISFIFO(mode))
2272 # else
2273  if (NCBI_IS_TYPE(mode, S_IFIFO))
2274 # endif
2275  return ePipe;
2276 
2277 # ifdef S_ISLNK
2278  if (S_ISLNK(mode))
2279 # else
2280  if (NCBI_IS_TYPE(mode, S_IFLNK))
2281 # endif
2282  return eLink;
2283 
2284 # ifdef S_ISSOCK
2285  if (S_ISSOCK(mode))
2286 # else
2287  if (NCBI_IS_TYPE(mode, S_IFSOCK))
2288 # endif
2289  return eSocket;
2290 
2291 # ifdef S_ISBLK
2292  if (S_ISBLK(mode))
2293 # else
2294  if (NCBI_IS_TYPE(mode, S_IFBLK))
2295 # endif
2296  return eBlockSpecial;
2297 
2298 # ifdef S_IFDOOR
2299  // only Solaris seems to have this one
2300 # ifdef S_ISDOOR
2301  if (S_ISDOOR(mode))
2302 # else
2303  if (NCBI_IS_TYPE(mode, S_IFDOOR))
2304 # endif
2305  return eDoor;
2306 # endif
2307 
2308 #endif //NCBI_OS_MSWIN
2309 
2310  // Check on regular file last
2311 #ifdef S_ISREG
2312  if (S_ISREG(mode))
2313 #else
2314  if (NCBI_IS_TYPE(mode, S_IFREG))
2315 #endif
2316  return eFile;
2317 
2318  return eUnknown;
2319 }
2320 
2321 
2322 #if defined(NCBI_OS_MSWIN)
2323 
2324 // Windows-specific implementation. See default implementation in the .hpp file
2325 bool CDirEntry::Exists(void) const
2326 {
2327  HANDLE h = ::CreateFile(_T_XCSTRING(GetPath()),
2328  GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2329  OPEN_EXISTING,
2330  FILE_FLAG_BACKUP_SEMANTICS /*for dirs*/, NULL);
2331  if (h == INVALID_HANDLE_VALUE) {
2332  // Don't report an error here, just set CNcbiError.
2334  return false;
2335  }
2336  ::CloseHandle(h);
2337  return true;
2338 }
2339 
2340 #endif
2341 
2342 
2343 string CDirEntry::LookupLink(void) const
2344 {
2345 #ifdef NCBI_OS_MSWIN
2346  return kEmptyStr;
2347 
2348 #else // NCBI_OS_UNIX
2349  char buf[PATH_MAX];
2350  string name;
2351  int length = (int)readlink(_T_XCSTRING(GetPath()), buf, sizeof(buf));
2352  if (length > 0) {
2353  name.assign(buf, length);
2354  }
2355  return name;
2356 #endif
2357 }
2358 
2359 
2361 {
2362 #ifdef NCBI_OS_MSWIN
2363  // Not implemented
2364  return;
2365 #endif
2366  string prev;
2367  while ( IsLink() ) {
2368  string name = LookupLink();
2369  if ( name.empty() || name == prev ) {
2370  return;
2371  }
2372  prev = name;
2373  if ( IsAbsolutePath(name) ) {
2374  Reset(name);
2375  } else {
2376  string path = MakePath(GetDir(), name);
2377  if (normalize == eNormalizePath) {
2378  Reset(NormalizePath(path));
2379  } else {
2380  Reset(path);
2381  }
2382  }
2383  }
2384 }
2385 
2386 
2388 {
2389 #ifdef NCBI_OS_MSWIN
2390  // Not implemented
2391  return;
2392 #endif
2393  // Dereference each path components starting from last one
2395 
2396  // Get dir and file names
2397  string path = entry.GetPath();
2398  size_t pos = path.find_last_of(ALL_SEPARATORS);
2399  if (pos == NPOS) {
2400  return;
2401  }
2402  string filename = path.substr(pos+1);
2403  string dirname = path.substr(0, pos);
2404  if ( dirname.empty() ) {
2405  return;
2406  }
2407  // Dereference path one level up
2408  entry.Reset(dirname);
2409  s_DereferencePath(entry);
2410  // Create new path
2411  entry.Reset(CDirEntry::MakePath(entry.GetPath(), filename));
2412 }
2413 
2414 
2416 {
2417 #ifdef NCBI_OS_MSWIN
2418  // Not implemented
2419  return;
2420 #endif
2421  // Use s_DereferencePath() recursively and normalize result only once
2422  CDirEntry e(GetPath());
2423  s_DereferencePath(e);
2424  Reset(NormalizePath(e.GetPath()));
2425 }
2426 
2427 
2428 bool CDirEntry::Copy(const string& path, TCopyFlags flags, size_t buf_size)
2429  const
2430 {
2431  // Dereference link if specified
2432  bool follow = F_ISSET(flags, fCF_FollowLinks);
2433  EType type = GetType(follow ? eFollowLinks : eIgnoreLinks);
2434  switch (type) {
2435  case eFile:
2436  return CFile(GetPath()).Copy(path, flags, buf_size);
2437  case eDir:
2438  return CDir(GetPath()).Copy(path, flags, buf_size);
2439  case eLink:
2440  return CSymLink(GetPath()).Copy(path, flags, buf_size);
2441  case eUnknown:
2442  {
2444  return false;
2445  }
2446  default:
2448  break;
2449  }
2450  // We "don't know" how to copy entry of other type, by default.
2451  // Use overloaded Copy() method in derived classes.
2453 }
2454 
2455 
2456 bool CDirEntry::Rename(const string& newname, TRenameFlags flags)
2457 {
2458  CDirEntry src(*this);
2459  CDirEntry dst(newname);
2460 
2461  // Dereference links
2462  if ( F_ISSET(flags, fRF_FollowLinks) ) {
2463  src.DereferenceLink();
2464  dst.DereferenceLink();
2465  }
2466  // The source entry must exists
2467  EType src_type = src.GetType();
2468  if ( src_type == eUnknown ) {
2469  LOG_ERROR_NCBI(17, "CDirEntry::Rename():"
2470  " Source path does not exist: " + src.GetPath(),
2472  return false;
2473  }
2474 
2475  // Try to "move" in one atomic operation if possible to avoid race
2476  // conditions between check on destination existence and renaming.
2477 
2478 #ifdef NCBI_OS_MSWIN
2479  // On Windows we can try to move file or whole directory, even across volumes
2480  if ( ::MoveFileEx(_T_XCSTRING(src.GetPath()),
2481  _T_XCSTRING(dst.GetPath()), MOVEFILE_COPY_ALLOWED) ) {
2482  Reset(newname);
2483  return true;
2484  }
2485 #else
2486  // On Unix we can use "link" technique for files.
2487 
2488  // link() have different behavior on some flavors of Unix
2489  // regarding symbolic links handling, and can automatically
2490  // dereference both source and destination as POSIX required,
2491  // or not (Linux with kernel 2.0 and up behavior).
2492  // We need to rename symlink itself, if not dereferenced yet
2493  // (see fRF_FollowLinks), and the destination should remain
2494  // a symlink. So just dont use link() in this case and,
2495  // fall back to regular rename() instead.
2496 
2497  if ( src_type == eFile &&
2498  link(_T_XCSTRING(src.GetPath()),
2499  _T_XCSTRING(dst.GetPath())) == 0 ) {
2500  // Hard link successfully created, so we can just remove source file
2501  if ( src.RemoveEntry() ) {
2502  Reset(newname);
2503  return true;
2504  }
2505  }
2506 #endif
2507  // On error, symlink, or destination existence --
2508  // continue on regular processing below
2509 
2510  EType dst_type = dst.GetType();
2511 
2512  // If destination exists...
2513 
2514  if ( dst_type != eUnknown ) {
2515  // Can rename entries with different types?
2516  if ( F_ISSET(flags, fRF_EqualTypes) && (src_type != dst_type) ) {
2517  LOG_ERROR_NCBI(18, "CDirEntry::Rename():"
2518  " Both source and destination exist and have different types: "
2519  + src.GetPath() + " and " + dst.GetPath(),
2521  return false;
2522  }
2523  // Can overwrite entry?
2524  if ( !F_ISSET(flags, fRF_Overwrite) ) {
2525  LOG_ERROR_NCBI(19, "CDirEntry::Rename(): Destination path already exists: "
2526  + dst.GetPath(),
2528  return false;
2529  }
2530  // Rename only if destination is older, otherwise just remove source
2531  if ( F_ISSET(flags, fRF_Update) && !src.IsNewer(dst.GetPath(), 0)) {
2532  return src.Remove();
2533  }
2534  // Backup destination entry first
2535  if ( F_ISSET(flags, fRF_Backup) ) {
2536  // Use new CDirEntry object instead of 'dst', because its path
2537  // will be changed after backup
2538  CDirEntry dst_tmp(dst);
2539  if ( !dst_tmp.Backup(GetBackupSuffix(), eBackup_Rename) ) {
2540  LOG_ERROR(20, "CDirEntry::Rename(): Cannot backup: " + dst.GetPath());
2541  return false;
2542  }
2543  }
2544  // Overwrite destination entry
2545  if ( F_ISSET(flags, fRF_Overwrite) ) {
2546  if ( dst.Exists() ) {
2547  dst.Remove();
2548  }
2549  }
2550  }
2551 
2552  // On some platform rename() fails if destination entry exists,
2553  // on others it can overwrite destination.
2554  // For consistency return FALSE if destination already exists.
2555  if ( dst.Exists() ) {
2556  // this means Remove() has failed, and error is set already
2557  LOG_ERROR(21, "CDirEntry::Rename(): Destination path exists: " + GetPath());
2558  return false;
2559  }
2560 
2561  // Rename
2562 
2563  if ( NcbiSys_rename(_T_XCSTRING(src.GetPath()),
2564  _T_XCSTRING(dst.GetPath())) != 0 ) {
2565 #ifdef NCBI_OS_MSWIN
2566  if ( errno != EACCES ) {
2567 #else
2568  if ( errno != EXDEV ) {
2569 #endif
2570  LOG_ERROR_ERRNO(21, "CDirEntry::Rename(): rename() failed for " + GetPath());
2571  return false;
2572  }
2573  // Note that rename() fails in the case of cross-device renaming.
2574  // So, try to make a copy and remove the original later.
2575  unique_ptr<CDirEntry> e(CDirEntry::CreateObject(src_type, src.GetPath()));
2576  if ( !e->Copy(dst.GetPath(), fCF_Recursive | fCF_PreserveAll ) ) {
2577  LOG_ERROR(102, "CDirEntry::Rename(): Renaming via Copy() failed for " + GetPath());
2578  unique_ptr<CDirEntry> tmp(CDirEntry::CreateObject(src_type, dst.GetPath()));
2579  tmp->Remove(eRecursive);
2580  return false;
2581  }
2582  // Remove 'src'
2583  if ( !e->Remove(eRecursive) ) {
2584  LOG_ERROR(102, "CDirEntry::Rename(): Renaming via Copy() failed for " + GetPath());
2585  // Do not delete 'dst' here because in case of directories the
2586  // source may be already partially removed, so we can lost data.
2587  return false;
2588  }
2589  }
2590  Reset(newname);
2591  return true;
2592 }
2593 
2594 
2596 {
2597  // Is this a directory ? (and processing not entry only)
2598  if ( (flags & (fDir_All | fDir_Recursive)) != eEntryOnly && IsDir(eIgnoreLinks) ) {
2599  return CDir(GetPath()).Remove(flags);
2600  }
2601  // Other entries
2602  return RemoveEntry(flags);
2603 }
2604 
2605 
2607 {
2608 
2609  if ( NcbiSys_remove(_T_XCSTRING(GetPath())) != 0 ) {
2610  switch (errno) {
2611  case ENOENT:
2612  if ( flags & fIgnoreMissing )
2613  return true;
2614  break;
2615 
2616 #if defined(NCBI_OS_MSWIN)
2617  case EACCES:
2618  if ( NCBI_PARAM_TYPE(NCBI, DeleteReadOnlyFiles)::GetDefault() ) {
2620  if ( NcbiSys_remove(_T_XCSTRING(GetPath())) == 0 ) {
2621  return true;
2622  }
2623  }
2624  errno = EACCES;
2625 #endif
2626  }
2627  LOG_ERROR_ERRNO(22, "CDirEntry::RemoveEntry(): remove() failed for: " + GetPath());
2628  return false;
2629  }
2630  return true;
2631 }
2632 
2633 
2635  TCopyFlags copyflags, size_t copybufsize)
2636 {
2637  string backup_name = DeleteTrailingPathSeparator(GetPath()) +
2638  (suffix.empty() ? string(GetBackupSuffix()) : suffix);
2639  switch (mode) {
2640  case eBackup_Copy:
2641  {
2642  TCopyFlags flags = copyflags;
2643  flags &= ~(fCF_Update | fCF_Backup);
2645  return Copy(backup_name, flags, copybufsize);
2646  }
2647  case eBackup_Rename:
2648  return Rename(backup_name, fRF_Overwrite);
2649  default:
2650  _TROUBLE;
2651  }
2652  // Unreachable
2653  return false;
2654 }
2655 
2656 
2657 bool CDirEntry::IsNewer(const string& entry_name, TIfAbsent2 if_absent) const
2658 {
2659  CDirEntry entry(entry_name);
2660  CTime this_time;
2661  CTime entry_time;
2662  int v = 0;
2663 
2664  if ( !GetTime(&this_time) ) {
2665  v += 1;
2666  }
2667  if ( !entry.GetTime(&entry_time) ) {
2668  v += 2;
2669  }
2670  if ( v == 0 ) {
2671  return this_time > entry_time;
2672  }
2673  if ( if_absent ) {
2674  switch(v) {
2675  case 1: // NoThis - HasPath
2676  if ( if_absent &
2678  return (if_absent & fNoThisHasPath_Newer) > 0;
2679  break;
2680  case 2: // HasThis - NoPath
2681  if ( if_absent &
2683  return (if_absent & fHasThisNoPath_Newer) > 0;
2684  break;
2685  case 3: // NoThis - NoPath
2686  if ( if_absent &
2688  return (if_absent & fNoThisNoPath_Newer) > 0;
2689  break;
2690  }
2691  }
2692  // throw an exception by default
2693  NCBI_THROW(CFileException, eNotExists,
2694  "Directory entry does not exist");
2695  /*NOTREACHED*/
2696  return false;
2697 }
2698 
2699 
2700 bool CDirEntry::IsNewer(time_t tm, EIfAbsent if_absent) const
2701 {
2702  time_t current;
2703  if ( !GetTimeT(&current) ) {
2704  switch(if_absent) {
2705  case eIfAbsent_Newer:
2706  return true;
2707  case eIfAbsent_NotNewer:
2708  return false;
2709  case eIfAbsent_Throw:
2710  default:
2711  NCBI_THROW(CFileException, eNotExists,
2712  "Directory entry does not exist");
2713  }
2714  }
2715  return current > tm;
2716 }
2717 
2718 
2719 bool CDirEntry::IsNewer(const CTime& tm, EIfAbsent if_absent) const
2720 {
2721  CTime current;
2722  if ( !GetTime(&current) ) {
2723  switch(if_absent) {
2724  case eIfAbsent_Newer:
2725  return true;
2726  case eIfAbsent_NotNewer:
2727  return false;
2728  case eIfAbsent_Throw:
2729  default:
2730  NCBI_THROW(CFileException, eNotExists,
2731  "Directory entry does not exist");
2732  }
2733  }
2734  return current > tm;
2735 }
2736 
2737 
2738 bool CDirEntry::IsIdentical(const string& entry_name,
2739  EFollowLinks follow_links) const
2740 {
2741 #if defined(NCBI_OS_UNIX)
2742  struct SStat st1, st2;
2743  if ( !Stat(&st1, follow_links) ) {
2744  LOG_ERROR(23, "CDirEntry::IsIdentical(): Cannot find: " + GetPath());
2745  return false;
2746  }
2747  if ( !CDirEntry(entry_name).Stat(&st2, follow_links) ) {
2748  LOG_ERROR(23, "CDirEntry::IsIdentical(): Cannot find: " + entry_name);
2749  return false;
2750  }
2751  return st1.orig.st_dev == st2.orig.st_dev &&
2752  st1.orig.st_ino == st2.orig.st_ino;
2753 #else
2754  return NormalizePath(GetPath(), follow_links) ==
2755  NormalizePath(entry_name, follow_links);
2756 #endif
2757 }
2758 
2759 
2760 bool CDirEntry::GetOwner(string* owner, string* group,
2761  EFollowLinks follow,
2762  unsigned int* uid, unsigned int* gid) const
2763 {
2764  if ( uid ) *uid = 0;
2765  if ( gid ) *gid = 0;
2766 
2767  if ( !owner && !group ) {
2768  LOG_ERROR_NCBI(24, "CDirEntry::GetOwner(): Parameters are NULL for: " + GetPath(), CNcbiError::eInvalidArgument);
2769  return false;
2770  }
2771 
2772 #if defined(NCBI_OS_MSWIN)
2773 
2774  bool res = CWinSecurity::GetFileOwner(GetPath(), owner, group, uid, gid);
2775  if (!res) {
2776  // CWinSecurity already set CNcbiError
2777  LOG_ERROR(24, "CDirEntry::GetOwner(): Unable to get owner for: " + GetPath());
2778  }
2779  return res;
2780 
2781 #elif defined(NCBI_OS_UNIX)
2782  struct stat st;
2783  int errcode;
2784 
2785  if ( follow == eFollowLinks ) {
2786  errcode = stat(GetPath().c_str(), &st);
2787  } else {
2788  errcode = lstat(GetPath().c_str(), &st);
2789  }
2790  if ( errcode != 0 ) {
2791  LOG_ERROR_ERRNO(24, "CDirEntry::GetOwner(): stat() failed for: " + GetPath());
2792  return false;
2793  }
2794  if ( uid ) {
2795  *uid = st.st_uid;
2796  }
2797  if ( gid ) {
2798  *gid = st.st_gid;
2799  }
2800  if ( owner ) {
2801  CUnixFeature::GetUserNameByUID(st.st_uid).swap(*owner);
2802  if (owner->empty()) {
2803  NStr::NumericToString(*owner, st.st_uid, 0 /* flags */, 10);
2804  }
2805  }
2806  if ( group ) {
2807  CUnixFeature::GetGroupNameByGID(st.st_gid).swap(*group);
2808  if (group->empty()) {
2809  NStr::NumericToString(*group, st.st_gid, 0 /* flags */, 10);
2810  }
2811  }
2812  return true;
2813 
2814 #endif
2815 }
2816 
2817 
2818 bool CDirEntry::SetOwner(const string& owner, const string& group,
2819  EFollowLinks follow,
2820  unsigned int* uid, unsigned int* gid) const
2821 {
2822  if ( uid ) *uid = (unsigned int)(-1);
2823  if ( gid ) *gid = (unsigned int)(-1);
2824 
2825  if ( owner.empty() && group.empty() ) {
2826  LOG_ERROR_NCBI(103, "CDirEntry::SetOwner(): Parameters are empty for: " + GetPath(), CNcbiError::eInvalidArgument);
2827  return false;
2828  }
2829 
2830 #if defined(NCBI_OS_MSWIN)
2831  bool res = CWinSecurity::SetFileOwner(GetPath(), owner, group, uid, gid);
2832  if (!res) {
2833  // CWinSecurity already set CNcbiError
2834  LOG_ERROR(104, "CDirEntry::SetOwner(): Unable to set owner \""
2835  + owner + ':' + group + "\" for: " + GetPath());
2836  }
2837  return res;
2838 
2839 #elif defined(NCBI_OS_UNIX)
2840 
2841  uid_t temp_uid;
2842  if ( !owner.empty() ) {
2843  temp_uid = CUnixFeature::GetUserUIDByName(owner);
2844  if (temp_uid == (uid_t)(-1)){
2846  unsigned int temp;
2847  if (!NStr::StringToNumeric(owner, &temp, NStr::fConvErr_NoThrow, 0)) {
2848  LOG_ERROR(25, "CDirEntry::SetOwner(): Invalid owner name \""
2849  + owner + "\" for: " + GetPath());
2850  return false;
2851  }
2852  temp_uid = (uid_t) temp;
2853  }
2854  if ( uid ) {
2855  *uid = temp_uid;
2856  }
2857  } else {
2858  temp_uid = (uid_t)(-1); // no change
2859  }
2860 
2861  gid_t temp_gid;
2862  if ( !group.empty() ) {
2863  temp_gid = CUnixFeature::GetGroupGIDByName(group);
2864  if (temp_gid == (gid_t)(-1)) {
2866  unsigned int temp;
2867  if (!NStr::StringToNumeric(group, &temp, NStr::fConvErr_NoThrow, 0)) {
2868  LOG_ERROR(26, "CDirEntry::SetOwner(): Invalid group name \""
2869  + group + "\" for: " + GetPath());
2870  return false;
2871  }
2872  temp_gid = (gid_t) temp;
2873  }
2874  if ( gid ) {
2875  *gid = temp_gid;
2876  }
2877  } else {
2878  temp_gid = (gid_t)(-1); // no change
2879  }
2880 
2881  if (follow == eFollowLinks || GetType(eIgnoreLinks) != eLink) {
2882  if ( chown(GetPath().c_str(), temp_uid, temp_gid) ) {
2883  LOG_ERROR_ERRNO(27, "CDirEntry::SetOwner(): Cannot change owner \""
2884  + owner + ':' + group + "\" for: " + GetPath());
2885  return false;
2886  }
2887  }
2888 # if defined(HAVE_LCHOWN)
2889  else {
2890  if ( lchown(GetPath().c_str(), temp_uid, temp_gid) ) {
2891  LOG_ERROR_ERRNO(28, "CDirEntry::SetOwner(): Cannot change symlink owner \""
2892  + owner + ':' + group + "\" for: " + GetPath());
2893  return false;
2894  }
2895  }
2896 # endif
2897 
2898  return true;
2899 #endif
2900 }
2901 
2902 
2904 {
2905 #if defined(NCBI_OS_MSWIN) || defined(NCBI_OS_UNIX)
2907 #else
2908  if (mode == eTmpFileCreate) {
2909  ERR_POST_X(2, Warning <<
2910  "Temporary file cannot be auto-created on this platform, "
2911  "return its name only");
2912  }
2913  TXChar* filename = NcbiSys_tempnam(0,0);
2914  if ( !filename ) {
2915  LOG_ERROR_ERRNO(91, "CDirEntry::GetTmpName(): tempnam() failed");
2916  return kEmptyStr;
2917  }
2918  string res(_T_CSTRING(filename));
2919  free(filename);
2920  return res;
2921 #endif
2922 }
2923 
2924 
2925 #if !defined(NCBI_OS_UNIX) && !defined(NCBI_OS_MSWIN)
2926 static string s_StdGetTmpName(const char* dir, const char* prefix)
2927 {
2928  char* filename = tempnam(dir, prefix);
2929  if ( !filename ) {
2930  LOG_ERROR_ERRNO(92, "CDirEntry::s_StdGetTmpName(): tempnam() failed");
2931  return kEmptyStr;
2932  }
2933  string str(filename);
2934  free(filename);
2935  return str;
2936 }
2937 #endif
2938 
2939 
2940 string CDirEntry::GetTmpNameEx(const string& dir,
2941  const string& prefix,
2943 {
2944  CFileIO temp_file;
2945  temp_file.CreateTemporary(dir, prefix,
2948  temp_file.Close();
2949  return temp_file.GetPathname();
2950 }
2951 
2952 
2953 class CTmpStream : public fstream
2954 {
2955 public:
2956 
2957  CTmpStream(const char* s, IOS_BASE::openmode mode) : fstream(s, mode)
2958  {
2959  m_FileName = s;
2960  // Try to remove file and OS will automatically delete it after
2961  // the last file descriptor to it is closed (works only on UNIXes)
2962  CFile(m_FileName).Remove();
2963  }
2964 
2965 #if defined(NCBI_OS_MSWIN)
2966  CTmpStream(const char* s, FILE* file) : fstream(file)
2967  {
2968  m_FileName = s;
2969  }
2970 #endif
2971 
2972  virtual ~CTmpStream(void)
2973  {
2974  close();
2975  if ( !m_FileName.empty() ) {
2976  CFile(m_FileName).Remove();
2977  }
2978  }
2979 
2980 protected:
2981  string m_FileName; // Temporary file name
2982 };
2983 
2984 
2985 fstream* CDirEntry::CreateTmpFile(const string& filename,
2986  ETextBinary text_binary,
2987  EAllowRead allow_read)
2988 {
2989  string tmpname = filename.empty() ? GetTmpName(eTmpFileCreate) : filename;
2990  if ( tmpname.empty() ) {
2991  LOG_ERROR(29, "CDirEntry::CreateTmpFile(): Cannot get temporary file name");
2992  return 0;
2993  }
2994 #if defined(NCBI_OS_MSWIN)
2995  // Open file manually, because we cannot say to fstream
2996  // to use some specific flags for file opening.
2997  // MS Windows should delete created file automatically
2998  // after closing all opened file descriptors.
2999 
3000  // We cannot enable "only write" mode here,
3001  // so ignore 'allow_read' flag.
3002  // Specify 'TD' (_O_SHORT_LIVED | _O_TEMPORARY)
3003  char mode[6] = "w+TDb";
3004  if (text_binary != eBinary) {
3005  mode[4] = '\0';
3006  }
3007  FILE* file = NcbiSys_fopen(_T_XCSTRING(tmpname), _T_XCSTRING(mode));
3008  if ( !file ) {
3009  LOG_ERROR_ERRNO(105, "CDirEntry::CreateTmpFile(): Cannot create temporary file: " + tmpname);
3010  return 0;
3011  }
3012  // Create FILE* based fstream.
3013  fstream* stream = new CTmpStream(tmpname.c_str(), file);
3014  // We dont need to close FILE*, it will be closed in the fstream
3015 
3016 #else
3017  // Create filename based fstream
3018  ios::openmode mode = ios::out | ios::trunc;
3019  if ( text_binary == eBinary ) {
3020  mode = mode | ios::binary;
3021  }
3022  if ( allow_read == eAllowRead ) {
3023  mode = mode | ios::in;
3024  }
3025  fstream* stream = new CTmpStream(tmpname.c_str(), mode);
3026 #endif
3027 
3028  if ( !stream->good() ) {
3029  delete stream;
3030  LOG_ERROR_NCBI(106, "CDirEntry::CreateTmpFile(): Cannot create temporary file stream for: " + tmpname,
3032  return 0;
3033  }
3034  return stream;
3035 }
3036 
3037 
3038 fstream* CDirEntry::CreateTmpFileEx(const string& dir, const string& prefix,
3039  ETextBinary text_binary,
3040  EAllowRead allow_read)
3041 {
3043  text_binary, allow_read);
3044 }
3045 
3046 
3047 // Helper: Copy attributes (owner/date/time) from one entry to another.
3048 // Both entries should have equal type.
3049 //
3050 // UNIX:
3051 // In mostly cases only super-user can change owner for
3052 // destination entry. The owner of a file may change the group of
3053 // the file to any group of which that owner is a member.
3054 // WINDOWS:
3055 // This function doesn't support ownership change yet.
3056 //
3057 static bool s_CopyAttrs(const char* from, const char* to,
3059 {
3060 #if defined(NCBI_OS_UNIX)
3061  CDirEntry::SStat st;
3062  if ( !CDirEntry(from).Stat(&st) ) {
3063  LOG_ERROR(30, "s_CopyAttrs(): cannot get attributes for: " + string(from));
3064  return false;
3065  }
3066 
3067  // Date/time.
3068  // Set time before chmod() call, because on some platforms
3069  // setting time can affect file mode also.
3071 # if defined(HAVE_UTIMES)
3072  struct timeval tvp[2];
3073  tvp[0].tv_sec = st.orig.st_atime;
3074  tvp[0].tv_usec = TV_USEC(st.atime_nsec / 1000);
3075  tvp[1].tv_sec = st.orig.st_mtime;
3076  tvp[1].tv_usec = TV_USEC(st.mtime_nsec / 1000);
3077 # if defined(HAVE_LUTIMES)
3078  if (lutimes(to, tvp)) {
3079  LOG_ERROR_ERRNO(31, "CDirEntry::s_CopyAttrs(): lutimes() failed for: " + string(to));
3080  return false;
3081  }
3082 # else
3083  if (utimes(to, tvp)) {
3084  LOG_ERROR_ERRNO(32, "CDirEntry::s_CopyAttrs(): utimes() failed for: " + string(to));
3085  return false;
3086  }
3087 # endif
3088 # else // !HAVE_UTIMES
3089  // utimes() does not exists on current platform,
3090  // so use less accurate utime().
3091  struct utimbuf times;
3092  times.actime = st.orig.st_atime;
3093  times.modtime = st.orig.st_mtime;
3094  if (utime(to, &times)) {
3095  LOG_ERROR_ERRNO(33, "CDirEntry::s_CopyAttrs(): utime() failed for: " + string(to));
3096  return false;
3097  }
3098 # endif // HAVE_UTIMES
3099  }
3100 
3101  // Owner.
3102  // To improve performance change it right here,
3103  // do not use GetOwner/SetOwner.
3104 
3106  if ( type == CDirEntry::eLink ) {
3107 # if defined(HAVE_LCHOWN)
3108  if ( lchown(to, st.orig.st_uid, st.orig.st_gid) ) {
3109  if (errno != EPERM) {
3110  LOG_ERROR_ERRNO(34, "CDirEntry::s_CopyAttrs(): lchown() failed for: " + string(to));
3111  return false;
3112  }
3113  }
3114 # endif
3115  // We cannot change permissions for sym.links (below),
3116  // so just exit from the function.
3117  return true;
3118  } else {
3119  // Changing the ownership will probably fail, unless we're root.
3120  // The setuid/gid bits can be cleared by OS. If chown() fails,
3121  // strip the setuid/gid bits.
3122  if ( chown(to, st.orig.st_uid, st.orig.st_gid) ) {
3123  if ( errno != EPERM ) {
3124  LOG_ERROR_ERRNO(35, "CDirEntry::s_CopyAttrs(): chown() failed for: " + string(to));
3125  return false;
3126  }
3127  st.orig.st_mode &= ~(S_ISUID | S_ISGID);
3128  }
3129  }
3130  }
3131 
3132  // Permissions
3134  type != CDirEntry::eLink ) {
3135  if ( chmod(to, st.orig.st_mode) ) {
3136  LOG_ERROR_ERRNO(36, "CDirEntry::s_CopyAttrs(): chmod() failed for: " + string(to));
3137  return false;
3138  }
3139  }
3140  return true;
3141 
3142 
3143 #elif defined(NCBI_OS_MSWIN)
3144 
3145  CDirEntry efrom(from), eto(to);
3146 
3147  WIN32_FILE_ATTRIBUTE_DATA attr;
3148  if ( !::GetFileAttributesEx(_T_XCSTRING(from), GetFileExInfoStandard, &attr) ) {
3149  LOG_ERROR_WIN(30, "CDirEntry::s_CopyAttrs(): cannot get attributes for: " + string(from));
3150  return false;
3151  }
3152 
3153  // Date/time
3155  HANDLE h = ::CreateFile(_T_XCSTRING(to),
3156  FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL,
3157  OPEN_EXISTING,
3158  FILE_FLAG_BACKUP_SEMANTICS /*for dirs*/, NULL);
3159  if ( h == INVALID_HANDLE_VALUE ) {
3160  LOG_ERROR_WIN(37, "CDirEntry::s_CopyAttrs(): Cannot open: " + string(to));
3161  return false;
3162  }
3163  if ( !::SetFileTime(h, &attr.ftCreationTime, &attr.ftLastAccessTime, &attr.ftLastWriteTime) ) {
3164  LOG_ERROR_WIN(38, "CDirEntry::s_CopyAttrs(): Cannot change time for: " + string(to));
3165  ::CloseHandle(h);
3166  return false;
3167  }
3168  ::CloseHandle(h);
3169  }
3170  // Permissions
3172  if ( !::SetFileAttributes(_T_XCSTRING(to), attr.dwFileAttributes) ) {
3173  LOG_ERROR_WIN(39, "CDirEntry::s_CopyAttrs(): Cannot change pemissions for: " + string(to));
3174  return false;
3175  }
3176  }
3177  // Owner
3179  string owner, group;
3180  // We don't check the result here, because often is impossible
3181  // to set the original owner name without administrator's rights.
3182  if ( efrom.GetOwner(&owner, &group) ) {
3183  eto.SetOwner(owner, group);
3184  }
3185  }
3186 
3187  return true;
3188 #endif
3189 }
3190 
3191 
3192 //////////////////////////////////////////////////////////////////////////////
3193 //
3194 // CFile
3195 //
3196 
3197 
3199 {
3200  return;
3201 }
3202 
3203 
3205 {
3206  TNcbiSys_stat st;
3207  if (NcbiSys_stat(_T_XCSTRING(GetPath()), &st) != 0) {
3208  LOG_ERROR_ERRNO(40, "CFile:GetLength(): stat() failed for: " + GetPath());
3209  return -1L;
3210  }
3211  if ( GetType(st) != eFile ) {
3212  LOG_ERROR_NCBI(40, "CFile:GetLength(): Not a file: " + GetPath(), CNcbiError::eOperationNotPermitted);
3213  return -1L;
3214  }
3215  return st.st_size;
3216 }
3217 
3218 
3219 #if !defined(NCBI_OS_MSWIN)
3220 
3221 // Close file handle
3222 //
3223 static int s_CloseFile(int fd)
3224 {
3225  while (close(fd) != 0) {
3226  if (errno != EINTR)
3227  return errno;
3228  }
3229  // Success
3230  return 0;
3231 }
3232 
3233 
3234 // Copy file "src" to "dst"
3235 //
3236 static bool s_CopyFile(const char* src, const char* dst, size_t buf_size)
3237 {
3238  int fs; // source file descriptor
3239  int fd; // destination file descriptor
3240 
3241  if ((fs = NcbiSys_open(src, O_RDONLY)) == -1) {
3243  return false;
3244  }
3245 
3246  TNcbiSys_fstat st;
3247  if (NcbiSys_fstat(fs, &st) != 0 ||
3248  (fd = NcbiSys_open(dst, O_WRONLY|O_CREAT|O_TRUNC, st.st_mode & 0777)) == -1) {
3249  int x_errno = errno;
3250  s_CloseFile(fs);
3251  CNcbiError::SetErrno(errno = x_errno, src);
3252  return false;
3253  }
3254 
3255  // To prevent unnecessary memory (re-)allocations,
3256  // use the on-stack buffer if either the specified
3257  // "buf_size" or the size of the copied file is small.
3258  char x_buf[4096];
3259  char* buf;
3260 
3261  if (3 * sizeof(x_buf) >= (Uint8) st.st_size) {
3262  // Use on-stack buffer for any files smaller than 3x of buffer size.
3263  buf_size = sizeof(x_buf);
3264  buf = x_buf;
3265  } else {
3266  if (buf_size == 0) {
3267  buf_size = kDefaultBufferSize;
3268  }
3269  // Use allocated buffer no bigger than the size of the file to copy.
3270  if (buf_size > (Uint8) st.st_size) {
3271  buf_size = st.st_size;
3272  }
3273  buf = buf_size > sizeof(x_buf) ? new char[buf_size] : x_buf;
3274  }
3275 
3276  // Copy files
3277  int x_errno = 0;
3278  do {
3279  ssize_t n_read = read(fs, buf, buf_size);
3280  if (n_read == 0) {
3281  break;
3282  }
3283  if (n_read < 0) {
3284  if (errno == EINTR) {
3285  continue;
3286  }
3287  x_errno = errno;
3288  break;
3289  }
3290  // Write to the output file
3291  const char* ptr = buf;
3292  do {
3293  ssize_t n_written = write(fd, ptr, n_read);
3294  if (n_written == 0) {
3295  x_errno = EINVAL;
3296  break;
3297  }
3298  if ( n_written < 0 ) {
3299  if (errno == EINTR) {
3300  continue;
3301  }
3302  x_errno = errno;
3303  break;
3304  }
3305  n_read -= n_written;
3306  ptr += n_written;
3307  } while (n_read > 0);
3308 
3309  if (n_read != 0) {
3310  if (x_errno == 0) {
3311  x_errno = EIO;
3312  }
3313  }
3314  } while (!x_errno);
3315 
3316  s_CloseFile(fs);
3317  int xx_err = s_CloseFile(fd);
3318  if (x_errno == 0) {
3319  x_errno = xx_err;
3320  }
3321  if (buf != x_buf) {
3322  delete [] buf;
3323  }
3324  if (x_errno != 0) {
3325  CNcbiError::SetErrno(errno = x_errno, src);
3326  return false;
3327  }
3328  return true;
3329 }
3330 
3331 #endif
3332 
3333 
3334 bool CFile::Copy(const string& newname, TCopyFlags flags, size_t buf_size) const
3335 {
3336  CFile src(*this);
3337  CFile dst(newname);
3338 
3339  // Dereference links
3340  if ( F_ISSET(flags, fCF_FollowLinks) ) {
3341  src.DereferenceLink();
3342  dst.DereferenceLink();
3343  }
3344  // The source file must exists
3345  EType src_type = src.GetType();
3346  if ( src_type != eFile ) {
3347  LOG_ERROR_NCBI(41, "CFile::Copy(): Source is not a file: " + GetPath(),
3349  return false;
3350  }
3351 
3352  EType dst_type = dst.GetType();
3353  bool dst_exists = (dst_type != eUnknown);
3354  string dst_safe_path; // saved path for fCF_Safe
3355 
3356  // If destination exists...
3357  if ( dst_exists ) {
3358  // UNIX: check on copying file into yourself.
3359  // MS Window's ::CopyFile() can recognize such case.
3360 #if defined(NCBI_OS_UNIX)
3361  if ( src.IsIdentical(dst.GetPath()) ) {
3362  LOG_ERROR_NCBI(41, "CFile::Copy(): Cannot copy into itself: " + src.GetPath(),
3364  return false;
3365  }
3366 #endif
3367  // Can copy entries with different types?
3368  // Destination must be a file too.
3369  if ( F_ISSET(flags, fCF_EqualTypes) && (src_type != dst_type) ) {
3370  LOG_ERROR_NCBI(41, "CFile::Copy(): Destination is not a file: " + dst.GetPath(),
3372  return false;
3373  }
3374  // Can overwrite entry?
3375  if ( !F_ISSET(flags, fCF_Overwrite) ) {
3376  LOG_ERROR_NCBI(42, "CFile::Copy(): Destination file exists: " + dst.GetPath(),
3378  return false;
3379  }
3380  // Copy only if destination is older
3381  if ( F_ISSET(flags, fCF_Update) && !src.IsNewer(dst.GetPath(),0) ) {
3382  return true;
3383  }
3384  // Backup destination entry first
3385  if ( F_ISSET(flags, fCF_Backup) ) {
3386  // Use new CDirEntry object for 'dst', because its path
3387  // will be changed after backup
3388  CDirEntry dst_tmp(dst);
3389  if ( !dst_tmp.Backup(GetBackupSuffix(), eBackup_Rename) ) {
3390  LOG_ERROR(43, "CFile::Copy(): Cannot backup: " + dst.GetPath());
3391  return false;
3392  }
3393  }
3394  }
3395  // Safe copy -- copy to temporary file and rename later
3396  if (F_ISSET(flags, fCF_Safe)) {
3397  // Get new temporary name in the same directory
3398  string path, name, ext;
3399  SplitPath(dst.GetPath(), &path, &name, &ext);
3400  string tmp = GetTmpNameEx(path.empty() ? CDir::GetCwd() : path, name + ext + kTmpSafeSuffix);
3401  // Set new destination
3402  dst_safe_path = dst.GetPath();
3403  dst.Reset(tmp);
3404  }
3405 
3406  // Copy
3407 #if defined(NCBI_OS_MSWIN)
3408  if ( !::CopyFile(_T_XCSTRING(src.GetPath()),
3409  _T_XCSTRING(dst.GetPath()), FALSE) ) {
3410  LOG_ERROR_WIN(44, "CFile::Copy(): Cannot copy " + src.GetPath() + " to " + dst.GetPath());
3411  dst.RemoveEntry();
3412  return false;
3413  }
3414 #else
3415  if ( !s_CopyFile(src.GetPath().c_str(), dst.GetPath().c_str(), buf_size) ) {
3416  LOG_ERROR_ERRNO(44, "CFile::Copy(): Cannot copy " + src.GetPath() + " to " + dst.GetPath());
3417  dst.Remove();
3418  return false;
3419  }
3420 #endif
3421 
3422  // Safe copy -- renaming
3423  if (F_ISSET(flags, fCF_Safe)) {
3424  if (!dst.Rename(dst_safe_path, fRF_Overwrite)) {
3425  dst.RemoveEntry();
3426  LOG_ERROR_NCBI(45, "CFile:Copy():"
3427  " Cannot rename temporary file " + dst.GetPath() +
3428  " to " + dst_safe_path, CNcbiError::eIoError);
3429  return false;
3430  }
3431  }
3432  // Verify copied data
3433  if ( F_ISSET(flags, fCF_Verify) && !src.Compare(dst.GetPath()) ) {
3434  LOG_ERROR_NCBI(46, "CFile::Copy(): Verification for " + src.GetPath() +
3435  " and " + dst.GetPath() + " failed", CNcbiError::eIoError);
3436  return false;
3437  }
3438 
3439  // Preserve attributes
3440  // s_CopyFile() preserve permissions on Unix, MS-Windows don't need it at all.
3441 
3442 #if defined(NCBI_OS_MSWIN)
3443  // On MS Windows ::CopyFile() already preserved file attributes
3444  // and all date/times.
3446 #endif
3447  if ( flags & fCF_PreserveAll ) {
3448  if ( !s_CopyAttrs(src.GetPath().c_str(),
3449  dst.GetPath().c_str(), eFile, flags) ) {
3450  LOG_ERROR(95, "CFile::Copy(): Cannot copy permissions from " +
3451  src.GetPath() + " to " + dst.GetPath());
3452  return false;
3453  }
3454  }
3455  return true;
3456 }
3457 
3458 
3459 bool CFile::Compare(const string& filename, size_t buf_size) const
3460 {
3461  // To prevent unnecessary memory (re-)allocations,
3462  // use the on-stack buffer if either the specified
3463  // "buf_size" or the size of the compared files is small.
3464  char x_buf[4096*2];
3465  size_t x_size = sizeof(x_buf)/2;
3466  char* buf1 = 0;
3467  char* buf2 = 0;
3468  bool equal = false;
3469 
3470  try {
3471  CFileIO f1;
3472  CFileIO f2;
3474  f2.Open(filename, CFileIO::eOpen, CFileIO::eRead);
3475 
3476  Uint8 s1 = f1.GetFileSize();
3477  Uint8 s2 = f2.GetFileSize();
3478 
3479  // Files should have equal sizes
3480  if (s1 != s2) {
3481  LOG_ERROR_NCBI(93, "CFile::Compare(): files have different size: " +
3482  GetPath() + " and " + filename,
3484  return false;
3485  }
3486  if (s1 == 0) {
3487  return true;
3488  }
3489  // Use on-stack buffer for any files smaller than 3x of buffer size.
3490  if (s1 <= 3 * x_size) {
3491  buf_size = x_size;
3492  buf1 = x_buf;
3493  buf2 = x_buf + x_size;
3494  } else {
3495  if (buf_size == 0) {
3496  buf_size = kDefaultBufferSize;
3497  }
3498  // Use allocated buffer no bigger than the size of the file to compare.
3499  // Align buffer in memory to 8 byte boundary.
3500  if (buf_size > s1) {
3501  buf_size = (size_t)s1 + (8 - s1 % 8);
3502  }
3503  if (buf_size > x_size) {
3504  buf1 = new char[buf_size*2];
3505  buf2 = buf1 + buf_size;
3506  } else {
3507  buf1 = x_buf;
3508  buf2 = x_buf + x_size;
3509  }
3510  }
3511 
3512  size_t n1 = 0;
3513  size_t n2 = 0;
3514  size_t s = 0;
3515 
3516  // Compare files
3517  for (;;) {
3518  size_t n;
3519  if (n1 < buf_size) {
3520  n = f1.Read(buf1 + n1, buf_size - n1);
3521  if (n == 0) {
3522  break;
3523  }
3524  n1 += n;
3525  }
3526  if (n2 < buf_size) {
3527  n = f2.Read(buf2 + n2, buf_size - n2);
3528  if (n == 0) {
3529  break;
3530  }
3531  n2 += n;
3532  }
3533  size_t m = min(n1, n2);
3534  if ( memcmp(buf1, buf2, m) != 0 ) {
3535  break;
3536  }
3537  if (n1 > m) {
3538  memmove(buf1, buf1 + m, n1 - m);
3539  n1 -= m;
3540  } else {
3541  n1 = 0;
3542  }
3543  if (n2 > m) {
3544  memmove(buf2, buf2 + m, n2 - m);
3545  n2 -= m;
3546  } else {
3547  n2 = 0;
3548  }
3549  s += m;
3550  }
3551  equal = (s1 == s);
3552  }
3553  catch (const CFileErrnoException& ex) {
3554  LOG_ERROR_NCBI(47, "CFile::Compare(): error comparing files " + GetPath() +
3555  " and " + filename + " : " + ex.what(),
3557  }
3558  if (buf1 != x_buf) {
3559  delete [] buf1;
3560  }
3561  return equal;
3562 }
3563 
3564 
3566  size_t buf_size) const
3567 {
3568  CNcbiIfstream f1(GetPath().c_str(), IOS_BASE::in);
3569  CNcbiIfstream f2(file.c_str(), IOS_BASE::in);
3570 
3571  if ( !buf_size ) {
3572  buf_size = kDefaultBufferSize;
3573  }
3574  return NcbiStreamCompareText(f1, f2, (ECompareTextMode)mode, (streamsize)buf_size);
3575 }
3576 
3577 
3578 
3579 //////////////////////////////////////////////////////////////////////////////
3580 //
3581 // CDir
3582 //
3583 
3584 #if defined(NCBI_OS_UNIX)
3585 
3586 static bool s_GetHomeByUID(string& home)
3587 {
3588  // Get the info using user ID
3589  struct passwd* pwd;
3590 
3591  if ((pwd = getpwuid(getuid())) == 0) {
3592  LOG_ERROR_ERRNO(48, "s_GetHomeByUID(): getpwuid() failed");
3593  return false;
3594  }
3595  home = pwd->pw_dir;
3596  return true;
3597 }
3598 
3599 static bool s_GetHomeByLOGIN(string& home)
3600 {
3601  const TXChar* ptr = 0;
3602  // Get user name
3603  if ( !(ptr = NcbiSys_getenv(_TX("USER"))) ) {
3604  if ( !(ptr = NcbiSys_getenv(_TX("LOGNAME"))) ) {
3605  if ( !(ptr = getlogin()) ) {
3606  LOG_ERROR_ERRNO(49, "s_GetHomeByLOGIN(): Unable to get user name");
3607  return false;
3608  }
3609  }
3610  }
3611  // Get home dir for this user
3612  struct passwd* pwd = getpwnam(ptr);
3613  if ( !pwd || pwd->pw_dir[0] == '\0') {
3614  LOG_ERROR_ERRNO(50, "s_GetHomeByLOGIN(): getpwnam() failed");
3615  return false;
3616  }
3617  home = pwd->pw_dir;
3618  return true;
3619 }
3620 
3621 #endif // NCBI_OS_UNIX
3622 
3623 
3624 string CDir::GetHome(void)
3625 {
3626  string home;
3627 
3628 #if defined(NCBI_OS_MSWIN)
3629  // Get home dir from environment variables
3630  // like - C:\Documents and Settings\user\Application Data
3631  const TXChar* str = NcbiSys_getenv(_TX("APPDATA"));
3632  if ( str ) {
3633  home = _T_CSTRING(str);
3634  } else {
3635  // like - C:\Documents and Settings\user
3636  str = NcbiSys_getenv(_TX("USERPROFILE"));
3637  if ( str ) {
3638  home = _T_CSTRING(str);
3639  }
3640  }
3641 #elif defined(NCBI_OS_UNIX)
3642  // Try get home dir from environment variable
3643  char* str = NcbiSys_getenv(_TX("HOME"));
3644  if ( str ) {
3645  home = str;
3646  } else {
3647  // Try to retrieve the home dir -- first use user's ID,
3648  // and if failed, then use user's login name.
3649  if ( !s_GetHomeByUID(home) ) {
3650  s_GetHomeByLOGIN(home);
3651  }
3652  }
3653 #endif
3654 
3655  // Add trailing separator if needed
3656  return AddTrailingPathSeparator(home);
3657 }
3658 
3659 
3660 string CDir::GetTmpDir(void)
3661 {
3662  string tmp;
3663 
3664 #if defined(NCBI_OS_UNIX)
3665 
3666  char* tmpdir = getenv("TMPDIR");
3667  if ( tmpdir ) {
3668  tmp = tmpdir;
3669  } else {
3670 # if defined(P_tmpdir)
3671  tmp = P_tmpdir;
3672 # else
3673  tmp = "/tmp";
3674 # endif
3675  }
3676 
3677 #elif defined(NCBI_OS_MSWIN)
3678 
3679  const TXChar* tmpdir = NcbiSys_getenv(_TX("TEMP"));
3680  if ( tmpdir ) {
3681  tmp = _T_CSTRING(tmpdir);
3682  } else {
3683 # if defined(P_tmpdir)
3684  tmp = P_tmpdir;
3685 # else
3686  tmp = CDir::GetHome();
3687 # endif
3688  }
3689 
3690 #endif
3691 
3692  return tmp;
3693 }
3694 
3695 
3697 {
3698  // Get application specific temporary directory name
3699  string tmp = NCBI_PARAM_TYPE(NCBI, TmpDir)::GetThreadDefault();
3700  if ( !tmp.empty() ) {
3701  return tmp;
3702  }
3703  // Use default TMP directory specified by OS
3704  return CDir::GetTmpDir();
3705 }
3706 
3707 
3708 string CDir::GetCwd(void)
3709 {
3710  TXChar buf[4096];
3711  if ( NcbiSys_getcwd(buf, sizeof(buf)/sizeof(TXChar) - 1) ) {
3712  return _T_CSTRING(buf);
3713  }
3714  LOG_ERROR_ERRNO(90, "CDir::GetCwd(): Cannot get current directory");
3715  return kEmptyCStr;
3716 }
3717 
3718 
3719 bool CDir::SetCwd(const string& dir)
3720 {
3721  if ( NcbiSys_chdir(_T_XCSTRING(dir)) != 0 ) {
3722  LOG_ERROR_ERRNO(51, "CDir::SetCwd(): Cannot change directory to: " + dir);
3723  return false;
3724  }
3725  return true;
3726 }
3727 
3728 
3730 {
3731  return;
3732 }
3733 
3734 
3735 bool CDirEntry::MatchesMask(const string& name,
3736  const vector<string>& masks,
3737  NStr::ECase use_case)
3738 {
3739  if ( masks.empty() ) {
3740  return true;
3741  }
3742  ITERATE(vector<string>, itm, masks) {
3743  const string& mask = *itm;
3744  if ( MatchesMask(name, mask, use_case) ) {
3745  return true;
3746  }
3747  }
3748  return false;
3749 }
3750 
3751 
3752 // Helpers functions and macro for GetEntries().
3753 
3754 #if defined(NCBI_OS_MSWIN)
3755 
3756 // Set errno for failed FindFirstFile/FindNextFile
3757 // TODO:
3758 // This method should be removed, we dont need to translate
3759 // Windows error to errno, we already have CNcbiErrno for this,
3760 // please use it.
3761 
3762 static void s_SetFindFileError(DWORD err)
3763 {
3764  ::SetLastError(err); // set Windows error back
3765  switch (err) {
3766  case ERROR_NO_MORE_FILES:
3767  case ERROR_FILE_NOT_FOUND:
3768  case ERROR_PATH_NOT_FOUND:
3769  errno = ENOENT;
3770  break;
3771  case ERROR_NOT_ENOUGH_MEMORY:
3772  errno = ENOMEM;
3773  break;
3774  case ERROR_ACCESS_DENIED:
3775  errno = EACCES;
3776  break;
3777  default:
3778  errno = EINVAL;
3779  break;
3780  }
3781 }
3782 
3783 # define IS_RECURSIVE_ENTRY \
3784  ( (flags & CDir::fIgnoreRecursive) && \
3785  ((NcbiSys_strcmp(entry.cFileName, _TX(".")) == 0) || \
3786  (NcbiSys_strcmp(entry.cFileName, _TX("..")) == 0)) )
3787 
3788 static void s_AddEntry(CDir::TEntries* contents,
3789  const string& base_path,
3790  const WIN32_FIND_DATA& entry,
3792 {
3793  const string name = (flags & CDir::fIgnorePath) ?
3794  _T_CSTRING(entry.cFileName) :
3795  base_path + _T_CSTRING(entry.cFileName);
3796 
3797  if (flags & CDir::fCreateObjects) {
3798  CDirEntry::EType type = (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3800  contents->push_back(CDirEntry::CreateObject(type, name));
3801  } else {
3802  contents->push_back(new CDirEntry(name));
3803  }
3804 }
3805 
3806 #else // NCBI_OS_UNIX
3807 
3808 # define IS_RECURSIVE_ENTRY \
3809  ( (flags & CDir::fIgnoreRecursive) && \
3810  ((::strcmp(entry->d_name, ".") == 0) || \
3811  (::strcmp(entry->d_name, "..") == 0)) )
3812 
3813 static void s_AddEntry(CDir::TEntries* contents,
3814  const string& base_path,
3815  const struct dirent* entry,
3817 {
3818  const string name = (flags & CDir::fIgnorePath) ? entry->d_name : base_path + entry->d_name;
3819 
3822 # if defined(_DIRENT_HAVE_D_TYPE)
3823  struct stat st;
3824  if (entry->d_type) {
3825  st.st_mode = DTTOIF(entry->d_type);
3826  type = CDirEntry::GetType(st);
3827  }
3828 # endif
3829  if (type == CDir::eUnknown) {
3830  if (flags & CDir::fIgnorePath) {
3831  const string path = base_path + entry->d_name;
3832  type = CDirEntry(path).GetType();
3833  } else {
3834  type = CDirEntry(name).GetType();
3835  }
3836  }
3837  contents->push_back(CDirEntry::CreateObject(type, name));
3838  } else {
3839  contents->push_back(new CDirEntry(name));
3840  }
3841 }
3842 
3843 #endif
3844 
3845 
3847 {
3848  CMaskFileName masks;
3849  if ( !mask.empty() ) {
3850  masks.Add(mask);
3851  }
3852  return GetEntries(masks, flags);
3853 }
3854 
3855 
3857 {
3858  CMaskFileName masks;
3859  if ( !mask.empty() ) {
3860  masks.Add(mask);
3861  }
3862  return GetEntriesPtr(masks, flags);
3863 }
3864 
3865 
3866 CDir::TEntries CDir::GetEntries(const vector<string>& masks, TGetEntriesFlags flags) const
3867 {
3868  unique_ptr<TEntries> contents(GetEntriesPtr(masks, flags));
3869  return contents.get() ? *contents.get() : TEntries();
3870 }
3871 
3872 
3873 CDir::TEntries* CDir::GetEntriesPtr(const vector<string>& masks, TGetEntriesFlags flags) const
3874 {
3875  if ( masks.empty() ) {
3876  return GetEntriesPtr(kEmptyStr, flags);
3877  }
3878  TEntries* contents = new TEntries;
3879  string base_path = AddTrailingPathSeparator(GetPath().empty() ? DIR_CURRENT : GetPath());
3880  NStr::ECase use_case = (flags & fNoCase) ? NStr::eNocase : NStr::eCase;
3881 
3882 #if defined(NCBI_OS_MSWIN)
3883 
3884  // Append to the "path" mask for all files in directory
3885  string pattern = base_path + "*";
3886 
3887  WIN32_FIND_DATA entry;
3888  HANDLE handle;
3889 
3890  handle = ::FindFirstFile(_T_XCSTRING(pattern), &entry);
3891  if (handle != INVALID_HANDLE_VALUE) {
3892  // Check all masks
3893  do {
3894  if (!IS_RECURSIVE_ENTRY) {
3895  ITERATE(vector<string>, it, masks) {
3896  const string& mask = *it;
3897  if ( mask.empty() ||
3898  MatchesMask(_T_CSTRING(entry.cFileName), mask, use_case) ) {
3899  s_AddEntry(contents, base_path, entry, flags);
3900  break;
3901  }
3902  }
3903  }
3904  } while (::FindNextFile(handle, &entry));
3906  ::FindClose(handle);
3907 
3908  } else {
3909  DWORD err = ::GetLastError();
3911  s_SetFindFileError(err);
3912  delete contents;
3913  if ( F_ISSET(flags, fThrowOnError) ) {
3914  NCBI_THROW(CFileErrnoException, eFile, "Cannot read directory " + base_path);
3915  }
3916  return NULL;
3917  }
3918 
3919 #elif defined(NCBI_OS_UNIX)
3920 
3921  DIR* dir = opendir(base_path.c_str());
3922  if ( !dir ) {
3924  delete contents;
3925  if ( F_ISSET(flags, fThrowOnError) ) {
3926  NCBI_THROW(CFileErrnoException, eFile, "Cannot read directory " + base_path);
3927  }
3928  return NULL;
3929  }
3930  while (struct dirent* entry = readdir(dir)) {
3931  if (IS_RECURSIVE_ENTRY) {
3932  continue;
3933  }
3934  ITERATE(vector<string>, it, masks) {
3935  const string& mask = *it;
3936  if ( mask.empty() || MatchesMask(entry->d_name, mask, use_case) ) {
3937  s_AddEntry(contents, base_path, entry, flags);
3938  break;
3939  }
3940  } // ITERATE
3941  } // while
3943  closedir(dir);
3944 
3945 #endif
3946 
3947  return contents;
3948 }
3949 
3950 
3952 {
3953  unique_ptr<TEntries> contents(GetEntriesPtr(masks, flags));
3954  return contents.get() ? *contents.get() : TEntries();
3955 }
3956 
3957 
3959 {
3960  TEntries* contents = new TEntries;
3961  string base_path = AddTrailingPathSeparator(GetPath().empty() ? DIR_CURRENT : GetPath());
3962  NStr::ECase use_case = (flags & fNoCase) ? NStr::eNocase : NStr::eCase;
3963 
3964 #if defined(NCBI_OS_MSWIN)
3965 
3966  // Append to the "path" mask for all files in directory
3967  string pattern = base_path + "*";
3968 
3969  WIN32_FIND_DATA entry;
3970  HANDLE handle;
3971 
3972  handle = ::FindFirstFile(_T_XCSTRING(pattern), &entry);
3973  if (handle != INVALID_HANDLE_VALUE) {
3974  do {
3975  if ( !IS_RECURSIVE_ENTRY &&
3976  masks.Match(_T_CSTRING(entry.cFileName), use_case) ) {
3977  s_AddEntry(contents, base_path, entry, flags);
3978  }
3979  } while ( ::FindNextFile(handle, &entry) );
3981  ::FindClose(handle);
3982 
3983  } else {
3984  DWORD err = ::GetLastError();
3986  s_SetFindFileError(err);
3987  delete contents;
3988  if ( F_ISSET(flags, fThrowOnError) ) {
3989  NCBI_THROW(CFileErrnoException, eFile, string("Cannot read directory ") + base_path);
3990  }
3991  return NULL;
3992  }
3993 
3994 #elif defined(NCBI_OS_UNIX)
3995 
3996  DIR* dir = opendir(base_path.c_str());
3997  if ( !dir ) {
3999  delete contents;
4000  if ( F_ISSET(flags, fThrowOnError) ) {
4001  NCBI_THROW(CFileErrnoException, eFile, string("Cannot read directory ") + base_path);
4002  }
4003  return NULL;
4004  }
4005  while (struct dirent* entry = readdir(dir)) {
4006  if ( !IS_RECURSIVE_ENTRY &&
4007  masks.Match(entry->d_name, use_case) ) {
4008  s_AddEntry(contents, base_path, entry, flags);
4009  }
4010  }
4012  closedir(dir);
4013 
4014 #endif
4015 
4016  return contents;
4017 }
4018 
4019 
4020 // Helper function for CDir::Create[Path]()
4021 inline bool s_DirCreate(const string&path, CDir::TCreateFlags flags, mode_t mode)
4022 {
4023  errno = 0;
4024 #if defined(NCBI_OS_MSWIN)
4025  int res = NcbiSys_mkdir(_T_XCSTRING(path));
4026 #elif defined(NCBI_OS_UNIX)
4027  int res = NcbiSys_mkdir(_T_XCSTRING(path), mode);
4028 #endif
4029  if (res != 0) {
4030  if (errno != EEXIST) {
4031  LOG_ERROR_ERRNO(52, "s_DirCreate(): Cannot create directory: " + path);
4032  return false;
4033  }
4034  // Entry with such name already exists, check its type
4035  CDirEntry::EType type = CDirEntry(path).GetType();
4036  if (type != CDirEntry::eDir) {
4037  LOG_ERROR_NCBI(53, "s_DirCreate(): Path already exist and is not a directory: " + path, CNcbiError::eNotADirectory);
4038  return false;
4039  }
4041  LOG_ERROR_NCBI(54, "s_DirCreate(): Directory already exist: " + path, CNcbiError::eFileExists);
4042  return false;
4043  }
4045  return true;
4046  }
4047  }
4048  // The permissions for the created directory is controlled by umask and is (mode & ~umask & 0777).
4049  // We need to call chmod() directly if we need other behavior.
4050 
4055 
4058  NCBI_PARAM_TYPE(NCBI, FileAPIHonorUmask)::GetDefault()) ) {
4059  // nothing to do if (umask) or (default mode with "honor umask" global flag)
4060  return true;
4061  }
4062  // Change directory permissions
4063  if (NcbiSys_chmod(_T_XCSTRING(path), mode) != 0) {
4064  LOG_ERROR_ERRNO(55, "CDir::Create(): Cannot set mode for directory: " + path);
4065  return false;
4066  }
4067  return true;
4068 }
4069 
4070 
4072 {
4073  if (GetPath().empty()) {
4074  LOG_ERROR_NCBI(56, "CDir::Create(): Path is empty", CNcbiError::eInvalidArgument);
4075  return false;
4076  }
4078 
4079  // Get parent permissions
4080  if (F_ISSET(flags, fCreate_PermAsParent)) {
4082  string path_up(d.GetDir());
4083  if ( path_up.empty() || path_up == d.GetPath() ) {
4084  LOG_ERROR_NCBI(57, "CDir::Create(): Cannot get parent directory for: " + GetPath(),
4086  return false;
4087  }
4088 #if defined(NCBI_OS_MSWIN)
4089  // Special case -- stat() dont works if directory have trailing path
4090  // separator, except it is a root directory with a disk name, like "C:\".
4091  if (path_up.length() > 3) {
4092  path_up = DeleteTrailingPathSeparator(path_up);
4093  }
4094 #endif
4095  TNcbiSys_stat st;
4096  if (NcbiSys_stat(_T_XCSTRING(path_up), &st) != 0) {
4097  LOG_ERROR_ERRNO(58, "CDir::Create(): stat() failed for: " + GetPath());
4098  return false;
4099  }
4100  mode = st.st_mode;
4101  }
4102  return s_DirCreate(GetPath(), flags, mode);
4103 }
4104 
4105 
4107 {
4108  if (GetPath().empty()) {
4109  LOG_ERROR_NCBI(59, "CDir::CreatePath(): Path is empty", CNcbiError::eInvalidArgument);
4110  return false;
4111  }
4112  string path(CreateAbsolutePath(GetPath()));
4113  if (path.empty()) {
4114  LOG_ERROR_NCBI(60, "CDir::CreatePath(): Cannot create absolute path from: " + path, CNcbiError::eInvalidArgument);
4115  return false;
4116  }
4117  if (path[path.length()-1] == GetPathSeparator()
4118 #if defined(NCBI_OS_MSWIN)
4119  && path.length() != 3
4120  // Special case -- for path like "C:\" dont remove a last separator, it represent a root directory
4121 #endif
4122  ) {
4123  path.erase(path.length() - 1);
4124  }
4125 
4126  // Find all missed parts of the path
4127 
4128  CTempString tmp(path); // existent part of a path
4129  std::list<CTempString> missed_parts;
4130 
4131  while (!tmp.empty() && !CDirEntry(tmp).Exists()) {
4132  size_t pos = tmp.find_last_of(DIR_SEPARATORS);
4133  if (pos == NPOS) {
4134  break;
4135  }
4136  CTempString part(tmp.substr(pos+1));
4137  missed_parts.push_front(part);
4138  tmp.erase(pos);
4139  }
4140 
4142 
4143  // Get parent permissions
4144  if (F_ISSET(flags, fCreate_PermAsParent)) {
4145  string parent;
4146  if (missed_parts.empty()) {
4147  parent.assign(CDir(tmp).GetDir());
4148  } else {
4149  parent.assign(tmp);
4150  }
4151 #if defined(NCBI_OS_MSWIN)
4152  // Special case -- for paths like "C:" add slash to represent a root directory
4153  if (parent.length() == 2) {
4154  parent += GetPathSeparator();
4155  }
4156 #endif
4157  TNcbiSys_stat st;
4158  if (NcbiSys_stat(_T_XCSTRING(parent), &st) != 0) {
4159  LOG_ERROR_ERRNO(61, "CDir::CreatePath(): stat() failed for: " + parent);
4160  return false;
4161  }
4162  mode = st.st_mode;
4163  }
4164 
4165  // Path exists?
4166  if (missed_parts.empty()) {
4167  // check existence and behave depends on flags
4168  if (!s_DirCreate(path, flags, mode)) {
4169  LOG_ERROR(96, "CDir::CreatePath(): Cannot create path: " + GetPath());
4170  return false;
4171  }
4172  return true;
4173  }
4174 
4175  // Create missed subdirectories
4176  string p = tmp;
4177  for (auto i : missed_parts) {
4178  p += GetPathSeparator();
4179  p += i;
4180  if (!s_DirCreate(p, flags, mode)) {
4181  LOG_ERROR(97, "CDir::CreatePath(): Cannot create path: " + GetPath());
4182  return false;
4183  }
4184  }
4185  return true;
4186 }
4187 
4188 
4189 bool CDir::Copy(const string& newname, TCopyFlags flags, size_t buf_size) const
4190 {
4191  CDir src(*this);
4192  CDir dst(newname);
4193 
4194  // Dereference links
4195  bool follow = F_ISSET(flags, fCF_FollowLinks);
4196  if ( follow ) {
4197  src.DereferenceLink();
4198  dst.DereferenceLink();
4199  }
4200  // The source dir must exists
4201  EType src_type = src.GetType();
4202  if ( src_type != eDir ) {
4203  LOG_ERROR_NCBI(62, "CDir::Copy(): Source is not a directory: " + src.GetPath(),
4205  return false;
4206  }
4207  EType dst_type = dst.GetType();
4208  bool dst_exists = (dst_type != eUnknown);
4209  bool need_create_dst = !dst_exists;
4210  string dst_safe_path; // saved path for fCF_Safe
4211 
4212  // Safe copy?
4213  // Don't use it if fCF_TopDirOnly is not specified. If target directory
4214  // exists it will be just "updated" and safe copying will be applied
4215  // on a file level for every copied entry.
4216  bool need_safe_copy = F_ISSET(flags, fCF_Safe | fCF_TopDirOnly);
4217 
4218  // If destination exists...
4219  if ( dst_exists ) {
4220  // Check on copying dir into yourself
4221  if ( src.IsIdentical(dst.GetPath()) ) {
4222  LOG_ERROR_NCBI(63, "CDir::Copy(): Source and destination are the same: " + src.GetPath(),
4224  return false;
4225  }
4226  // Can rename entries with different types?
4227  if ( F_ISSET(flags, fCF_EqualTypes) && (src_type != dst_type) ) {
4228  LOG_ERROR_NCBI(64, "CDir::Copy(): Destination is not a directory: " + dst.GetPath(),
4230  return false;
4231  }
4232 
4233  // Some operation can be made for top directory only
4234 
4235  if ( F_ISSET(flags, fCF_TopDirOnly) ) {
4236  // Can overwrite entry?
4237  if ( !F_ISSET(flags, fCF_Overwrite) ) {
4238  LOG_ERROR_NCBI(65, "CDir::Copy(): Destination directory already exists: " + dst.GetPath(),
4240  return false;
4241  }
4242  // Copy only if destination is older
4243  if ( F_ISSET(flags, fCF_Update) && !src.IsNewer(dst.GetPath(), 0) ) {
4244  return true;
4245  }
4246  // Backup destination directory
4247  if (F_ISSET(flags, fCF_Backup)) {
4248  // Use new CDirEntry object instead of 'dst', because its path
4249  // will be changed after backup
4250  CDirEntry dst_tmp(dst);
4251  if ( !dst_tmp.Backup(GetBackupSuffix(), eBackup_Rename) ) {
4252  LOG_ERROR(66, "CDir::Copy(): Cannot backup destination directory: " + dst.GetPath());
4253  return false;
4254  }
4255  need_create_dst = true;
4256  }
4257  // Clear flags not needed anymore.
4258  // Keep fCF_Overwrite if it is set (fCF_Backup is a compound flag).
4260  }
4261  }
4262 
4263  // Safe copy for top directory -- copy to temporary directory in the same
4264  // parent directory and rename later.
4265 
4266  if ( need_safe_copy ) {
4267  // Get new temporary name in the same directory
4268  string path, name, ext;
4269  SplitPath(dst.GetPath(), &path, &name, &ext);
4270  string tmp = GetTmpNameEx(path.empty() ? CDir::GetCwd() : path, name + ext + kTmpSafeSuffix);
4271  // Set new destination
4272  dst_safe_path = dst.GetPath();
4273  dst.Reset(tmp);
4274  need_create_dst = true;
4275  // Clear safe flag, we already have a temporary top directory
4276  flags &= ~fCF_Safe;
4277  }
4278 
4279  // Create target directory if needed
4280  if ( need_create_dst ) {
4281  if ( !dst.CreatePath() ) {
4282  LOG_ERROR(67, "CDir::Copy(): Cannot create " <<
4283  (dst_safe_path.empty() ? "target" : "temporary") <<
4284  " directory: " << dst.GetPath());
4285  return false;
4286  }
4287  }
4288 
4289  // Read all entries in source directory
4290  unique_ptr<TEntries> contents(src.GetEntriesPtr(kEmptyStr, fIgnoreRecursive));
4291  if ( !contents.get() ) {
4292  LOG_ERROR(68, "CDir::Copy(): Cannot get content of " + src.GetPath());
4293  return false;
4294  }
4295 
4296  // And copy each of them to target directory
4297  ITERATE(TEntries, e, *contents.get()) {
4298  CDirEntry& entry = **e;
4299  if (!F_ISSET(flags, fCF_Recursive) &&
4300  entry.IsDir(follow ? eFollowLinks : eIgnoreLinks)) {
4301  continue;
4302  }
4303  // Copy entry
4304  if (!entry.CopyToDir(dst.GetPath(), flags, buf_size)) {
4305  LOG_ERROR(69, "CDir::Copy(): Cannot copy " +
4306  entry.GetPath() + " to directory " + dst.GetPath());
4307  return false;
4308  }
4309  }
4310 
4311  // Safe copy for top directory -- renaming temporary to target
4312  if (!dst_safe_path.empty()) {
4313  if (!dst.Rename(dst_safe_path, fRF_Overwrite)) {
4314  dst.Remove();
4315  LOG_ERROR(70, "CDir:Copy(): Cannot rename temporary directory " +
4316  dst.GetPath() + " to " + dst_safe_path);
4317  return false;
4318  }
4319  }
4320  // Preserve attributes
4321  if ( flags & fCF_PreserveAll ) {
4322  if ( !s_CopyAttrs(src.GetPath().c_str(),
4323  dst.GetPath().c_str(), eDir, flags) ) {
4324  LOG_ERROR(98, "CDir:Copy(): Cannot copy attributes from " +
4325  src.GetPath() + " to " + dst.GetPath());
4326  return false;
4327  }
4328  } else {
4329  // Set default permissions for directory, if we should not
4330  // honor umask settings.
4331  if ( !NCBI_PARAM_TYPE(NCBI, FileAPIHonorUmask)::GetDefault()) {
4332  if ( !dst.SetMode(fDefault, fDefault, fDefault) ) {
4333  LOG_ERROR(99, "CDir:Copy(): Cannot set default directory permissions: " + dst.GetPath());
4334  return false;
4335  }
4336  }
4337  }
4338  return true;
4339 }
4340 
4341 
4343 {
4344  // Assumption
4345  _ASSERT(fDir_Self == fEntry);
4347 
4348  // Remove directory as empty
4349  if ( (flags & (fDir_All | fDir_Recursive)) == eOnlyEmpty ) {
4350  if ( NcbiSys_rmdir(_T_XCSTRING(GetPath())) != 0 ) {
4351  if ( (flags & fIgnoreMissing) && (errno == ENOENT) ) {
4352  return true;
4353  }
4354  LOG_ERROR_ERRNO(71, "CDir::Remove(): Cannot remove (by implication empty)"
4355  " directory: " + GetPath());
4356  return false;
4357  }
4358  return true;
4359  }
4360 
4361 #if !defined(NCBI_OS_MSWIN)
4362  // Make directory writable for user to remove any entry inside
4366 #endif
4367 
4368  // Read all entries in directory
4369  unique_ptr<TEntries> contents(GetEntriesPtr());
4370  if (!contents.get()) {
4371  LOG_ERROR(72, "CDir::Remove(): Cannot get content of: " + GetPath());
4372  return false;
4373  }
4374 
4375  bool success = true;
4376  try {
4377  // Remove each entry
4378  ITERATE(TEntries, entry, *contents.get()) {
4379  string name = (*entry)->GetName();
4380  if (name == "." || name == ".." || name == string(1, GetPathSeparator())) {
4381  continue;
4382  }
4383  // Get entry item with full pathname
4384  CDirEntry item(GetPath() + GetPathSeparator() + name);
4385 
4386  if (flags & fDir_Recursive) {
4387  // Update flags to process subdirectories itself,
4388  // because the top directory entry may not have
4389  // such flag.
4390  int f = (flags & fDir_Subdirs) ? (flags | fDir_Self) : flags;
4391  if (item.IsDir(eIgnoreLinks)) {
4392  if (!CDir(item.GetPath()).Remove(f)) {
4393  if (flags & fProcessAll) {
4394  success = false;
4395  } else {
4396  throw "Removing subdirectory failed";
4397  }
4398  }
4399  }
4400  else if (flags & fDir_Files) {
4401  if (!item.Remove(f)) {
4402  if (flags & fProcessAll) {
4403  success = false;
4404  } else {
4405  throw "Removing directory entry failed";
4406  }
4407  }
4408  }
4409  }
4410  else if (item.IsDir(eIgnoreLinks)) {
4411  // Non-recursive directory removal
4412  if (flags & fDir_Subdirs) {
4413  // Clear all flags to go inside directory,
4414  // and try to remove it as "empty".
4415  if (!item.Remove((flags & ~fDir_All) | fDir_Self)) {
4416  if (flags & fProcessAll) {
4417  success = false;
4418  } else {
4419  throw "Removing directory entry (non-recursive) failed";
4420  }
4421  }
4422  }
4423  continue;
4424  }
4425  else {
4426  if (flags & fDir_Files) {
4427  if (!item.Remove(flags)) {
4428  if (flags & fProcessAll) {
4429  success = false;
4430  } else {
4431  throw "";
4432  }
4433  }
4434  }
4435  }
4436  }
4437  // Remove top directory
4438  if ((flags & fDir_Self) && NcbiSys_rmdir(_T_XCSTRING(GetPath())) != 0) {
4439  if ((flags & fIgnoreMissing) && (errno == ENOENT)) {
4440  return true;
4441  }
4442  if (flags & fProcessAll) {
4443  success = false;
4444  } else {
4445  throw "Cannot remove directory entry";
4446  }
4447  }
4448  }
4449  catch (const char* what) {
4450  // The error is may be reported already in child .Remove() for recursive calls,
4451  // but add an additional error log for the current directory itself.
4452  LOG_ERROR(73, "CDir::Remove(): Cannot remove directory: " + GetPath() + ": " + what);
4453  return false;
4454  }
4455  return success;
4456 }
4457 
4458 
4459 bool CDir::SetMode(TMode user_mode, TMode group_mode,
4460  TMode other_mode, TSpecialModeBits special_mode,
4461  TSetModeFlags flags) const
4462 {
4463  // Assumption
4464  _ASSERT(fDir_Self == fEntry);
4466 
4467  // Default mode (backward compatibility) -- top entry only
4468  if ( (flags & (fDir_All | fDir_Recursive)) == eEntryOnly ) {
4469  return SetModeEntry(user_mode, group_mode, other_mode, special_mode, flags);
4470  }
4471 
4472  // Read all entries in directory
4473  unique_ptr<TEntries> contents(GetEntriesPtr());
4474  if (!contents.get()) {
4475  LOG_ERROR(74, "CDir::SetMode(): Cannot get content of: " + GetPath());
4476  return false;
4477  }
4478 
4479  bool success = true;
4480  try {
4481  // Process each entry
4482  ITERATE(TEntries, entry, *contents.get()) {
4483  string name = (*entry)->GetName();
4484  if (name == "." || name == ".." ||
4485  name == string(1, GetPathSeparator())) {
4486  continue;
4487  }
4488  // Get entry item with full pathname.
4489  CDirEntry item(GetPath() + GetPathSeparator() + name);
4490  if (flags & fDir_Recursive) {
4491  // Update flags to process subdirectories itself,
4492  // because the top directory entry may not have such flag.
4493  int f = (flags & fDir_Subdirs) ? (flags | fDir_Self) : flags;
4494  if (item.IsDir(eIgnoreLinks)) {
4495  if (!CDir(item.GetPath()).SetMode(user_mode, group_mode, other_mode, special_mode, f)) {
4496  if (flags & fProcessAll) {
4497  success = false;
4498  } else {
4499  throw "Changing mode for subdirectory failed";
4500  }
4501  }
4502  }
4503  else if (flags & fDir_Files) {
4504  if (!item.SetModeEntry(user_mode, group_mode, other_mode, special_mode, f)) {
4505  if (flags & fProcessAll) {
4506  success = false;
4507  } else {
4508  throw "Changing mode for subdirectory entry failed";
4509  }
4510  }
4511  }
4512  }
4513  else if (item.IsDir(eIgnoreLinks)) {
4514  // Non-recursive directory processing
4515  if (flags & fDir_Subdirs) {
4516  // Clear all flags to go inside directory,
4517  // and try to change modes for entry only.
4518  if (!CDir(item.GetPath()).SetMode(user_mode, group_mode, other_mode, special_mode,
4519  (flags & ~fDir_All) | fDir_Self)) {
4520  if (flags & fProcessAll) {
4521  success = false;
4522  } else {
4523  throw "Changing mode for subdirectory (non-recursive) failed";
4524  }
4525  }
4526  }
4527  continue;
4528  }
4529  else {
4530  if (flags & fDir_Files) {
4531  if (!item.SetModeEntry(user_mode, group_mode, other_mode, special_mode, flags)) {
4532  // Changing mode for a regular file entry failed
4533  if (flags & fProcessAll) {
4534  success = false;
4535  } else {
4536  throw "Changing mode for subdirectory entry failed";
4537  }
4538  }
4539  }
4540  }
4541  }
4542  }
4543  catch (const char* what) {
4544  // The error is may be reported already in child .SetMode() for recursive calls,
4545  // but add an additional error log for the current directory itself.
4546  LOG_ERROR(94, "CDir::SetMode(): Cannot change mode for directory: " + GetPath() + ": " + what);
4547  return false;
4548  }
4549 
4550  // Process directory entry
4551  if (flags & fDir_Self) {
4552  // Change mode for entry/directory itself.
4553  // Clear all flags to disable to go inside directory, but allow to pass all additional flags.
4554  if (!SetModeEntry(user_mode, group_mode, other_mode, special_mode, (flags & ~fDir_All) | fEntry)) {
4555  success = false;
4556  };
4557  }
4558  return success;
4559 }
4560 
4561 
4562 
4563 //////////////////////////////////////////////////////////////////////////////
4564 //
4565 // CSymLink
4566 //
4567 
4569 {
4570  return;
4571 }
4572 
4573 
4574 bool CSymLink::Create(const string& path) const
4575 {
4576 #if defined(NCBI_OS_UNIX)
4577  char buf[PATH_MAX + 1];
4578  int len = (int)readlink(_T_XCSTRING(GetPath()), buf, sizeof(buf) - 1);
4579  if (len >= 0) {
4580  buf[len] = '\0';
4581  if (strcmp(buf, path.c_str()) == 0) {
4582  return true;
4583  }
4584  }
4585  // Leave it to the kernel to decide whether the symlink can be recreated
4586  if ( symlink(_T_XCSTRING(path), _T_XCSTRING(GetPath())) == 0 ) {
4587  return true;
4588  }
4589  LOG_ERROR_ERRNO(75, "CSymLink::Create(): failed: " + path);
4590 #else
4591  LOG_ERROR_NCBI(76, "CSymLink::Create():"
4592  " Symbolic links not supported on this platform: "
4593  + path, CNcbiError::eNotSupported);
4594 #endif
4595  return false;
4596 }
4597 
4598 
4599 bool CSymLink::Copy(const string& new_path, TCopyFlags flags, size_t buf_size) const
4600 {
4601 #if defined(NCBI_OS_UNIX)
4602 
4603  // Dereference link if specified
4604  if ( F_ISSET(flags, fCF_FollowLinks) ) {
4605  switch ( GetType(eFollowLinks) ) {
4606  case eFile:
4607  return CFile(*this).Copy(new_path, flags, buf_size);
4608  case eDir:
4609  return CDir(*this).Copy(new_path, flags, buf_size);
4610  case eLink:
4611  return CSymLink(*this).Copy(new_path, flags, buf_size);
4612  default:
4613  return CDirEntry(*this).Copy(new_path, flags, buf_size);
4614  }
4615  // not reached
4616  }
4617 
4618  // The source link must exists
4619  EType src_type = GetType(eIgnoreLinks);
4620  if ( src_type == eUnknown ) {
4621  LOG_ERROR_NCBI(77, "CSymLink::Copy(): Unknown entry type " + GetPath(),
4623  return false;
4624  }
4625 
4626  CSymLink dst(new_path);
4627  EType dst_type = dst.GetType(eIgnoreLinks);
4628  bool dst_exists = (dst_type != eUnknown);
4629  string dst_safe_path; // saved path for fCF_Safe
4630 
4631  // If destination exists...
4632  if ( dst_exists ) {
4633  // Check on copying link into yourself.
4634  if ( IsIdentical(dst.GetPath()) ) {
4635  LOG_ERROR_NCBI(78, "CSymLink::Copy(): Source and destination are the same: " + GetPath(),
4637  return false;
4638  }
4639  // Can copy entries with different types?
4640  if ( F_ISSET(flags, fCF_EqualTypes) && (src_type != dst_type) ) {
4641  LOG_ERROR_NCBI(79, "CSymLink::Copy(): Cannot copy entries with different types: " + GetPath(),
4643  return false;
4644  }
4645  // Can overwrite entry?
4646  if ( !F_ISSET(flags, fCF_Overwrite) ) {
4647  LOG_ERROR_NCBI(80, "CSymLink::Copy(): Destination already exists: " + dst.GetPath(),
4649  return false;
4650  }
4651  // Copy only if destination is older
4652  if ( F_ISSET(flags, fCF_Update) && !IsNewer(dst.GetPath(), 0)) {
4653  return true;
4654  }
4655  // Backup destination entry first
4656  if ( F_ISSET(flags, fCF_Backup) ) {
4657  // Use a new CDirEntry object for 'dst', because its path
4658  // will be changed after backup
4659  CDirEntry dst_tmp(dst);
4660  if ( !dst_tmp.Backup(GetBackupSuffix(), eBackup_Rename) ) {
4661  LOG_ERROR(81, "CSymLink::Copy(): Cannot backup destination: " + dst.GetPath());
4662  return false;
4663  }
4664  }
4665  // Overwrite destination entry
4666  if ( F_ISSET(flags, fCF_Overwrite) ) {
4667  dst.Remove();
4668  }
4669  }
4670  // Safe copy -- create temporary symlink and rename later
4671  if (F_ISSET(flags, fCF_Safe)) {
4672  // Get new temporary name in the same directory
4673  string path, name, ext;
4674  SplitPath(dst.GetPath(), &path, &name, &ext);
4675  string tmp = GetTmpNameEx(path.empty() ? CDir::GetCwd() : path, name + ext + kTmpSafeSuffix);
4676  // Set new destination
4677  dst_safe_path = dst.GetPath();
4678  dst.Reset(tmp);
4679  }
4680  else {
4681  // Overwrite destination entry
4682  if (dst_exists && F_ISSET(flags, fCF_Overwrite)) {
4683  dst.Remove();
4684  }
4685  }
4686  // Copy symbolic link (create new one)
4687  char buf[PATH_MAX + 1];
4688  int len = (int)readlink(_T_XCSTRING(GetPath()), buf, sizeof(buf)-1);
4689  if (len < 1) {
4690  LOG_ERROR_ERRNO(82, "CSymLink::Copy(): Cannot read symbolic link: " + GetPath());
4691  return false;
4692  }
4693  buf[len] = '\0';
4694  if (symlink(buf, _T_XCSTRING(dst.GetPath()))) {
4695  LOG_ERROR_ERRNO(83, "CSymLink::Copy():"
4696  " Cannot create symbolic link " + dst.GetPath() +
4697  " to " + string(buf));
4698  return false;
4699  }
4700 
4701  // Safe copy -- renaming
4702  if (F_ISSET(flags, fCF_Safe)) {
4703  if (!dst.Rename(dst_safe_path, fRF_Overwrite)) {
4704  dst.Remove();
4705  LOG_ERROR_NCBI(84, "CSymLink:Copy():"
4706  " Cannot rename temporary symlink " + dst.GetPath() +
4707  " to " + dst_safe_path, CNcbiError::eIoError);
4708  return false;
4709  }
4710  }
4711  // Preserve attributes
4712  if (flags & fCF_PreserveAll) {
4713  if (!s_CopyAttrs(GetPath().c_str(), new_path.c_str(), eLink, flags)) {
4714  LOG_ERROR(100, "CSymLink::Copy(): Cannot copy permissions from " +
4715  GetPath() + " to " + new_path);
4716  return false;
4717  }
4718  }
4719  return true;
4720 
4721 #else
4722  // Windows -- regular copy
4723  return CParent::Copy(new_path, flags, buf_size);
4724 #endif
4725 }
4726 
4727 
4728 //////////////////////////////////////////////////////////////////////////////
4729 //
4730 // CFileUtil
4731 //
4732 
4733 /// Flags to get information about file system.
4734 /// Each flag corresponds to one or some fields in
4735 /// the CFileUtil::SFileSystemInfo structure.
4737  fFSI_Type = (1<<1), ///< fs_type
4738  fFSI_DiskSpace = (1<<2), ///< total_space, free_space
4739  fFSI_BlockSize = (1<<3), ///< block_size
4740  fFSI_FileNameMax = (1<<4), ///< filename_max
4741  fFSI_All = 0xFF ///< get all possible information
4742 };
4743 typedef int TFileSystemInfo; ///< Binary OR of "EFileSystemInfo"
4744 
4745 // File system identification strings
4746 struct SFileSystem {
4747  const char* name;
4749 };
4750 
4751 // File system identification table
4752 static const SFileSystem s_FileSystem[] = {
4753  { "ADFS", CFileUtil::eADFS },
4754  { "ADVFS", CFileUtil::eAdvFS },
4755  { "AFFS", CFileUtil::eAFFS },
4756  { "AUTOFS", CFileUtil::eAUTOFS },
4757  { "CACHEFS", CFileUtil::eCacheFS },
4758  { "CD9669", CFileUtil::eCDFS },
4759  { "CDFS", CFileUtil::eCDFS },
4760  { "DEVFS", CFileUtil::eDEVFS },
4761  { "DFS", CFileUtil::eDFS },
4762  { "DOS", CFileUtil::eFAT },
4763  { "EXT", CFileUtil::eExt },
4764  { "EXT2", CFileUtil::eExt2 },
4765  { "EXT3", CFileUtil::eExt3 },
4766  { "FAT", CFileUtil::eFAT },
4767  { "FAT32", CFileUtil::eFAT32 },
4768  { "FDFS", CFileUtil::eFDFS },
4769  { "FFM", CFileUtil::eFFM },
4770  { "FFS", CFileUtil::eFFS },
4771  { "HFS", CFileUtil::eHFS },
4772  { "HSFS", CFileUtil::eHSFS },
4773  { "HPFS", CFileUtil::eHPFS },
4774  { "JFS", CFileUtil::eJFS },
4775  { "LOFS", CFileUtil::eLOFS },
4776  { "MFS", CFileUtil::eMFS },
4777  { "MSFS", CFileUtil::eMSFS },
4778  { "NFS", CFileUtil::eNFS },
4779  { "NFS2", CFileUtil::eNFS },
4780  { "NFSV2", CFileUtil::eNFS },
4781  { "NFS3", CFileUtil::eNFS },
4782  { "NFSV3", CFileUtil::eNFS },
4783  { "NFS4", CFileUtil::eNFS },
4784  { "NFSV4", CFileUtil::eNFS },
4785  { "NTFS", CFileUtil::eNTFS },
4786  { "PCFS", CFileUtil::eFAT },
4787  { "PROC", CFileUtil::ePROC },
4788  { "PROCFS", CFileUtil::ePROC },
4789  { "RFS", CFileUtil::eRFS },
4790  { "SMBFS", CFileUtil::eSMBFS },
4791  { "SPECFS", CFileUtil::eSPECFS },
4792  { "TMP", CFileUtil::eTMPFS },
4793  { "UFS", CFileUtil::eUFS },
4794  { "VXFS", CFileUtil::eVxFS },
4795  { "XFS", CFileUtil::eXFS }
4796 };
4797 
4798 
4799 // Macros to get filesystem status information
4800 
4801 #define GET_STATVFS_INFO \
4802  struct statvfs st; \
4803  memset(&st, 0, sizeof(st)); \
4804  if (statvfs(path.c_str(), &st) != 0) { \
4805  CNcbiError::SetFromErrno(); \
4806  NCBI_THROW(CFileErrnoException, eFileSystemInfo, string(msg) + path); \
4807  } \
4808  info->total_space = (Uint8)st.f_bsize * st.f_blocks; \
4809  if (st.f_frsize) { \
4810  info->free_space = (Uint8)st.f_frsize * st.f_bavail; \
4811  info->block_size = (unsigned long)st.f_frsize; \
4812  } else { \
4813  info->free_space = (Uint8)st.f_bsize * st.f_bavail; \
4814  info->block_size = (unsigned long)st.f_bsize; \
4815  } \
4816  info->used_space = info->total_space - info->free_space
4817 
4818 
4819 #define GET_STATFS_INFO \
4820  struct statfs st; \
4821  memset(&st, 0, sizeof(st)); \
4822  if (statfs(path.c_str(), &st) != 0) { \
4823  CNcbiError::SetFromErrno(); \
4824  NCBI_THROW(CFileErrnoException, eFileSystemInfo, string(msg) + path); \
4825  } \
4826  info->total_space = (Uint8)st.f_bsize * st.f_blocks; \
4827  info->free_space = (Uint8)st.f_bsize * st.f_bavail; \
4828  info->used_space = info->total_space - info->free_space; \
4829  info->block_size = (unsigned long)st.f_bsize
4830 
4831 
4832 
4833 #if defined(SUPPORT_PANFS)
4834 
4835 // Auxiliary function to exit from forked process with reporting errno
4836 // on errors to specified file descriptor
4837 static void s_PipeExit(int status, int fd)
4838 {
4839  int errcode = errno;
4840  _no_warning(::write(fd, &errcode, sizeof(errcode)));
4841  ::close(fd);
4842  ::_exit(status);
4843 }
4844 
4845 // Close pipe handle
4846 #define CLOSE_PIPE_END(fd) \
4847  if (fd != -1) { \
4848  ::close(fd); \
4849  fd = -1; \
4850  }
4851 
4852 // Standard kernel calls cannot get correct information
4853 // about PANFS mounts, so we use workaround for that.
4854 //
4855 // Use external method fist, if 'ncbi_panfs.so' exists and can be loaded.
4856 // Fall back to 'pan_df' utuility (if present).
4857 // Fall back to use standard OS info if none of above works.
4858 //
4860 {
4861  DEFINE_STATIC_FAST_MUTEX(s_Mutex);
4862  CFastMutexGuard guard_mutex(s_Mutex);
4863 
4864  // TRUE if initialization has done for EXE method
4865  static bool s_InitEXE = false;
4866  static bool s_ExistEXE = false;
4867 
4868 #if defined(ALLOW_USE_NCBI_PANFS_DLL)
4869 
4870  // TRUE if initialization has done for DLL method
4871  static bool s_InitDLL = false;
4872  static FGetDiskSpace_PANFS f_GetDiskSpace = NULL;
4873 
4874  if ( !s_InitDLL ) {
4875  s_InitDLL = true;
4876 
4877 # define _TOSTRING(x) #x
4878 # define TOSTRING(x) _TOSTRING(x)
4879  const char* kNcbiPanfsDLL = "/opt/ncbi/" TOSTRING(NCBI_PLATFORM_BITS) "/lib/ncbi_panfs.so";
4880 
4881  // Check if 'ncbi_panfs.so' exists and can be loaded
4882  if ( CFile(kNcbiPanfsDLL).Exists() ) {
4883  void* handle = ::dlopen(kNcbiPanfsDLL, RTLD_NOW | RTLD_GLOBAL);
4884  const char* err = NULL;
4885  if ( handle ) {
4886  f_GetDiskSpace = (FGetDiskSpace_PANFS) ::dlsym(handle, "ncbi_GetDiskSpace_PANFS");
4887  if ( !f_GetDiskSpace ) {
4888  err = "Undefined symbol";
4889  }
4890  } else {
4891  err = "Cannot open shared object file";
4892  }
4893  if ( err ) {
4894  char* dlerr = dlerror();
4895  string msg = "Trying to get ncbi_GetDiskSpace_PANFS() function from '" +
4896  string(kNcbiPanfsDLL) + "': " + err;
4897  if ( dlerr ) {
4898  msg = msg + " (" + dlerr + ")";
4899  }
4901  if ( handle) {
4902  dlclose(handle);
4903  }
4904  }
4905  }
4906  }
4907 
4908  if ( f_GetDiskSpace ) {
4909  const char* err_msg = NULL;
4910  bool do_throw = false;
4911 
4912  int res = f_GetDiskSpace(path.c_str(), &info->total_space, &info->free_space, &err_msg);
4913  switch ( res ) {
4914 
4915  case NCBI_PANFS_OK:
4916  info->used_space = info->total_space - info->free_space;
4917  // All done, return
4918  return;
4919 
4920  case NCBI_PANFS_THROW:
4921  do_throw = true;
4922  /*FALLTHRU*/
4923 
4924  // Same processing for all errors codes, but could be detailed
4925  case NCBI_PANFS_ERR:
4926  case NCBI_PANFS_ERR_OPEN:
4927  case NCBI_PANFS_ERR_QUERY:
4928  case NCBI_PANFS_ERR_VERSION:
4929  default:
4930  {
4931  string msg = "Cannot get information for PANFS mount '"+ path + "'";
4932  if ( err_msg ) {
4933  msg += string(": ") + err_msg;
4934  }
4935  if ( do_throw ) {
4936  NCBI_THROW(CFileException, eFileSystemInfo, msg);
4937  }
4939  }
4940  }
4941  }
4942 #endif // defined(ALLOW_USE_NCBI_PANFS_DLL)
4943 
4944 
4945  // Cannot use DLL, so -- fall through to use pan_df.
4946 
4947  // -----------------------------------------
4948  // Call 'pan_df' utility and parse results
4949  // -----------------------------------------
4950 
4951  const char* kPanDF = "/opt/panfs/bin/pan_df";
4952 
4953  if ( !s_InitEXE ) {
4954  s_InitEXE = true;
4955  // Check if 'pan_df' exists
4956  if ( CFile(kPanDF).Exists() ) {
4957  s_ExistEXE = true;
4958  }
4959  }
4960 
4961  if ( s_ExistEXE ) {
4962  // Child process I/O handles
4963  int status_pipe[2] = {-1,-1};
4964  int pipe_fd[2] = {-1,-1};
4965 
4966  try {
4967  ::fflush(NULL);
4968  // Create pipe for child's stdout
4969  if (::pipe(pipe_fd) < 0) {
4970  throw "failed to create pipe for stdout";
4971  }
4972  // Create temporary pipe to get status of execution
4973  // of the child process
4974  if (::pipe(status_pipe) < 0) {
4975  throw "failed to create status pipe";
4976  }
4977  if (::fcntl(status_pipe[1], F_SETFD,
4978  ::fcntl(status_pipe[1], F_GETFD, 0) | FD_CLOEXEC) < 0) {
4979  throw "failed to set close-on-exec mode for status pipe";
4980  }
4981 
4982  // Fork child process
4983  pid_t pid = ::fork();
4984  if (pid == -1) {
4985  throw "fork() failed";
4986  }
4987  if (pid == 0) {
4988  // -- Now we are in the child process
4989 
4990  // Close unused pipe handle
4991  ::close(status_pipe[0]);
4992  // stdin/stderr -- don't use
4993  _no_warning(::freopen("/dev/null", "r", stdin));
4994  _no_warning(::freopen("/dev/null", "a", stderr));
4995  // stdout
4996  if (pipe_fd[1] != STDOUT_FILENO) {
4997  if (::dup2(pipe_fd[1], STDOUT_FILENO) < 0) {
4998  s_PipeExit(-1, status_pipe[1]);
4999  }
5000  ::close(pipe_fd[1]);
5001  }
5002  ::close(pipe_fd[0]);
5003  int status = ::execl(kPanDF, kPanDF, "--block-size=1", path.c_str(), NULL);
5004  s_PipeExit(status, status_pipe[1]);
5005 
5006  // -- End of child process
5007  }
5008 
5009  // Close unused pipes' ends
5010  CLOSE_PIPE_END(pipe_fd[1]);
5011  CLOSE_PIPE_END(status_pipe[1]);
5012 
5013  // Check status pipe.
5014  // If it have some data, this is an errno from the child process.
5015  // If EOF in status pipe, that child executed successful.
5016  // Retry if either blocked or interrupted
5017 
5018  // Try to read errno from forked process
5019  ssize_t n;
5020  int errcode;
5021  while ((n = read(status_pipe[0], &errcode, sizeof(errcode))) < 0) {
5022  if (errno != EINTR)
5023  break;
5024  }
5025  CLOSE_PIPE_END(status_pipe[0]);
5026  if (n > 0) {
5027  // Child could not run -- reap it and exit with error
5028  ::waitpid(pid, 0, 0);
5029  errno = (size_t) n >= sizeof(errcode) ? errcode : 0;
5030  throw "failed to run pan_df";
5031  }
5032 
5033  // Read data from pipe
5034  char buf[1024];
5035  while ((n = read(pipe_fd[0], &buf, sizeof(buf)-1)) < 0) {
5036  if (errno != EINTR)
5037  break;
5038  }
5039  CLOSE_PIPE_END(pipe_fd[0]);
5040  if ( !n ) {
5041  throw "error reading from pipe";
5042  }
5043  buf[n] = '\0';
5044 
5045  // Parse resilt
5046  const char* kParseError = "results parse error";
5047  const char* data = strchr(buf, '\n');
5048  if ( !data ) {
5049  throw kParseError;
5050  }
5051  vector<string> tokens;
5053  if ( tokens.size() != 6 ) {
5054  throw kParseError;
5055  }
5056  Uint8 x_total = 1, x_free = 2, x_used = 3; // dummy values
5057  try {
5058  x_total = NStr::StringToUInt8(tokens[1]);
5059  x_free = NStr::StringToUInt8(tokens[2]);
5060  x_used = NStr::StringToUInt8(tokens[3]);
5061  }
5062  catch (const CException& e) {
5063  throw kParseError;
5064  }
5065  // Check
5066  if ( x_free + x_used != x_total ) {
5067  throw kParseError;
5068  }
5069  info->total_space = x_total;
5070  info->free_space = x_free;
5071  info->used_space = x_used;
5072  return;
5073  }
5074  catch (const char* what) {
5075  CLOSE_PIPE_END(pipe_fd[0]);
5076  CLOSE_PIPE_END(pipe_fd[1]);
5077  CLOSE_PIPE_END(status_pipe[0]);
5078  CLOSE_PIPE_END(status_pipe[1]);
5079  ERR_POST_X_ONCE(3, Warning << "Failed to use 'pan_df': " << what);
5080  }
5081  } // if ( s_ExistEXE )
5082 
5083  // Failed
5084  ERR_POST_X_ONCE(3, Warning <<
5085  "Cannot use any external method to get information about "
5086  "PANFS mount, fall back to use standard OS info "
5087  "(NOTE: it can be incorrect)");
5088  return;
5089 }
5090 
5091 #endif // defined(SUPPORT_PANFS)
5092 
5093 
5094 
5095 void s_GetFileSystemInfo(const string& path,
5098 {
5099  if ( !info ) {
5100  NCBI_THROW(CCoreException, eInvalidArg,
5101  "s_GetFileSystemInfo(path, NULL) is not allowed");
5102  }
5103  memset(info, 0, sizeof(*info));
5104  const char* msg = "Cannot get system information for ";
5105  const char* fs_name_ptr = 0;
5106 
5107 #if defined(NCBI_OS_MSWIN)
5108  // Try to get a root disk directory from given path
5109  string xpath = path;
5110  // Not UNC path
5111  if (!s_Win_IsNetworkPath(path)) {
5112  if ( !isalpha((unsigned char)path[0]) || path[1] != DISK_SEPARATOR ) {
5113  // absolute or relative path without disk name -- current disk,
5114  // dir entry should exists
5115  if ( CDirEntry(path).Exists() ) {
5116  xpath = CDir::GetCwd();
5117  }
5118  }
5119  // Get disk root directory name from the path
5120  xpath[2] = '\\';
5121  xpath.resize(3);
5122  }
5123 
5124  // Get volume information
5125  TXChar fs_name[MAX_PATH+1];
5126  string ufs_name;
5127  if (flags & (fFSI_Type | fFSI_FileNameMax)) {
5128  DWORD filename_max;
5129  DWORD fs_flags;
5130 
5131  if ( !::GetVolumeInformation(_T_XCSTRING(xpath),
5132  NULL, 0, // Name of the volume
5133  NULL, // and its serial number
5134  &filename_max,
5135  &fs_flags,
5136  fs_name,
5137  sizeof(fs_name)/sizeof(fs_name[0])) ) {
5138  NCBI_THROW(CFileErrnoException, eFileSystemInfo, string(msg) + path);
5139  }
5140  info->filename_max = filename_max;
5141  ufs_name = _T_CSTRING(fs_name);
5142  fs_name_ptr = ufs_name.c_str();
5143  }
5144 
5145  // Get disk spaces
5146  if (flags & fFSI_DiskSpace) {
5147  if ( !::GetDiskFreeSpaceEx(_T_XCSTRING(xpath),
5148  (PULARGE_INTEGER)&info->free_space,
5149  (PULARGE_INTEGER)&info->total_space, 0) ) {
5150  NCBI_THROW(CFileErrnoException, eFileSystemInfo, string(msg) + path);
5151  }
5152  }
5153 
5154  // Get volume cluster size
5155  if (flags & fFSI_BlockSize) {
5156  DWORD dwSectPerClust;
5157  DWORD dwBytesPerSect;
5158  if ( !::GetDiskFreeSpace(_T_XCSTRING(xpath),
5159  &dwSectPerClust, &dwBytesPerSect,
5160  NULL, NULL) ) {
5161  NCBI_THROW(CFileErrnoException, eFileSystemInfo, string(msg) + path);
5162  }
5163  info->block_size = dwBytesPerSect * dwSectPerClust;
5164  }
5165 
5166 #else // defined(NCBI_OS_MSWIN)
5167 
5168  bool need_name_max = true;
5169 # ifdef _PC_NAME_MAX
5170  long r_name_max = pathconf(path.c_str(), _PC_NAME_MAX);
5171  if (r_name_max != -1) {
5172  info->filename_max = (unsigned long)r_name_max;
5173  need_name_max = false;
5174  }
5175 # endif
5176 
5177 # if (defined(NCBI_OS_LINUX) || defined(NCBI_OS_CYGWIN)) && defined(HAVE_STATFS)
5178 
5180  if (flags & (fFSI_Type | fFSI_DiskSpace)) {
5181  switch (st.f_type) {
5182  case 0xADF5: info->fs_type = CFileUtil::eADFS; break;
5183  case 0xADFF: info->fs_type = CFileUtil::eAFFS; break;
5184  case 0x5346414F: info->fs_type = CFileUtil::eAFS; break;
5185  case 0x0187: info->fs_type = CFileUtil::eAUTOFS; break;
5186  case 0x1BADFACE: info->fs_type = CFileUtil::eBFS; break;
5187  case 0x4004:
5188  case 0x4000:
5189  case 0x9660: info->fs_type = CFileUtil::eCDFS; break;
5190  case 0xF15F: info->fs_type = CFileUtil::eCryptFS; break;
5191  case 0xFF534D42: info->fs_type = CFileUtil::eCIFS; break;
5192  case 0x73757245: info->fs_type = CFileUtil::eCODA; break;
5193  case 0x012FF7B7: info->fs_type = CFileUtil::eCOH; break;
5194  case 0x28CD3D45: info->fs_type = CFileUtil::eCRAMFS; break;
5195  case 0x1373: info->fs_type = CFileUtil::eDEVFS; break;
5196  case 0x414A53: info->fs_type = CFileUtil::eEFS; break;
5197  case 0x5DF5: info->fs_type = CFileUtil::eEXOFS; break;
5198  case 0x137D: info->fs_type = CFileUtil::eExt; break;
5199  case 0xEF51:
5200  case 0xEF53: info->fs_type = CFileUtil::eExt2; break;
5201  case 0x4d44: info->fs_type = CFileUtil::eFAT; break;
5202  case 0x65735546: info->fs_type = CFileUtil::eFUSE; break;
5203  case 0x65735543: info->fs_type = CFileUtil::eFUSE_CTL; break;
5204  case 0x01161970: info->fs_type = CFileUtil::eGFS2; break;
5205  case 0x4244: info->fs_type = CFileUtil::eHFS; break;
5206  case 0x482B: info->fs_type = CFileUtil::eHFSPLUS; break;
5207  case 0xF995E849: info->fs_type = CFileUtil::eHPFS; break;
5208  case 0x3153464A: info->fs_type = CFileUtil::eJFS; break;
5209  case 0x07C0: info->fs_type = CFileUtil::eJFFS; break;
5210  case 0x72B6: info->fs_type = CFileUtil::eJFFS2; break;
5211  case 0x47504653: info->fs_type = CFileUtil::eGPFS; break;
5212  case 0x137F:
5213  case 0x138F: info->fs_type = CFileUtil::eMinix; break;
5214  case 0x2468:
5215  case 0x2478: info->fs_type = CFileUtil::eMinix2; break;
5216  case 0x4D5A: info->fs_type = CFileUtil::eMinix3; break;
5217  case 0x564C: info->fs_type = CFileUtil::eNCPFS; break;
5218  case 0x6969: info->fs_type = CFileUtil::eNFS; break;
5219  case 0x5346544E: info->fs_type = CFileUtil::eNTFS; break;
5220  case 0x7461636F: info->fs_type = CFileUtil::eOCFS2; break;
5221  case 0x9fA1: info->fs_type = CFileUtil::eOPENPROM; break;
5222  case 0xAAD7AAEA: info->fs_type = CFileUtil::ePANFS; break;
5223  case 0x9fA0: info->fs_type = CFileUtil::ePROC; break;
5224  case 0x20030528: info->fs_type = CFileUtil::ePVFS2; break;
5225  case 0x002F: info->fs_type = CFileUtil::eQNX4; break;
5226  case 0x52654973: info->fs_type = CFileUtil::eReiserFS; break;
5227  case 0x7275: info->fs_type = CFileUtil::eROMFS; break;
5228  case 0xF97CFF8C: info->fs_type = CFileUtil::eSELINUX; break;
5229  case 0x517B: info->fs_type = CFileUtil::eSMBFS; break;
5230  case 0x73717368: info->fs_type = CFileUtil::eSquashFS; break;
5231  case 0x62656572: info->fs_type = CFileUtil::eSYSFS; break;
5232  case 0x012FF7B6: info->fs_type = CFileUtil::eSYSV2; break;
5233  case 0x012FF7B5: info->fs_type = CFileUtil::eSYSV4; break;
5234  case 0x01021994: info->fs_type = CFileUtil::eTMPFS; break;
5235  case 0x24051905: info->fs_type = CFileUtil::eUBIFS; break;
5236  case 0x15013346: info->fs_type = CFileUtil::eUDF; break;
5237  case 0x00011954: info->fs_type = CFileUtil::eUFS; break;
5238  case 0x19540119: info->fs_type = CFileUtil::eUFS2; break;
5239  case 0x9fA2: info->fs_type = CFileUtil::eUSBDEVICE;break;
5240  case 0x012FF7B8: info->fs_type = CFileUtil::eV7; break;
5241  case 0xa501FCF5: info->fs_type = CFileUtil::eVxFS; break;
5242  case 0x565a4653: info->fs_type = CFileUtil::eVZFS; break;
5243  case 0x012FF7B4: info->fs_type = CFileUtil::eXENIX; break;
5244  case 0x58465342: info->fs_type = CFileUtil::eXFS; break;
5245  case 0x012FD16D: info->fs_type = CFileUtil::eXIAFS; break;
5246  default: info->fs_type = CFileUtil::eUnknown; break;
5247  }
5248  }
5249  if (need_name_max) {
5250  info->filename_max = (unsigned long)st.f_namelen;
5251  }
5252 
5253 # elif (defined(NCBI_OS_SOLARIS) || defined(NCBI_OS_IRIX) || defined(NCBI_OS_OSF1)) \
5254  && defined(HAVE_STATVFS)
5255 
5257  if (need_name_max) {
5258  info->filename_max = (unsigned long)st.f_namemax;
5259  }
5260  fs_name_ptr = st.f_basetype;
5261 
5262 # elif defined(NCBI_OS_DARWIN) && defined(HAVE_STATFS)
5263 
5265  // Seems statfs structure on Darwin doesn't have any information
5266  // about name length, so rely on pathconf() only (see above).
5267  // empty if - to avoid compilation warning on defined but unused variable
5268  if (need_name_max) {
5269  // info->filename_max = (unsigned long)st.f_namelen;
5270  }
5271  fs_name_ptr = st.f_fstypename;
5272 
5273 # elif defined(NCBI_OS_BSD) && defined(HAVE_STATFS)
5274 
5276  fs_name_ptr = st.f_fstypename;
5277  if (need_name_max) {
5278  info->filename_max = (unsigned long)st.f_namemax;
5279  }
5280 
5281 # elif defined(NCBI_OS_OSF1) && defined(HAVE_STATVFS)
5282 
5284  if (need_name_max) {
5285  info->filename_max = (unsigned long)st.f_namelen;
5286  }
5287  fs_name_ptr = st.f_fstypename;
5288 
5289 # else
5290 
5291  // Unknown UNIX OS
5292  #if defined(HAVE_STATVFS)
5294  #elif defined(HAVE_STATFS)
5296  #endif
5297 
5298 # endif
5299 #endif
5300 
5301  // Try to define file system type by name
5302  if ((flags & fFSI_Type) && fs_name_ptr) {
5303  for (size_t i=0;
5304  i < sizeof(s_FileSystem)/sizeof(s_FileSystem[0]); i++) {
5305  if ( NStr::EqualNocase(fs_name_ptr, s_FileSystem[i].name) ) {
5306  info->fs_type = s_FileSystem[i].type;
5307  break;
5308  }
5309  }
5310  }
5311 
5312 #if defined(SUPPORT_PANFS)
5313  // Standard kernel calls cannot get correct information
5314  // about PANFS mounts, so we use workaround for that.
5315  if ((info->fs_type == CFileUtil::ePANFS) && (flags & fFSI_DiskSpace)) {
5316  s_GetDiskSpace_PANFS(path, info);
5317  }
5318 #endif
5319 }
5320 
5321 
5323 {
5325 }
5326 
5327 
5329 {
5332  return info.free_space;
5333 }
5334 
5335 
5337 {
5340  return info.used_space;
5341 }
5342 
5343 
5345 {
5348  return info.total_space;
5349 }
5350 
5351 
5352 
5353 //////////////////////////////////////////////////////////////////////////////
5354 //
5355 // CFileDeleteList / CFileDeleteAtExit
5356 //
5357 
5359 {
5360  ITERATE (TList, path, m_Paths) {
5361  if (!CDirEntry(*path).Remove(CDirEntry::eRecursiveIgnoreMissing)) {
5362  ERR_POST_X(5, Warning << "CFileDeleteList: failed to remove path: " << *path);
5363  }
5364  }
5365 }
5366 
5367 void CFileDeleteAtExit::Add(const string& path)
5368 {
5369  s_DeleteAtExitFileList->Add(path);
5370 }
5371 
5373 {
5374  return *s_DeleteAtExitFileList;
5375 }
5376 
5378 {
5379  *s_DeleteAtExitFileList = list;
5380 }
5381 
5382 
5383 //////////////////////////////////////////////////////////////////////////////
5384 //
5385 // CTmpFile
5386 //
5387 
5388 
5390 {
5391  m_FileName = CFile::GetTmpName();
5392  if ( m_FileName.empty() ) {
5393  NCBI_THROW(CFileException, eTmpFile, "Cannot generate temporary file name");
5394  }
5395  m_RemoveOnDestruction = remove_file;
5396 }
5397 
5398 CTmpFile::CTmpFile(const string& file_name, ERemoveMode remove_file)
5399  : m_FileName(file_name),
5400  m_RemoveOnDestruction(remove_file)
5401 {
5402  return;
5403 }
5404 
5406 {
5407  // First, close and delete created streams.
5408  m_InFile.reset();
5409  m_OutFile.reset();
5410 
5411  // Remove file if specified
5412  if (m_RemoveOnDestruction == eRemove) {
5414  }
5415 }
5416 
5417  enum EIfExists {
5418  /// You can make call of AsInputFile/AsOutputFile only once,
5419  /// on each following call throws CFileException exception.
5421  /// Delete previous stream and return reference to new object.
5423  /// Return reference to current stream, or new if this is first call.
5425  };
5426 
5427  // CTmpFile