libzypp  17.38.7
PathInfo.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
13 #include <utime.h> // for ::utime
14 #include <sys/statvfs.h>
15 #include <sys/sysmacros.h> // for ::minor, ::major macros
16 
17 #include <iostream>
18 #include <fstream>
19 #include <iomanip>
20 #include <utility>
21 
22 #include <zypp-core/fs/PathInfo.h>
24 #include <zypp-core/base/String.h>
26 #include <zypp-core/base/Errno.h>
27 
28 #include <zypp-core/AutoDispose.h>
30 #include <zypp-core/Digest.h>
31 #include <zypp-core/fs/TmpPath.h>
32 
33 using std::endl;
34 using std::string;
35 
37 namespace zypp
38 {
39  namespace filesystem
41  {
42 
43  /******************************************************************
44  **
45  ** FUNCTION NAME : operator<<
46  ** FUNCTION TYPE : std::ostream &
47  */
48  std::ostream & operator<<( std::ostream & str, FileType obj )
49  {
50  switch ( obj ) {
51 #define EMUMOUT(T) case T: return str << #T; break
54  EMUMOUT( FT_FILE );
55  EMUMOUT( FT_DIR );
58  EMUMOUT( FT_FIFO );
59  EMUMOUT( FT_LINK );
60  EMUMOUT( FT_SOCKET );
61 #undef EMUMOUT
62  }
63  return str;
64  }
65 
67  //
68  // METHOD NAME : StatMode::fileType
69  // METHOD TYPE : FileType
70  //
72  {
73  if ( isFile() )
74  return FT_FILE;
75  if ( isDir() )
76  return FT_DIR;
77  if ( isLink() )
78  return FT_LINK;
79  if ( isChr() )
80  return FT_CHARDEV;
81  if ( isBlk() )
82  return FT_BLOCKDEV;
83  if ( isFifo() )
84  return FT_FIFO;
85  if ( isSock() )
86  return FT_SOCKET ;
87 
88  return FT_NOT_AVAIL;
89  }
90 
91  /******************************************************************
92  **
93  ** FUNCTION NAME : operator<<
94  ** FUNCTION TYPE : std::ostream &
95  */
96  std::ostream & operator<<( std::ostream & str, const StatMode & obj )
97  {
98  iostr::IosFmtFlagsSaver autoResoreState( str );
99 
100  char t = '?';
101  if ( obj.isFile() )
102  t = '-';
103  else if ( obj.isDir() )
104  t = 'd';
105  else if ( obj.isLink() )
106  t = 'l';
107  else if ( obj.isChr() )
108  t = 'c';
109  else if ( obj.isBlk() )
110  t = 'b';
111  else if ( obj.isFifo() )
112  t = 'p';
113  else if ( obj.isSock() )
114  t = 's';
115 
116  str << t << " " << std::setfill( '0' ) << std::setw( 4 ) << std::oct << obj.perm();
117  return str;
118  }
119 
121  //
122  // Class : PathInfo
123  //
125 
127  //
128  // METHOD NAME : PathInfo::PathInfo
129  // METHOD TYPE : Constructor
130  //
132  : mode_e( STAT )
133  , error_i( -1 )
134  {}
135 
137  //
138  // METHOD NAME : PathInfo::PathInfo
139  // METHOD TYPE : Constructor
140  //
142  : path_t(std::move( path ))
143  , mode_e( initial )
144  , error_i( -1 )
145  {
146  operator()();
147  }
148 
150  //
151  // METHOD NAME : PathInfo::PathInfo
152  // METHOD TYPE : Constructor
153  //
154  PathInfo::PathInfo( const std::string & path, Mode initial )
155  : path_t( path )
156  , mode_e( initial )
157  , error_i( -1 )
158  {
159  operator()();
160  }
161 
163  //
164  // METHOD NAME : PathInfo::PathInfo
165  // METHOD TYPE : Constructor
166  //
167  PathInfo::PathInfo( const char * path, Mode initial )
168  : path_t( path )
169  , mode_e( initial )
170  , error_i( -1 )
171  {
172  operator()();
173  }
174 
176  //
177  // METHOD NAME : PathInfo::~PathInfo
178  // METHOD TYPE : Destructor
179  //
181  {
182  }
183 
185  //
186  // METHOD NAME : PathInfo::operator()
187  // METHOD TYPE : bool
188  //
190  {
191  if ( path_t.empty() ) {
192  error_i = -1;
193  } else {
194  switch ( mode_e ) {
195  case STAT:
196  error_i = ::stat( path_t.asString().c_str(), &statbuf_C );
197  break;
198  case LSTAT:
199  error_i = ::lstat( path_t.asString().c_str(), &statbuf_C );
200  break;
201  }
202  if ( error_i == -1 )
203  error_i = errno;
204  }
205  return !error_i;
206  }
207 
209  //
210  // METHOD NAME : PathInfo::fileType
211  // METHOD TYPE : File_type
212  //
214  {
215  if ( isExist() )
216  return asStatMode().fileType();
217  return FT_NOT_EXIST;
218  }
219 
221  //
222  // METHOD NAME : PathInfo::userMay
223  // METHOD TYPE : mode_t
224  //
225  mode_t PathInfo::userMay() const
226  {
227  if ( !isExist() )
228  return 0;
229  if ( owner() == geteuid() ) {
230  return( uperm()/0100 );
231  } else if ( group() == getegid() ) {
232  return( gperm()/010 );
233  }
234  return operm();
235  }
236 
237  /******************************************************************
238  **
239  ** FUNCTION NAME : PathInfo::devMajor
240  ** FUNCTION TYPE : unsigned int
241  */
242  unsigned int PathInfo::devMajor() const
243  {
244  return isBlk() || isChr() ? major(statbuf_C.st_rdev) : 0;
245  }
246 
247  /******************************************************************
248  **
249  ** FUNCTION NAME : PathInfo::devMinor
250  ** FUNCTION TYPE : unsigned int
251  */
252  unsigned int PathInfo::devMinor() const
253  {
254  return isBlk() || isChr() ? minor(statbuf_C.st_rdev) : 0;
255  }
256 
257  /******************************************************************
258  **
259  ** FUNCTION NAME : operator<<
260  ** FUNCTION TYPE : std::ostream &
261  */
262  std::ostream & operator<<( std::ostream & str, const PathInfo & obj )
263  {
264  iostr::IosFmtFlagsSaver autoResoreState( str );
265 
266  str << obj.asString() << "{";
267  if ( !obj.isExist() ) {
268  str << Errno( obj.error() );
269  } else {
270  str << obj.asStatMode() << " " << std::dec << obj.owner() << "/" << obj.group();
271 
272  if ( obj.isFile() )
273  str << " size " << obj.size();
274  }
275 
276  return str << "}";
277  }
278 
280  //
281  // filesystem utilities
282  //
284 
285 #define logResult( ... ) doLogResult( __FUNCTION__, __LINE__, __VA_ARGS__ )
286  namespace {
288  inline int doLogResult( const char *function, const int line, const int res, const char * rclass = 0 /*errno*/ )
289  {
290  // calling code has started a logline via: `MIL << "Some text";` but did not flush it via endl yet.
291  // we need to do this here but pass the actual function and line to getStream, because the last call to it before
292  // flushing is setting the logging location ( function/line ).
294  if ( res )
295  {
296  if ( rclass )
297  WAR << " FAILED: " << rclass << " " << res << endl;
298  else
299  WAR << " FAILED: " << str::strerror( res ) << endl;
300  }
301  return res;
302  }
303  } // namespace
304 
305 
306  bool userMayWriteOrCreateDir( const Pathname & path_r )
307  {
308  if ( path_r.empty() )
309  return false;
310  PathInfo pi { path_r };
311  if ( pi.isDir() && pi.userMayW() )
312  return true; // MayWrite
313  do {
314  pi.stat( pi.path().dirname() );
315  } while ( not pi.isExist() && pi.path().asString().size() > 1 ); // stop at "/" or "."
316  return ( pi.isDir() && pi.userMayW() );
317  }
318 
320  //
321  // METHOD NAME : PathInfo::mkdir
322  // METHOD TYPE : int
323  //
324  int mkdir( const Pathname & path, unsigned mode )
325  {
326  MIL << "mkdir " << path << ' ' << str::octstring( mode );
327  if ( ::mkdir( path.asString().c_str(), mode ) == -1 ) {
328  return logResult( errno );
329  }
330  return logResult( 0 );
331  }
332 
334  //
335  // METHOD NAME : assert_dir()
336  // METHOD TYPE : int
337  //
338  int assert_dir( const Pathname & path, unsigned mode )
339  {
340  if ( path.empty() )
341  return ENOENT;
342 
343  { // Handle existing paths in advance.
344  PathInfo pi( path );
345  if ( pi.isDir() )
346  return 0;
347  if ( pi.isExist() )
348  return EEXIST;
349  }
350 
351  string spath = path.asString()+"/";
352  std::string::size_type lastpos = ( path.relative() ? 2 : 1 ); // skip leasding './' or '/'
353  std::string::size_type pos = std::string::npos;
354  int ret = 0;
355 
356  while ( (pos = spath.find('/',lastpos)) != std::string::npos )
357  {
358  string dir( spath.substr(0,pos) );
359  ret = ::mkdir( dir.c_str(), mode );
360  if ( ret == -1 )
361  {
362  if ( errno == EEXIST ) // ignore errors about already existing paths
363  ret = 0;
364  else
365  {
366  ret = errno;
367  WAR << " FAILED: mkdir " << dir << ' ' << str::octstring( mode ) << " errno " << ret << endl;
368  }
369  }
370  else
371  {
372  MIL << "mkdir " << dir << ' ' << str::octstring( mode ) << endl;
373  }
374  lastpos = pos+1;
375  }
376 
377  return ret;
378  }
379 
381  //
382  // METHOD NAME : rmdir
383  // METHOD TYPE : int
384  //
385  int rmdir( const Pathname & path )
386  {
387  MIL << "rmdir " << path;
388  if ( ::rmdir( path.asString().c_str() ) == -1 ) {
389  return logResult( errno );
390  }
391  return logResult( 0 );
392  }
393 
395  //
396  // METHOD NAME : recursive_rmdir
397  // METHOD TYPE : int
398  //
399  static int recursive_rmdir_1( const Pathname & dir, bool removeDir = true )
400  {
401  DIR * dp = nullptr;
402  struct dirent * d = nullptr;
403 
404  if ( ! (dp = opendir( dir.c_str() )) )
405  return logResult( errno );
406 
407  while ( (d = readdir(dp)) )
408  {
409  std::string direntry = d->d_name;
410  if ( direntry == "." || direntry == ".." )
411  continue;
412  Pathname new_path( dir / d->d_name );
413 
414  struct stat st;
415  if ( ! lstat( new_path.c_str(), &st ) )
416  {
417  if ( S_ISDIR( st.st_mode ) )
418  recursive_rmdir_1( new_path );
419  else
420  ::unlink( new_path.c_str() );
421  }
422  }
423  closedir( dp );
424 
425  if ( removeDir && ::rmdir( dir.c_str() ) < 0 )
426  return errno;
427 
428  return 0;
429  }
431  int recursive_rmdir( const Pathname & path )
432  {
433  MIL << "recursive_rmdir " << path << ' ';
434  PathInfo p( path );
435 
436  if ( !p.isExist() ) {
437  return logResult( 0 );
438  }
439 
440  if ( !p.isDir() ) {
441  return logResult( ENOTDIR );
442  }
443 
444  p.lstat(); // get dir symlinks
445  if ( !p.isDir() ) {
446  MIL << "unlink symlink ";
447  if ( ::unlink( path.asString().c_str() ) == -1 ) {
448  return logResult( errno );
449  }
450  return logResult( 0 );
451  }
452 
453  return logResult( recursive_rmdir_1( path ) );
454  }
455 
457  //
458  // METHOD NAME : clean_dir
459  // METHOD TYPE : int
460  //
461  int clean_dir( const Pathname & path )
462  {
463  MIL << "clean_dir " << path << ' ';
464  PathInfo p( path );
465 
466  if ( !p.isExist() ) {
467  return logResult( 0 );
468  }
469 
470  if ( !p.isDir() ) {
471  return logResult( ENOTDIR );
472  }
473 
474  return logResult( recursive_rmdir_1( path, false/* don't remove path itself */ ) );
475  }
476 
478  //
479  // METHOD NAME : copy_dir
480  // METHOD TYPE : int
481  //
482  int copy_dir( const Pathname & srcpath, const Pathname & destpath )
483  {
484  MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
485 
486  PathInfo sp( srcpath );
487  if ( !sp.isDir() ) {
488  return logResult( ENOTDIR );
489  }
490 
491  PathInfo dp( destpath );
492  if ( !dp.isDir() ) {
493  return logResult( ENOTDIR );
494  }
495 
496  PathInfo tp( destpath + srcpath.basename() );
497  if ( tp.isExist() ) {
498  return logResult( EEXIST );
499  }
500 
501 
502  const char *const argv[] = {
503  "/bin/cp",
504  "-dR",
505  "--",
506  srcpath.asString().c_str(),
507  destpath.asString().c_str(),
508  NULL
509  };
511  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
512  MIL << " " << output;
513  }
514  int ret = prog.close();
515  return logResult( ret, "returned" );
516  }
517 
519  //
520  // METHOD NAME : copy_dir_content
521  // METHOD TYPE : int
522  //
523  int copy_dir_content(const Pathname & srcpath, const Pathname & destpath)
524  {
525  MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
526 
527  PathInfo sp( srcpath );
528  if ( !sp.isDir() ) {
529  return logResult( ENOTDIR );
530  }
531 
532  PathInfo dp( destpath );
533  if ( !dp.isDir() ) {
534  return logResult( ENOTDIR );
535  }
536 
537  if ( srcpath == destpath ) {
538  return logResult( EEXIST );
539  }
540 
541  std::string src( srcpath.asString());
542  src += "/.";
543  const char *const argv[] = {
544  "/bin/cp",
545  "-dR",
546  "--",
547  src.c_str(),
548  destpath.asString().c_str(),
549  NULL
550  };
552  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
553  MIL << " " << output;
554  }
555  int ret = prog.close();
556  return logResult( ret, "returned" );
557  }
558 
560  // dirForEachImpl
562  template <class... T>
563  constexpr bool always_false = false;
564 
565  template <typename F>
566  int dirForEachImpl ( const Pathname & dir_r, F &&fnc_r )
567  {
568  AutoDispose<DIR *> dir( ::opendir( dir_r.c_str() ),
569  []( DIR * dir_r ) { if ( dir_r ) ::closedir( dir_r ); } );
570 
571  MIL << "readdir " << dir_r << ' ';
572  if ( ! dir )
573  return logResult( errno );
574  MIL << endl; // close line before callbacks are invoked.
575 
576  int ret = 0;
577  for ( struct dirent * entry = ::readdir( dir ); entry; entry = ::readdir( dir ) )
578  {
579  if ( entry->d_name[0] == '.' && ( entry->d_name[1] == '\0' || ( entry->d_name[1] == '.' && entry->d_name[2] == '\0' ) ) )
580  continue; // omitt . and ..
581 
582  // some static checks to make sure the correct func is selected
583  static_assert( !std::is_invocable_v< function<bool(const Pathname &, const char *const)>, const Pathname &, const DirEntry &> , "Invoke detection broken" );
584  static_assert( !std::is_invocable_v< function<bool(const Pathname &, const DirEntry& )>, const Pathname &, const char *> , "Invoke detection broken" );
585 
586  if constexpr ( std::is_invocable_v<F, const Pathname &, const char *const> ) {
587  if ( ! fnc_r( dir_r, entry->d_name ) ) {
588  ret = -1;
589  break;
590  }
591  } else if constexpr ( std::is_invocable_v<F, const Pathname &, const DirEntry&> ) {
592  if ( ! fnc_r( dir_r, DirEntry( entry ) ) ) {
593  ret = -1;
594  break;
595  }
596  } else {
597  static_assert( always_false<F>, "Callback not supported" );
598  }
599  }
600  return ret;
601  }
602 
603  int dirForEach( const Pathname & dir_r, const function<bool(const Pathname &, const char *const)>& fnc_r )
604  {
605  if ( ! fnc_r )
606  return 0;
607 
608  return dirForEachImpl( dir_r, fnc_r );
609  }
610 
611 
612  int dirForEachExt( const Pathname & dir_r, const function<bool(const Pathname &, const DirEntry &)> &fnc_r )
613  {
614  if ( ! fnc_r )
615  return 0;
616 
617  return dirForEachImpl( dir_r, fnc_r );
618  }
619 
621  // readdir
623 
624  int readdir( std::list<std::string> & retlist_r, const Pathname & path_r, bool dots_r )
625  {
626  retlist_r.clear();
627  return dirForEach( path_r,
628  [&]( const Pathname & dir_r, const char *const name_r )->bool
629  {
630  if ( dots_r || name_r[0] != '.' )
631  retlist_r.push_back( name_r );
632  return true;
633  } );
634  }
635 
636 
637  int readdir( std::list<Pathname> & retlist_r, const Pathname & path_r, bool dots_r )
638  {
639  retlist_r.clear();
640  return dirForEach( path_r,
641  [&]( const Pathname & dir_r, const char *const name_r )->bool
642  {
643  if ( dots_r || name_r[0] != '.' )
644  retlist_r.push_back( dir_r/name_r );
645  return true;
646  } );
647  }
648 
649  DirEntry::DirEntry( struct dirent* entry )
650  : name( str::asString( entry->d_name ) )
651  {
652  switch( entry->d_type ) {
653  case DT_BLK:
654  this->type = FileType::FT_BLOCKDEV;
655  break;
656  case DT_CHR:
657  this->type = FileType::FT_CHARDEV;
658  break;
659  case DT_DIR:
660  this->type = FileType::FT_DIR;
661  break;
662  case DT_FIFO:
663  this->type = FileType::FT_FIFO;
664  break;
665  case DT_LNK:
666  this->type = FileType::FT_LINK;
667  break;
668  case DT_REG:
669  this->type = FileType::FT_FILE;
670  break;
671  case DT_SOCK:
672  this->type = FileType::FT_SOCKET;
673  break;
674  case DT_UNKNOWN:
676  break;
677  }
678  }
679 
680  bool DirEntry::operator==( const DirEntry &rhs ) const
681  {
682  // if one of the types is not known, use the name only
683  if ( type == FT_NOT_AVAIL || rhs.type == FT_NOT_AVAIL )
684  return ( name == rhs.name );
685  return ((name == rhs.name ) && (type == rhs.type));
686  }
687 
688  int readdir( DirContent & retlist_r, const Pathname & path_r, bool dots_r, PathInfo::Mode statmode_r )
689  {
690  retlist_r.clear();
691  return dirForEach( path_r,
692  [&]( const Pathname & dir_r, const char *const name_r )->bool
693  {
694  if ( dots_r || name_r[0] != '.' )
695  retlist_r.push_back( DirEntry( name_r, PathInfo( dir_r/name_r, statmode_r ).fileType() ) );
696  return true;
697  } );
698  }
699 
700  std::ostream & operator<<( std::ostream & str, const DirContent & obj )
701  { return dumpRange( str, obj.begin(), obj.end() ); }
702 
704  // is_empty_dir
706 
707  int is_empty_dir( const Pathname & path_r )
708  {
709  return dirForEach( path_r,
710  [&]( const Pathname & dir_r, const char *const name_r )->bool
711  { return false; } );
712  }
713 
715  //
716  // METHOD NAME : unlink
717  // METHOD TYPE : int
718  //
719  int unlink( const Pathname & path )
720  {
721  MIL << "unlink " << path;
722  if ( ::unlink( path.asString().c_str() ) == -1 ) {
723  return logResult( errno );
724  }
725  return logResult( 0 );
726  }
727 
729  namespace
730  {
731  int safe_rename( const Pathname & oldpath, const Pathname & newpath )
732  {
733  int ret = ::rename( oldpath.asString().c_str(), newpath.asString().c_str() );
734 
735  // rename(2) can fail on OverlayFS. Fallback to using mv(1), which is
736  // explicitly mentioned in the kernel docs to deal correctly with OverlayFS.
737  if ( ret == -1 && errno == EXDEV ) {
738  const char *const argv[] = {
739  "/usr/bin/mv",
740  oldpath.asString().c_str(),
741  newpath.asString().c_str(),
742  NULL
743  };
744  ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
745  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
746  MIL << " " << output;
747  }
748  ret = prog.close();
749  }
750 
751  return ret;
752  }
753  } // namespace
755 
757  //
758  // METHOD NAME : rename
759  // METHOD TYPE : int
760  //
761  int rename( const Pathname & oldpath, const Pathname & newpath )
762  {
763  MIL << "rename " << oldpath << " -> " << newpath;
764  if ( safe_rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
765  return logResult( errno );
766  }
767  return logResult( 0 );
768  }
769 
771  //
772  // METHOD NAME : exchange
773  // METHOD TYPE : int
774  //
775  int exchange( const Pathname & lpath, const Pathname & rpath )
776  {
777  MIL << "exchange " << lpath << " <-> " << rpath;
778  if ( lpath.empty() || rpath.empty() )
779  return logResult( EINVAL );
780 
781  PathInfo linfo( lpath );
782  PathInfo rinfo( rpath );
783 
784  if ( ! linfo.isExist() )
785  {
786  if ( ! rinfo.isExist() )
787  return logResult( 0 ); // both don't exist.
788 
789  // just rename rpath -> lpath
790  int ret = assert_dir( lpath.dirname() );
791  if ( ret != 0 )
792  return logResult( ret );
793  if ( safe_rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
794  return logResult( errno );
795  }
796  return logResult( 0 );
797  }
798 
799  // HERE: lpath exists:
800  if ( ! rinfo.isExist() )
801  {
802  // just rename lpath -> rpath
803  int ret = assert_dir( rpath.dirname() );
804  if ( ret != 0 )
805  return logResult( ret );
806  if ( safe_rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
807  return logResult( errno );
808  }
809  return logResult( 0 );
810  }
811 
812  // HERE: both exist
813  TmpFile tmpfile( TmpFile::makeSibling( rpath ) );
814  if ( ! tmpfile )
815  return logResult( errno );
816  Pathname tmp( tmpfile.path() );
817  ::unlink( tmp.c_str() );
818 
819  if ( safe_rename( lpath.c_str(), tmp.c_str() ) == -1 ) {
820  return logResult( errno );
821  }
822  if ( safe_rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
823  safe_rename( tmp.c_str(), lpath.c_str() );
824  return logResult( errno );
825  }
826  if ( safe_rename( tmp.c_str(), rpath.c_str() ) == -1 ) {
827  safe_rename( lpath.c_str(), rpath.c_str() );
828  safe_rename( tmp.c_str(), lpath.c_str() );
829  return logResult( errno );
830  }
831  return logResult( 0 );
832  }
833 
835  //
836  // METHOD NAME : copy
837  // METHOD TYPE : int
838  //
839  int copy( const Pathname & file, const Pathname & dest )
840  {
841  MIL << "copy " << file << " -> " << dest << ' ';
842 
843  PathInfo sp( file );
844  if ( !sp.isFile() ) {
845  return logResult( EINVAL );
846  }
847 
848  PathInfo dp( dest );
849  if ( dp.isDir() ) {
850  return logResult( EISDIR );
851  }
852 
853  const char *const argv[] = {
854  "/bin/cp",
855  "--remove-destination",
856  "--",
857  file.asString().c_str(),
858  dest.asString().c_str(),
859  NULL
860  };
862  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
863  MIL << " " << output;
864  }
865  int ret = prog.close();
866  return logResult( ret, "returned" );
867  }
868 
870  //
871  // METHOD NAME : symlink
872  // METHOD TYPE : int
873  //
874  int symlink( const Pathname & oldpath, const Pathname & newpath )
875  {
876  MIL << "symlink " << newpath << " -> " << oldpath;
877  if ( ::symlink( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
878  return logResult( errno );
879  }
880  return logResult( 0 );
881  }
882 
884  //
885  // METHOD NAME : hardlink
886  // METHOD TYPE : int
887  //
888  int hardlink( const Pathname & oldpath, const Pathname & newpath )
889  {
890  MIL << "hardlink " << newpath << " -> " << oldpath;
891  if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
892  return logResult( errno );
893  }
894  return logResult( 0 );
895  }
896 
898  //
899  // METHOD NAME : hardlink
900  // METHOD TYPE : int
901  //
902  int hardlinkCopy( const Pathname & oldpath, const Pathname & newpath )
903  {
904  MIL << "hardlinkCopy " << oldpath << " -> " << newpath;
905 
906  PathInfo pi( oldpath, PathInfo::LSTAT );
907  if ( pi.isLink() )
908  {
909  // dont hardlink symlinks!
910  MIL << " => copy" << endl;
911  return copy( oldpath, newpath );
912  }
913 
914  pi.lstat( newpath );
915  if ( pi.isExist() )
916  {
917  int res = unlink( newpath );
918  if ( res != 0 )
919  return logResult( res );
920  }
921 
922  // Here: no symlink, no newpath
923  if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 )
924  {
925  switch ( errno )
926  {
927  case EPERM: // /proc/sys/fs/protected_hardlink in proc(5)
928  case EXDEV: // oldpath and newpath are not on the same mounted file system
929  MIL << " => copy" << endl;
930  return copy( oldpath, newpath );
931  break;
932  }
933  return logResult( errno );
934  }
935  return logResult( 0 );
936  }
937 
939  //
940  // METHOD NAME : readlink
941  // METHOD TYPE : int
942  //
943  int readlink( const Pathname & symlink_r, Pathname & target_r )
944  {
945  static const ssize_t bufsiz = 2047;
946  static char buf[bufsiz+1];
947  ssize_t ret = ::readlink( symlink_r.c_str(), buf, bufsiz );
948  if ( ret == -1 )
949  {
950  target_r = Pathname();
951  MIL << "readlink " << symlink_r;
952  return logResult( errno );
953  }
954  buf[ret] = '\0';
955  target_r = buf;
956  return 0;
957  }
958 
960  //
961  // METHOD NAME : expandlink
962  // METHOD TYPE : Pathname
963  //
964  Pathname expandlink( const Pathname & path_r )
965  {
966  static const unsigned int level_limit = 256;
967  static unsigned int count;
968  Pathname path(path_r);
969  PathInfo info(path_r, PathInfo::LSTAT);
970 
971  for (count = level_limit; info.isLink() && count; count--)
972  {
973  DBG << "following symlink " << path;
974  path = path.dirname() / readlink(path);
975  DBG << "->" << path << std::endl;
976  info = PathInfo(path, PathInfo::LSTAT);
977  }
978 
979  // expand limit reached
980  if (count == 0)
981  {
982  ERR << "Expand level limit reached. Probably a cyclic symbolic link." << endl;
983  return Pathname();
984  }
985  // symlink
986  else if (count < level_limit)
987  {
988  // check for a broken link
989  if (PathInfo(path).isExist())
990  return path;
991  // broken link, return an empty path
992  else
993  {
994  ERR << path << " is broken (expanded from " << path_r << ")" << endl;
995  return Pathname();
996  }
997  }
998 
999  // not a symlink, return the original pathname
1000  DBG << "not a symlink" << endl;
1001  return path;
1002  }
1003 
1005  //
1006  // METHOD NAME : copy_file2dir
1007  // METHOD TYPE : int
1008  //
1009  int copy_file2dir( const Pathname & file, const Pathname & dest )
1010  {
1011  MIL << "copy_file2dir " << file << " -> " << dest << ' ';
1012 
1013  PathInfo sp( file );
1014  if ( !sp.isFile() ) {
1015  return logResult( EINVAL );
1016  }
1017 
1018  PathInfo dp( dest );
1019  if ( !dp.isDir() ) {
1020  return logResult( ENOTDIR );
1021  }
1022 
1023  const char *const argv[] = {
1024  "/bin/cp",
1025  "--",
1026  file.asString().c_str(),
1027  dest.asString().c_str(),
1028  NULL
1029  };
1031  for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
1032  MIL << " " << output;
1033  }
1034  int ret = prog.close();
1035  return logResult( ret, "returned" );
1036  }
1037 
1039  //
1040  // METHOD NAME : md5sum
1041  // METHOD TYPE : std::string
1042  //
1043  std::string md5sum( const Pathname & file )
1044  {
1045  if ( ! PathInfo( file ).isFile() ) {
1046  return string();
1047  }
1048  std::ifstream istr( file.asString().c_str() );
1049  if ( ! istr ) {
1050  return string();
1051  }
1052  return Digest::digest( "MD5", istr );
1053  }
1054 
1056  //
1057  // METHOD NAME : sha1sum
1058  // METHOD TYPE : std::string
1059  //
1060  std::string sha1sum( const Pathname & file )
1061  {
1062  return checksum(file, "SHA1");
1063  }
1064 
1066  //
1067  // METHOD NAME : checksum
1068  // METHOD TYPE : std::string
1069  //
1070  std::string checksum( const Pathname & file, const std::string &algorithm )
1071  {
1072  if ( ! PathInfo( file ).isFile() ) {
1073  return string();
1074  }
1075  std::ifstream istr( file.asString().c_str() );
1076  if ( ! istr ) {
1077  return string();
1078  }
1079  return Digest::digest( algorithm, istr );
1080  }
1081 
1082  bool is_checksum( const Pathname & file, const CheckSum &checksum )
1083  {
1084  return ( filesystem::checksum(file, checksum.type()) == checksum.checksum() );
1085  }
1086 
1088  //
1089  // METHOD NAME : erase
1090  // METHOD TYPE : int
1091  //
1092  int erase( const Pathname & path )
1093  {
1094  int res = 0;
1095  PathInfo p( path, PathInfo::LSTAT );
1096  if ( p.isExist() )
1097  {
1098  if ( p.isDir() )
1099  res = recursive_rmdir( path );
1100  else
1101  res = unlink( path );
1102  }
1103  return res;
1104  }
1105 
1107  //
1108  // METHOD NAME : chmod
1109  // METHOD TYPE : int
1110  //
1111  int chmod( const Pathname & path, mode_t mode )
1112  {
1113  MIL << "chmod " << path << ' ' << str::octstring( mode );
1114  if ( ::chmod( path.asString().c_str(), mode ) == -1 ) {
1115  return logResult( errno );
1116  }
1117  return logResult( 0 );
1118  }
1119 
1120  int chmodApplyUmask( const Pathname & path, mode_t mode )
1121  { return chmod( path, applyUmaskTo( mode ) ); }
1122 
1123  int addmod( const Pathname & path, mode_t mode )
1124  {
1125  mode_t omode( PathInfo( path ).st_mode() );
1126  mode_t tmode( omode | mode );
1127  if ( omode != mode )
1128  return chmod( path, tmode );
1129  return 0;
1130  }
1131 
1132  int delmod( const Pathname & path, mode_t mode )
1133  {
1134  mode_t omode( PathInfo( path ).st_mode() );
1135  mode_t tmode( omode & ~mode );
1136  if ( omode != mode )
1137  return chmod( path, tmode );
1138  return 0;
1139  }
1140 
1142  //
1143  // METHOD NAME : zipType
1144  // METHOD TYPE : ZIP_TYPE
1145  //
1146  ZIP_TYPE zipType( const Pathname & file )
1147  {
1148  ZIP_TYPE ret = ZT_NONE;
1149 
1150  int fd = open( file.asString().c_str(), O_RDONLY|O_CLOEXEC );
1151 
1152  if ( fd != -1 ) {
1153  const int magicSize = 5;
1154  unsigned char magic[magicSize];
1155  memset( magic, 0, magicSize );
1156  if ( read( fd, magic, magicSize ) == magicSize ) {
1157  if ( magic[0] == 0037 && magic[1] == 0213 ) {
1158  ret = ZT_GZ;
1159  } else if ( magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' ) {
1160  ret = ZT_BZ2;
1161  } else if ( magic[0] == '\0' && magic[1] == 'Z' && magic[2] == 'C' && magic[3] == 'K' && magic[4] == '1') {
1162  ret = ZT_ZCHNK;
1163 
1164  }
1165  }
1166  close( fd );
1167  }
1168 
1169  return ret;
1170  }
1171 
1173  //
1174  // METHOD NAME : df
1175  // METHOD TYPE : ByteCount
1176  //
1177  ByteCount df( const Pathname & path_r )
1178  {
1179  ByteCount ret( -1 );
1180  struct statvfs sb;
1181  if ( statvfs( path_r.c_str(), &sb ) == 0 )
1182  {
1183  ret = sb.f_bfree * sb.f_bsize;
1184  }
1185  return ret;
1186  }
1187 
1189  //
1190  // METHOD NAME : getUmask
1191  // METHOD TYPE : mode_t
1192  //
1193  mode_t getUmask()
1194  {
1195  mode_t mask = ::umask( 0022 );
1196  ::umask( mask );
1197  return mask;
1198  }
1199 
1201  //
1202  // METHOD NAME : getUmask
1203  // METHOD TYPE : mode_t
1204  //
1205  int assert_file( const Pathname & path, unsigned mode )
1206  {
1207  int ret = assert_dir( path.dirname() );
1208  MIL << "assert_file " << str::octstring( mode ) << " " << path;
1209  if ( ret != 0 )
1210  return logResult( ret );
1211 
1212  PathInfo pi( path );
1213  if ( pi.isExist() )
1214  return logResult( pi.isFile() ? 0 : EEXIST );
1215 
1216  int fd = ::creat( path.c_str(), mode );
1217  if ( fd == -1 )
1218  return logResult( errno );
1219 
1220  ::close( fd );
1221  return logResult( 0 );
1222  }
1223 
1224  int assert_file_mode( const Pathname & path, unsigned mode )
1225  {
1226  int ret = assert_dir( path.dirname() );
1227  MIL << "assert_file_mode " << str::octstring( mode ) << " " << path;
1228  if ( ret != 0 )
1229  return logResult( ret );
1230 
1231  PathInfo pi( path );
1232  if ( pi.isExist() )
1233  {
1234  if ( ! pi.isFile() )
1235  return logResult( EEXIST );
1236 
1237  mode = applyUmaskTo( mode );
1238  if ( pi.st_mode() != mode )
1239  return chmod( path, mode );
1240 
1241  return logResult( 0 );
1242  }
1243 
1244  int fd = ::creat( path.c_str(), mode );
1245  if ( fd == -1 )
1246  return logResult( errno );
1247  ::close( fd );
1248  return logResult( 0 );
1249  }
1250 
1252  //
1253  // METHOD NAME : touch
1254  // METHOD TYPE : int
1255  //
1256  int touch (const Pathname & path)
1257  {
1258  MIL << "touch " << path;
1259  struct ::utimbuf times;
1260  times.actime = ::time( 0 );
1261  times.modtime = ::time( 0 );
1262  if ( ::utime( path.asString().c_str(), &times ) == -1 ) {
1263  return logResult( errno );
1264  }
1265  return logResult( 0 );
1266  }
1267 
1269  } // namespace filesystem
1272 } // namespace zypp
#define MIL
Definition: Logger.h:103
int assert_file(const Pathname &path, unsigned mode)
Create an empty file if it does not yet exist.
Definition: PathInfo.cc:1205
#define ZYPP_BASE_LOGGER_LOGGROUP
Definition: Measure.cc:27
int mkdir(const Pathname &path, unsigned mode)
Like &#39;mkdir&#39;.
Definition: PathInfo.cc:324
int exchange(const Pathname &lpath, const Pathname &rpath)
Exchanges two files or directories.
Definition: PathInfo.cc:775
ZIP_TYPE
Test whether a file is compressed (gzip/bzip2).
Definition: PathInfo.h:779
std::string digest()
get hex string representation of the digest
Definition: Digest.cc:239
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:338
Listentry returned by readdir.
Definition: PathInfo.h:517
std::string octstring(char n, int w=4)
Definition: String.h:419
#define EMUMOUT(T)
std::string sha1sum(const Pathname &file)
Compute a files sha1sum.
Definition: PathInfo.cc:1060
Convenience errno wrapper.
Definition: Errno.h:25
int clean_dir(const Pathname &path)
Like &#39;rm -r DIR/ *&#39;.
Definition: PathInfo.cc:461
std::string md5sum(const Pathname &file)
Compute a files md5sum.
Definition: PathInfo.cc:1043
int assert_file_mode(const Pathname &path, unsigned mode)
Like assert_file but enforce mode even if the file already exists.
Definition: PathInfo.cc:1224
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like &#39;readlink&#39;.
Definition: PathInfo.cc:943
std::string asString(const Patch::Category &obj)
relates: Patch::Category string representation.
Definition: Patch.cc:122
bool lstat()
LSTAT current path.
Definition: PathInfo.h:276
Store and operate with byte count.
Definition: ByteCount.h:31
#define logResult(...)
Definition: PathInfo.cc:285
bool operator==(const DirEntry &rhs) const
Definition: PathInfo.cc:680
mode_t perm() const
Definition: PathInfo.h:161
int chmod(const Pathname &path, mode_t mode)
Like &#39;chmod&#39;.
Definition: PathInfo.cc:1111
std::ostream & dumpRange(std::ostream &str, TIterator begin, TIterator end, const std::string &intro="{", const std::string &pfx="\ ", const std::string &sep="\ ", const std::string &sfx="\, const std::string &extro="}")
Print range defined by iterators (multiline style).
Definition: LogTools.h:409
int dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition: PathInfo.cc:32
const char * c_str() const
String representation.
Definition: Pathname.h:113
String related utilities and Regular expression matching.
#define L_BASEFILE
Definition: Logger.h:136
Definition: ansi.h:854
mode_t userMay() const
Returns current users permission ([0-7])
Definition: PathInfo.cc:225
Pathname path() const
Definition: TmpPath.cc:124
constexpr bool always_false
Definition: PathInfo.cc:563
mode_t uperm() const
Definition: PathInfo.h:327
std::string receiveLine()
Read one line from the input stream.
bool stat()
STAT current path.
Definition: PathInfo.h:274
std::string basename() const
Return the last component of this path.
Definition: Pathname.h:137
Provide a new empty temporary file and delete it when no longer needed.
Definition: TmpPath.h:117
unsigned int devMinor() const
Definition: PathInfo.cc:252
int copy_file2dir(const Pathname &file, const Pathname &dest)
Like &#39;cp file dest&#39;.
Definition: PathInfo.cc:1009
int copy_dir_content(const Pathname &srcpath, const Pathname &destpath)
Like &#39;cp -a srcpath/.
Definition: PathInfo.cc:523
int recursive_rmdir(const Pathname &path)
Like &#39;rm -r DIR&#39;.
Definition: PathInfo.cc:431
std::list< DirEntry > DirContent
Returned by readdir.
Definition: PathInfo.h:534
int hardlinkCopy(const Pathname &oldpath, const Pathname &newpath)
Create newpath as hardlink or copy of oldpath.
Definition: PathInfo.cc:902
#define ERR
Definition: Logger.h:105
Mode
stat() or lstat()
Definition: PathInfo.h:231
std::ostream & operator<<(std::ostream &str, const Glob &obj)
relates: Glob Stream output
Definition: Glob.cc:53
mode_t gperm() const
Definition: PathInfo.h:328
ZIP_TYPE zipType(const Pathname &file)
Definition: PathInfo.cc:1146
std::string strerror(int errno_r)
Return string describing the error_r code.
Definition: String.cc:56
bool empty() const
Test for an empty path.
Definition: Pathname.h:117
int addmod(const Pathname &path, mode_t mode)
Add the mode bits to the file given by path.
Definition: PathInfo.cc:1123
bool operator()()
Restat current path using current mode.
Definition: PathInfo.cc:189
int touch(const Pathname &path)
Change file&#39;s modification and access times.
Definition: PathInfo.cc:1256
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
const std::string & asString() const
String representation.
Definition: Pathname.h:94
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:286
int copy_dir(const Pathname &srcpath, const Pathname &destpath)
Like &#39;cp -a srcpath destpath&#39;.
Definition: PathInfo.cc:482
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:133
DirEntry(std::string name_r=std::string(), FileType type_r=FT_NOT_AVAIL)
Definition: PathInfo.h:520
int copy(const Pathname &file, const Pathname &dest)
Like &#39;cp file dest&#39;.
Definition: PathInfo.cc:839
int error() const
Return error returned from last stat/lstat call.
Definition: PathInfo.h:259
#define WAR
Definition: Logger.h:104
int close() override
Wait for the progamm to complete.
Pathname expandlink(const Pathname &path_r)
Recursively follows the symlink pointed to by path_r and returns the Pathname to the real file or dir...
Definition: PathInfo.cc:964
int is_empty_dir(const Pathname &path_r)
Check if the specified directory is empty.
Definition: PathInfo.cc:707
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition: PathInfo.cc:624
int hardlink(const Pathname &oldpath, const Pathname &newpath)
Like &#39;::link&#39;.
Definition: PathInfo.cc:888
const std::string & asString() const
Return current Pathname as String.
Definition: PathInfo.h:253
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:719
SolvableIdType size_type
Definition: poolconstants.h:59
int symlink(const Pathname &oldpath, const Pathname &newpath)
Like &#39;symlink&#39;.
Definition: PathInfo.cc:874
int dirForEachImpl(const Pathname &dir_r, F &&fnc_r)
Definition: PathInfo.cc:566
mode_t getUmask()
Get the current umask (file mode creation mask)
Definition: PathInfo.cc:1193
Wrapper class for mode_t values as derived from ::stat.
Definition: PathInfo.h:85
FileType
File type information.
Definition: PathInfo.h:59
std::string checksum(const Pathname &file, const std::string &algorithm)
Compute a files checksum.
Definition: PathInfo.cc:1070
bool userMayWriteOrCreateDir(const Pathname &path_r)
Returns whether path_r denotes an existing directory with write permission for the current user or an...
Definition: PathInfo.cc:306
int erase(const Pathname &path)
Erase whatever happens to be located at path (file or directory).
Definition: PathInfo.cc:1092
int chmodApplyUmask(const Pathname &path, mode_t mode)
Similar to &#39;chmod&#39;, but mode is modified by the process&#39;s umask in the usual way. ...
Definition: PathInfo.cc:1120
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition: AutoDispose.h:94
bool relative() const
Test for a relative path.
Definition: Pathname.h:121
unsigned int devMajor() const
Definition: PathInfo.cc:242
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:225
std::map< std::string, std::string > read(const Pathname &_path)
Read sysconfig file path_r and return (key,valye) pairs.
Definition: sysconfig.cc:34
static TmpFile makeSibling(const Pathname &sibling_r)
Provide a new empty temporary directory as sibling.
Definition: TmpPath.cc:182
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
Definition: PathInfo.h:814
FileType fileType() const
Definition: PathInfo.cc:213
int delmod(const Pathname &path, mode_t mode)
Remove the mode bits from the file given by path.
Definition: PathInfo.cc:1132
int rename(const Pathname &oldpath, const Pathname &newpath)
Like &#39;rename&#39;.
Definition: PathInfo.cc:761
std::ostream & getStream(const char *group_r, LogLevel level_r, const char *file_r, const char *func_r, const int line_r)
Return a log stream to write on.
Definition: LogControl.cc:775
int dirForEachExt(const Pathname &dir_r, const function< bool(const Pathname &, const DirEntry &)> &fnc_r)
Simiar to.
Definition: PathInfo.cc:612
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
bool lstat(const Pathname &path)
LSTAT path.
Definition: PathInfo.h:269
boost::io::ios_base_all_saver IosFmtFlagsSaver
Save and restore streams width, precision and fmtflags.
Definition: IOStream.h:36
int rmdir(const Pathname &path)
Like &#39;rmdir&#39;.
Definition: PathInfo.cc:385
mode_t operm() const
Definition: PathInfo.h:329
static int recursive_rmdir_1(const Pathname &dir, bool removeDir=true)
Definition: PathInfo.cc:399
#define DBG
Definition: Logger.h:102
bool is_checksum(const Pathname &file, const CheckSum &checksum)
check files checksum
Definition: PathInfo.cc:1082
mode_t st_mode() const
Definition: PathInfo.h:332
FileType fileType() const
Definition: PathInfo.cc:71
StatMode asStatMode() const
Return st_mode() as filesystem::StatMode.
Definition: PathInfo.h:336
ByteCount df(const Pathname &path_r)
Report free disk space on a mounted file system.
Definition: PathInfo.cc:1177