are created as needed.
- exchange exchanges two files or directories located on the same
filesystem. Mainly used when creating new config files in a temp
file and later exchanging it with the original one.
- fixed assert_dir returning OK, if the path alsready exists but isn't a
directory.
- updated testcases
#include <boost/test/auto_unit_test.hpp>
#include "zypp/base/Logger.h"
+#include "zypp/base/LogControl.h"
#include "zypp/base/Exception.h"
#include "zypp/PathInfo.h"
#include "zypp/TmpPath.h"
Pathname subdir("text with spaces");
// create a fake file
BOOST_CHECK_EQUAL( filesystem::mkdir(dir.path() + subdir), 0 );
-
+
Pathname filepath = (dir.path() + subdir+ "filename");
ofstream str(filepath.asString().c_str(),ofstream::out);
str << "foo bar" << endl;
str.flush();
str.close();
-
+
BOOST_CHECK( PathInfo(filepath).isExist() );
}
cout << brokenlink << " -> " << filesystem::expandlink(brokenlink) << endl;
}
+BOOST_AUTO_TEST_CASE(test_assert_dir_file)
+{
+ TmpDir root;
+
+ Pathname rfile( root/"file" );
+ BOOST_CHECK_EQUAL( filesystem::assert_file( rfile ), 0 );
+ BOOST_CHECK( PathInfo(rfile).isFile() );
+
+ Pathname rdir ( root/"dir" );
+ BOOST_CHECK_EQUAL( filesystem::assert_dir ( rdir ), 0 );
+ BOOST_CHECK( PathInfo(rdir).isDir() );
+
+ // empty path
+ Pathname path;
+ BOOST_CHECK_EQUAL( filesystem::assert_file( path ), ENOENT );
+ BOOST_CHECK_EQUAL( filesystem::assert_dir ( path ), ENOENT );
+
+ // for dirs:
+ // existing dir
+ path = rdir;
+ BOOST_CHECK_EQUAL( filesystem::assert_dir( path ), 0 );
+ BOOST_CHECK( PathInfo(path).isDir() );
+ // new dirs
+ path = rdir/"sub/subsub";
+ BOOST_CHECK_EQUAL( filesystem::assert_dir( path ), 0 );
+ BOOST_CHECK( PathInfo(path).isDir() );
+ // file in path
+ path = rfile/"sub";
+ BOOST_CHECK_EQUAL( filesystem::assert_dir( path ), ENOTDIR );
+ BOOST_CHECK( !PathInfo(path).isDir() );
+ // path is file
+ path = rfile;
+ BOOST_CHECK_EQUAL( filesystem::assert_dir( path ), EEXIST );
+ BOOST_CHECK( !PathInfo(path).isDir() );
+
+ // for files:
+ // existing file
+ path = rfile;
+ BOOST_CHECK_EQUAL( filesystem::assert_file( path ), 0 );
+ BOOST_CHECK( PathInfo(path).isFile() );
+ // new file
+ path = rdir/"sub/file";
+ BOOST_CHECK_EQUAL( filesystem::assert_file( path ), 0 );
+ BOOST_CHECK( PathInfo(path).isFile() );
+ // file in path
+ path = rfile/"sub/file";
+ BOOST_CHECK_EQUAL( filesystem::assert_file( path ), ENOTDIR );
+ BOOST_CHECK( ! PathInfo(path).isFile() );
+ // path is dir
+ path = rdir;
+ BOOST_CHECK_EQUAL( filesystem::assert_file( path ), EEXIST );
+ BOOST_CHECK( ! PathInfo(path).isFile() );
+}
+BOOST_AUTO_TEST_CASE(test_exchange)
+{
+ TmpDir root;
+ Pathname a;
+ Pathname b;
+ // paths must not be epmty:
+ BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), EINVAL );
+ a = root/"a/p";
+ BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), EINVAL );
+ b = root/"b/p";
+ BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 ); // ok if both don't exist
+
+ // one path not existing:
+ filesystem::assert_file( a );
+ BOOST_CHECK( PathInfo(a).isFile() );
+ BOOST_CHECK( !PathInfo(b).isFile() );
+
+ BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 );
+ BOOST_CHECK( !PathInfo(a).isFile() );
+ BOOST_CHECK( PathInfo(b).isFile() );
+
+ BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 );
+ BOOST_CHECK( PathInfo(a).isFile() );
+ BOOST_CHECK( !PathInfo(b).isFile() );
+
+ // both paths exist:
+ filesystem::assert_dir( b );
+ BOOST_CHECK( PathInfo(b).isDir() );
+
+ BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 );
+ BOOST_CHECK( PathInfo(a).isDir() );
+ BOOST_CHECK( PathInfo(b).isFile() );
+
+ BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), 0 );
+ BOOST_CHECK( PathInfo(a).isFile() );
+ BOOST_CHECK( PathInfo(b).isDir() );
+
+ // Exchange with location that can't be created:
+ BOOST_CHECK_EQUAL( chmod( b.dirname(), 0555 ), 0 );
+ BOOST_CHECK_EQUAL( filesystem::exchange( a, b ), EACCES );
+ BOOST_CHECK( PathInfo(a).isFile() );
+ BOOST_CHECK( PathInfo(b).isDir() );
+}
#include "zypp/ExternalProgram.h"
#include "zypp/PathInfo.h"
#include "zypp/Digest.h"
+#include "zypp/TmpPath.h"
using std::endl;
using std::string;
**
** DESCRIPTION : Helper function to log return values.
*/
- inline int _Log_Result( const int res, const char * rclass = "errno" )
+#define _Log_Result MIL << endl, __Log_Result
+ inline int __Log_Result( const int res, const char * rclass = 0 /*errno*/ )
{
- MIL << endl;
if ( res )
- WAR << " FAILED: " << rclass << " " << res << endl;
+ {
+ if ( rclass )
+ WAR << " FAILED: " << rclass << " " << res << endl;
+ else
+ WAR << " FAILED: " << str::strerror( res ) << endl;
+ }
return res;
}
//
int assert_dir( const Pathname & path, unsigned mode )
{
- string::size_type pos, lastpos = 0;
- string spath = path.asString()+"/";
- int ret = 0;
-
- if(path.empty())
+ if ( path.empty() )
return ENOENT;
- // skip ./
- if(path.relative())
- lastpos=2;
- // skip /
- else
- lastpos=1;
+ { // Handle existing paths in advance.
+ PathInfo pi( path );
+ if ( pi.isDir() )
+ return 0;
+ if ( pi.isExist() )
+ return EEXIST;
+ }
- // MIL << "about to create " << spath << endl;
- while((pos = spath.find('/',lastpos)) != string::npos )
+ string spath = path.asString()+"/";
+ string::size_type lastpos = ( path.relative() ? 2 : 1 ); // skip leasding './' or '/'
+ string::size_type pos = string::npos;
+ int ret = 0;
+
+ while ( (pos = spath.find('/',lastpos)) != string::npos )
+ {
+ string dir( spath.substr(0,pos) );
+ ret = ::mkdir( dir.c_str(), mode );
+ if ( ret == -1 )
{
- string dir = spath.substr(0,pos);
- ret = ::mkdir(dir.c_str(), mode);
- if(ret == -1)
- {
- // ignore errors about already existing directorys
- if(errno == EEXIST)
- ret=0;
- else
- {
- ret=errno;
- WAR << " FAILED: mkdir " << dir << ' ' << str::octstring( mode ) << " errno " << ret << endl;
- }
- }
+ if ( errno == EEXIST ) // ignore errors about already existing paths
+ ret = 0;
else
{
- MIL << "mkdir " << dir << ' ' << str::octstring( mode ) << endl;
+ ret = errno;
+ WAR << " FAILED: mkdir " << dir << ' ' << str::octstring( mode ) << " errno " << ret << endl;
}
- lastpos = pos+1;
}
+ else
+ {
+ MIL << "mkdir " << dir << ' ' << str::octstring( mode ) << endl;
+ }
+ lastpos = pos+1;
+ }
+
return ret;
}
///////////////////////////////////////////////////////////////////
//
+ // METHOD NAME : exchange
+ // METHOD TYPE : int
+ //
+ int exchange( const Pathname & lpath, const Pathname & rpath )
+ {
+ MIL << "exchange " << lpath << " <-> " << rpath;
+ if ( lpath.empty() || rpath.empty() )
+ return _Log_Result( EINVAL );
+
+ PathInfo linfo( lpath );
+ PathInfo rinfo( rpath );
+
+ if ( ! linfo.isExist() )
+ {
+ if ( ! rinfo.isExist() )
+ return _Log_Result( 0 ); // both don't exist.
+
+ // just rename rpath -> lpath
+ int ret = assert_dir( lpath.dirname() );
+ if ( ret != 0 )
+ return _Log_Result( ret );
+ if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
+ return _Log_Result( errno );
+ }
+ return _Log_Result( 0 );
+ }
+
+ // HERE: lpath exists:
+ if ( ! rinfo.isExist() )
+ {
+ // just rename lpath -> rpath
+ int ret = assert_dir( rpath.dirname() );
+ if ( ret != 0 )
+ return _Log_Result( ret );
+ if ( ::rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
+ return _Log_Result( errno );
+ }
+ return _Log_Result( 0 );
+ }
+
+ // HERE: both exist
+ TmpFile tmpfile( TmpFile::makeSibling( rpath ) );
+ if ( ! tmpfile )
+ return _Log_Result( errno );
+ Pathname tmp( tmpfile.path() );
+ ::unlink( tmp.c_str() );
+
+ if ( ::rename( lpath.c_str(), tmp.c_str() ) == -1 ) {
+ return _Log_Result( errno );
+ }
+ if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
+ ::rename( tmp.c_str(), lpath.c_str() );
+ return _Log_Result( errno );
+ }
+ if ( ::rename( tmp.c_str(), rpath.c_str() ) == -1 ) {
+ ::rename( lpath.c_str(), rpath.c_str() );
+ ::rename( tmp.c_str(), lpath.c_str() );
+ return _Log_Result( errno );
+ }
+ return _Log_Result( 0 );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
// METHOD NAME : copy
// METHOD TYPE : int
//
///////////////////////////////////////////////////////////////////
//
+ // METHOD NAME : getUmask
+ // METHOD TYPE : mode_t
+ //
+ int assert_file( const Pathname & path, unsigned mode )
+ {
+ int ret = assert_dir( path.dirname() );
+ MIL << "assert_file " << str::octstring( mode ) << " " << path;
+ if ( ret != 0 )
+ return _Log_Result( ret );
+
+ PathInfo pi( path );
+ if ( pi.isExist() )
+ return _Log_Result( pi.isFile() ? 0 : EEXIST );
+
+ int fd = ::creat( path.c_str(), mode );
+ if ( fd == -1 )
+ return _Log_Result( errno );
+
+ ::close( fd );
+ return _Log_Result( 0 );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
// METHOD NAME : touch
// METHOD TYPE : int
//
/** \name File related functions. */
//@{
/**
+ * Create an empty file if it does not yet exist. Make parent directories
+ * as needed. mode specifies the permissions to use. It is modified by the
+ * process's umask in the usual way.
+ *
+ * @return 0 on success, errno on failure
+ **/
+ int assert_file( const Pathname & path, unsigned mode = 0644 );
+
+ /**
* Change file's modification and access times.
*
* \return 0 on success, errno on failure
**/
int rename( const Pathname & oldpath, const Pathname & newpath );
+ /** Exchanges two files or directories.
+ *
+ * Most common use is when building a new config file (or dir)
+ * in a tempfile. After the job is done, configfile and tempfile
+ * are exchanged. This includes moving away the configfile in case
+ * the tempfile does not exist. Parent directories are created as
+ * needed.
+ *
+ * \note Paths are exchanged using \c ::rename, so take care both paths
+ * are located on the same filesystem.
+ *
+ * \code
+ * Pathname configfile( "/etc/myconfig" );
+ * TmpFile newconfig( TmpFile::makeSibling( configfile ) );
+ * // now write the new config:
+ * std::ofstream o( newconfig.path().c_str() );
+ * o << "mew values << endl;
+ * o.close();
+ * // If everything is fine, exchange the files:
+ * exchange( newconfig.path(), configfile );
+ * // Now the old configfile is still available at newconfig.path()
+ * // until newconfig goes out of scope.
+ * \endcode
+ *
+ * @return 0 on success, errno on failure
+ */
+ int exchange( const Pathname & lpath, const Pathname & rpath );
+
/**
* Like 'cp file dest'. Copy file to destination file.
*
/**
* Recursively follows the symlink pointed to by \a path_r and returns
* the Pathname to the real file or directory pointed to by the link.
- *
+ *
* There is a recursion limit of 256 iterations to protect against a cyclic
* link.
*
* if it is a valid link. If \a path_r is not a link, an exact copy of
* it is returned. If \a path_r is a broken or a cyclic link, an empty
* Pathname is returned and the event logged.
- */
+ */
Pathname expandlink( const Pathname & path_r );
/**