libzypp 17.38.3
PathInfo.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12
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
27
30#include <zypp-core/Digest.h>
32
33using std::endl;
34using std::string;
35
37namespace zypp
38{
40 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
55 EMUMOUT( FT_DIR );
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 }
430
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:
675 this->type = FileType::FT_NOT_AVAIL;
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 L_BASEFILE
Definition Logger.h:133
#define DBG
Definition Logger.h:99
#define MIL
Definition Logger.h:100
#define ERR
Definition Logger.h:102
#define WAR
Definition Logger.h:101
#define ZYPP_BASE_LOGGER_LOGGROUP
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition AutoDispose.h:95
Store and operate with byte count.
Definition ByteCount.h:32
std::string digest()
get hex string representation of the digest
Definition Digest.cc:239
Convenience errno wrapper.
Definition Errno.h:26
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
int close() override
Wait for the progamm to complete.
std::string receiveLine()
Read one line from the input stream.
Wrapper class for stat/lstat.
Definition PathInfo.h:226
bool lstat(const Pathname &path)
LSTAT path.
Definition PathInfo.h:269
StatMode asStatMode() const
Return st_mode() as filesystem::StatMode.
Definition PathInfo.h:336
bool stat()
STAT current path.
Definition PathInfo.h:274
mode_t st_mode() const
Definition PathInfo.h:332
const Pathname & path() const
Return current Pathname.
Definition PathInfo.h:251
bool operator()()
Restat current path using current mode.
Definition PathInfo.cc:189
Mode
stat() or lstat()
Definition PathInfo.h:231
unsigned int devMinor() const
Definition PathInfo.cc:252
FileType fileType() const
Definition PathInfo.cc:213
bool lstat()
LSTAT current path.
Definition PathInfo.h:276
mode_t userMay() const
Returns current users permission ([0-7]).
Definition PathInfo.cc:225
bool isExist() const
Return whether valid stat info exists.
Definition PathInfo.h:286
const std::string & asString() const
Return current Pathname as String.
Definition PathInfo.h:253
unsigned int devMajor() const
Definition PathInfo.cc:242
int error() const
Return error returned from last stat/lstat call.
Definition PathInfo.h:259
bool stat(const Pathname &path)
STAT path.
Definition PathInfo.h:267
Pathname dirname() const
Return all but the last component od this path.
Definition Pathname.h:133
const char * c_str() const
String representation.
Definition Pathname.h:113
const std::string & asString() const
String representation.
Definition Pathname.h:94
std::string basename() const
Return the last component of this path.
Definition Pathname.h:137
bool empty() const
Test for an empty path.
Definition Pathname.h:117
bool relative() const
Test for a relative path.
Definition Pathname.h:121
FileType fileType() const
Definition PathInfo.cc:71
StatMode(const mode_t &mode_r=0)
Ctor taking mode_t value from stat.
Definition PathInfo.h:91
Provide a new empty temporary file and delete it when no longer needed.
Definition TmpPath.h:118
static TmpFile makeSibling(const Pathname &sibling_r)
Provide a new empty temporary directory as sibling.
Definition TmpPath.cc:182
Pathname path() const
Definition TmpPath.cc:124
Definition ansi.h:855
String related utilities and Regular expression matching.
@ E_MIL
Milestone.
Definition Logger.h:159
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.
Types and functions for filesystem operations.
Definition Glob.cc:24
int chmod(const Pathname &path, mode_t mode)
Like 'chmod'.
Definition PathInfo.cc:1111
int symlink(const Pathname &oldpath, const Pathname &newpath)
Like 'symlink'.
Definition PathInfo.cc:874
std::ostream & operator<<(std::ostream &str, const Glob &obj)
Definition Glob.cc:53
std::string checksum(const Pathname &file, const std::string &algorithm)
Compute a files checksum.
Definition PathInfo.cc:1070
int delmod(const Pathname &path, mode_t mode)
Remove the mode bits from the file given by path.
Definition PathInfo.cc:1132
int dirForEachImpl(const Pathname &dir_r, F &&fnc_r)
Definition PathInfo.cc:566
int rmdir(const Pathname &path)
Like 'rmdir'.
Definition PathInfo.cc:385
int mkdir(const Pathname &path, unsigned mode)
Like 'mkdir'.
Definition PathInfo.cc:324
static int recursive_rmdir_1(const Pathname &dir, bool removeDir=true)
Definition PathInfo.cc:399
FileType
File type information.
Definition PathInfo.h:60
int assert_file(const Pathname &path, unsigned mode)
Create an empty file if it does not yet exist.
Definition PathInfo.cc:1205
int copy_dir(const Pathname &srcpath, const Pathname &destpath)
Like 'cp -a srcpath destpath'.
Definition PathInfo.cc:482
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
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
Definition PathInfo.h:813
int recursive_rmdir(const Pathname &path)
Like 'rm -r DIR'.
Definition PathInfo.cc:431
ByteCount df(const Pathname &path_r)
Report free disk space on a mounted file system.
Definition PathInfo.cc:1177
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
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 copy(const Pathname &file, const Pathname &dest)
Like 'cp file dest'.
Definition PathInfo.cc:839
int clean_dir(const Pathname &path)
Like 'rm -r DIR/ *'.
Definition PathInfo.cc:461
bool is_checksum(const Pathname &file, const CheckSum &checksum)
check files checksum
Definition PathInfo.cc:1082
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 unlink(const Pathname &path)
Like 'unlink'.
Definition PathInfo.cc:719
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 dirForEach(const Pathname &dir_r, const StrMatcher &matcher_r, function< bool(const Pathname &, const char *const)> fnc_r)
Definition PathInfo.cc:32
int erase(const Pathname &path)
Erase whatever happens to be located at path (file or directory).
Definition PathInfo.cc:1092
int addmod(const Pathname &path, mode_t mode)
Add the mode bits to the file given by path.
Definition PathInfo.cc:1123
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition PathInfo.cc:338
int readlink(const Pathname &symlink_r, Pathname &target_r)
Like 'readlink'.
Definition PathInfo.cc:943
int copy_file2dir(const Pathname &file, const Pathname &dest)
Like 'cp file dest'.
Definition PathInfo.cc:1009
mode_t getUmask()
Get the current umask (file mode creation mask).
Definition PathInfo.cc:1193
int copy_dir_content(const Pathname &srcpath, const Pathname &destpath)
Like 'cp -a srcpath/.
Definition PathInfo.cc:523
int dirForEachExt(const Pathname &dir_r, const function< bool(const Pathname &, const DirEntry &)> &fnc_r)
Simiar to.
Definition PathInfo.cc:612
constexpr bool always_false
Definition PathInfo.cc:563
int is_empty_dir(const Pathname &path_r)
Check if the specified directory is empty.
Definition PathInfo.cc:707
std::string md5sum(const Pathname &file)
Compute a files md5sum.
Definition PathInfo.cc:1043
int hardlink(const Pathname &oldpath, const Pathname &newpath)
Like 'link'.
Definition PathInfo.cc:888
int exchange(const Pathname &lpath, const Pathname &rpath)
Exchanges two files or directories.
Definition PathInfo.cc:775
std::string sha1sum(const Pathname &file)
Compute a files sha1sum.
Definition PathInfo.cc:1060
ZIP_TYPE zipType(const Pathname &file)
Definition PathInfo.cc:1146
ZIP_TYPE
Test whether a file is compressed (gzip/bzip2).
Definition PathInfo.h:778
int rename(const Pathname &oldpath, const Pathname &newpath)
Like 'rename'.
Definition PathInfo.cc:761
int chmodApplyUmask(const Pathname &path, mode_t mode)
Similar to 'chmod', but mode is modified by the process's umask in the usual way.
Definition PathInfo.cc:1120
int touch(const Pathname &path)
Change file's modification and access times.
Definition PathInfo.cc:1256
boost::io::ios_base_all_saver IosFmtFlagsSaver
Save and restore streams width, precision and fmtflags.
Definition IOStream.h:36
std::string octstring(char n, int w=4)
Definition String.h:419
std::string strerror(int errno_r)
Return string describing the error_r code.
Definition String.cc:56
Easy-to use interface to the ZYPP dependency resolver.
std::ostream & dumpRange(std::ostream &str, TIterator begin, TIterator end, const std::string &intro="{", const std::string &pfx="\n ", const std::string &sep="\n ", const std::string &sfx="\n", const std::string &extro="}")
Print range defined by iterators (multiline style).
Definition LogTools.h:404
std::string asString(const Patch::Category &obj)
Definition Patch.cc:122
Listentry returned by readdir.
Definition PathInfo.h:517
bool operator==(const DirEntry &rhs) const
Definition PathInfo.cc:680
DirEntry(std::string name_r=std::string(), FileType type_r=FT_NOT_AVAIL)
Definition PathInfo.h:520
#define EMUMOUT(T)
#define logResult(...)
Definition PathInfo.cc:285