Imported Upstream version 17.25.4
[platform/upstream/libzypp.git] / zypp / RepoStatus.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/RepoStatus.cc
10  *
11 */
12 #include <iostream>
13 #include <sstream>
14 #include <fstream>
15 #include <set>
16 #include <zypp/base/Logger.h>
17 #include <zypp/base/String.h>
18 #include <zypp/RepoStatus.h>
19 #include <zypp/RepoInfo.h>
20 #include <zypp/PathInfo.h>
21
22 using std::endl;
23
24 ///////////////////////////////////////////////////////////////////
25 namespace zypp
26 {
27   ///////////////////////////////////////////////////////////////////
28   namespace
29   {
30     /** Recursive computation of max dir timestamp. */
31     void recursiveTimestamp( const Pathname & dir_r, time_t & max_r )
32     {
33       std::list<std::string> dircontent;
34       if ( filesystem::readdir( dircontent, dir_r, false/*no dots*/ ) != 0 )
35         return; // readdir logged the error
36
37       for_( it, dircontent.begin(), dircontent.end() )
38       {
39         PathInfo pi( dir_r + *it, PathInfo::LSTAT );
40         if ( pi.isDir() )
41         {
42           if ( pi.mtime() > max_r )
43             max_r = pi.mtime();
44           recursiveTimestamp( pi.path(), max_r );
45         }
46       }
47     }
48   } // namespace
49   ///////////////////////////////////////////////////////////////////
50
51   ///////////////////////////////////////////////////////////////////
52   //
53   //    CLASS NAME : RepoStatus::Impl
54   //
55   /** RepoStatus implementation. */
56   struct RepoStatus::Impl
57   {
58     using Checksums = std::set<std::string>;
59
60   public:
61     /** Assign data called from RepoStatus ctor (adds magic).
62      *
63      * \Note Changing magic will at once invalidate all solv file caches.
64      * Helpfull if solv file content must be refreshed (e.g. due to different
65      * repo2solv arguments) even if raw metadata are unchanged. Only values
66      * set from a RepoStatus ctor need magic to be added!
67      */
68     void assignFromCtor( std::string && checksum_r, Date && timestamp_r )
69     {
70       if ( !checksum_r.empty() ) {
71         static const std::string magic( "43" );
72         checksum_r += magic;
73         _checksums.insert( std::move(checksum_r) );
74       }
75       _timestamp = std::move(timestamp_r);
76     }
77
78     /** Inject raw data (no magic added). */
79     void inject( std::string && checksum_r, Date && timestamp_r )
80     {
81       if ( !checksum_r.empty() ) {
82         _checksums.insert( std::move(checksum_r) );
83         _cachedchecksum.reset();
84       }
85
86       if ( timestamp_r > _timestamp )
87         _timestamp = timestamp_r;
88     }
89
90     /** Inject the raw data from rhs */
91     void injectFrom( const Impl & rhs )
92     {
93       if ( &rhs == this )       // no self insert
94         return;
95
96       if ( !rhs._checksums.empty() ) {
97         _checksums.insert( rhs._checksums.begin(), rhs._checksums.end() );
98         _cachedchecksum.reset();
99       }
100
101       if ( rhs._timestamp > _timestamp )
102         _timestamp = rhs._timestamp;
103     }
104
105     bool empty() const
106     { return _checksums.empty(); }
107
108     std::string checksum() const
109     {
110       std::string ret;
111       if ( _checksums.empty() )
112         return ret;
113
114       if ( _checksums.size() == 1 )
115         ret = *_checksums.begin();
116       else {
117         if ( !_cachedchecksum ) {
118           std::stringstream ss;
119           for ( std::string_view c : _checksums )
120             ss << c;
121           _cachedchecksum = CheckSum::sha1(ss).checksum();
122         }
123         ret = *_cachedchecksum;
124       }
125       return ret;
126     }
127
128     Date timestamp() const
129     { return _timestamp; }
130
131     /** Dump to log file (not to/from CookieFile). */
132     std::ostream & dumpOn( std::ostream & str ) const
133     { return str << ( empty() ? "NO_REPOSTATUS" : checksum() ) << " " << time_t(_timestamp); }
134
135   private:
136     Checksums _checksums;
137     Date _timestamp;
138
139     mutable std::optional<std::string> _cachedchecksum;
140
141   private:
142     friend Impl * rwcowClone<Impl>( const Impl * rhs );
143     /** clone for RWCOW_pointer */
144     Impl * clone() const
145     { return new Impl( *this ); }
146   };
147   ///////////////////////////////////////////////////////////////////
148
149   ///////////////////////////////////////////////////////////////////
150   //
151   //    CLASS NAME : RepoStatus
152   //
153   ///////////////////////////////////////////////////////////////////
154
155   RepoStatus::RepoStatus()
156     : _pimpl( new Impl() )
157   {}
158
159   RepoStatus::RepoStatus( const Pathname & path_r )
160     : _pimpl( new Impl() )
161   {
162     PathInfo info( path_r );
163     if ( info.isExist() )
164     {
165       if ( info.isFile() )
166       {
167         _pimpl->assignFromCtor( filesystem::sha1sum( path_r ), Date( info.mtime() ) );
168       }
169       else if ( info.isDir() )
170       {
171         time_t t = info.mtime();
172         recursiveTimestamp( path_r, t );
173         _pimpl->assignFromCtor( CheckSum::sha1FromString( str::numstring( t ) ).checksum(), Date( t ) );
174       }
175     }
176   }
177
178   RepoStatus::RepoStatus( const RepoInfo & info_r )
179   : _pimpl( new Impl() )
180   {
181     _pimpl->assignFromCtor( CheckSum::sha1FromString( info_r.url().asString() ).checksum(), Date() );
182   }
183
184   RepoStatus::RepoStatus( std::string checksum_r, Date timestamp_r )
185   : _pimpl( new Impl() )
186   {
187     _pimpl->assignFromCtor( std::move(checksum_r), std::move(timestamp_r) );
188   }
189
190   RepoStatus::~RepoStatus()
191   {}
192
193   RepoStatus RepoStatus::fromCookieFile( const Pathname & path_r )
194   {
195     RepoStatus ret;
196     std::ifstream file( path_r.c_str() );
197     if ( !file )
198     {
199       WAR << "No cookie file " << path_r << endl;
200     }
201     else
202     {
203       // line := "[checksum] time_t"  !!! strip time from line
204       std::string line { str::getline( file ) };
205       Date        stmp { str::strtonum<time_t>( str::stripLastWord( line ) ) };
206       ret._pimpl->inject( std::move(line), std::move(stmp) );   // raw inject to avoid magic being added
207     }
208     return ret;
209   }
210
211   void RepoStatus::saveToCookieFile( const Pathname & path_r ) const
212   {
213     std::ofstream file(path_r.c_str());
214     if (!file) {
215       ZYPP_THROW (Exception( "Can't open " + path_r.asString() ) );
216     }
217     file << _pimpl->checksum() << " " << time_t(_pimpl->timestamp()) << endl;
218     file.close();
219   }
220
221   bool RepoStatus::empty() const
222   { return _pimpl->empty(); }
223
224   Date RepoStatus::timestamp() const
225   { return _pimpl->timestamp(); }
226
227   std::ostream & operator<<( std::ostream & str, const RepoStatus & obj )
228   { return obj._pimpl->dumpOn( str ); }
229
230   RepoStatus operator&&( const RepoStatus & lhs, const RepoStatus & rhs )
231   {
232     RepoStatus result { lhs };
233     result._pimpl->injectFrom( *rhs._pimpl );
234     return result;
235   }
236
237   bool operator==( const RepoStatus & lhs, const RepoStatus & rhs )
238   { return lhs._pimpl->checksum() == rhs._pimpl->checksum(); }
239
240   /////////////////////////////////////////////////////////////////
241 } // namespace zypp
242 ///////////////////////////////////////////////////////////////////