diff --git a/doc/reference.html b/doc/reference.html index 67ba273..c9b73cd 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -1807,11 +1807,11 @@ template <class Path> file_status symlink_status<

Returns:

- For status, determine the attributes + For status, determine the attributes of p as if by POSIX stat(), - for symlink_status determine the attributes as if by POSIX + for symlink_status, determine the attributes as if by POSIX lstat().

[Note: For symbolic links, stat() continues @@ -1819,14 +1819,23 @@ template <class Path> file_status symlink_status< does not. -- end note]

-

If the operating system reports an error during attribute determination:

+

If the underlying file system reports an error during attribute determination:

    -
  • If the error indicating that p could not - be resolved, as if by POSIX error codes ENOENT or ENOTDIR, set ec to 0 and return +
  • If the error indicating that p could not be resolved, as + if by POSIX errors ENOENT or ENOTDIR, call ec.clear() and return file_status(not_found_flag).
+
+
+

[Note: The effect of this behavior is to distinguish between + knowing that p + does not exist, and not being able to determine the status of p. This + distinction is important to users.  --end note]

+
+
    -
  • Otherwise, set ec to the error code reported by the operating system +
  • Otherwise, set ec to the error number reported by the underlying + implementation API and return file_status(status_unknown).
@@ -2154,22 +2163,26 @@ systems. --end note.]

basic_filesystem_error<Path1, Path2> if ec is not zero.

-
template <class Path> bool remove(const Path& p);
+
template <class Path> void remove(const Path& p, system::error_code & ec = singular );
-

Effects:  Deletes the file p resolves - to if it exists, +

Effects:  Removes the file p, as if by POSIX - - remove().

-

Returns: The value of exists(p) prior to the - establishment of the postcondition.

-

Postcondition: !exists(p)

-

Throws: basic_filesystem_error<Path> if:

+ remove()
. + If no error is reported by the underlying removal implementation or if + status(p).type() == file_not_found, then:

    -
  • exists(p) && is_directory(p) && !empty(p).
  • -
  • Effects fails for any reason other than because p - does not resolve to an existing file.
  • +
  • if ec != singular, then ec.clear().
+

Otherwise,

+
    +
  • if ec != singular, then set ec + to represent the error.
  • +
  • otherwise, throw basic_filesystem_error<Path> to represent + the error.if ec != singular, then + ec.clear()
  • +
+

Postcondition: !exists(p)

+

Throws: See Effects.

[Note: A symbolic link is itself removed, rather than the file it resolves to being removed. -- end note]

@@ -2178,8 +2191,7 @@ systems. --end note.]

Effects:  Recursively deletes the contents of p if it exists, then deletes file p itself, as if by POSIX - - remove().

+ remove()
.

Returns: The number of files removed.

Postcondition: !exists(p)

[Note: A symbolic link is itself removed, rather than the file it @@ -3068,7 +3080,7 @@ final document.

Distributed under the Boost Software License, Version 1.0. See www.boost.org/LICENSE_1_0.txt

Revised -29 June 2008

+02 July 2008

diff --git a/include/boost/filesystem/operations.hpp b/include/boost/filesystem/operations.hpp index 04f6542..20aa449 100644 --- a/include/boost/filesystem/operations.hpp +++ b/include/boost/filesystem/operations.hpp @@ -117,6 +117,10 @@ namespace boost namespace detail { + // singular object used only as a tag; thus initialization and + // thread-safety are not issues + BOOST_FILESYSTEM_DECL extern system::error_code throws; + typedef std::pair< system::error_code, bool > query_pair; @@ -454,21 +458,13 @@ namespace boost return ec; } - BOOST_FS_FUNC(bool) remove( const Path & ph ) + BOOST_FS_FUNC(void) remove( const Path & ph, system::error_code & ec = detail::throws ) { - if ( exists( ph ) - || is_symlink( ph ) ) // handle dangling symbolic links - // note that the POSIX behavior for symbolic links is what we want; - // the link rather than what it points to is deleted. Windows behavior - // doesn't matter; is_symlink() is always false on Windows. - { - system::error_code ec( detail::remove_api( ph.external_file_string() ) ); - if ( ec ) - boost::throw_exception( basic_filesystem_error( - "boost::filesystem::remove", ph, ec ) ); - return true; - } - return false; + system::error_code error( detail::remove_api(ph.external_file_string()) ); + if ( error && &ec == &detail::throws ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::remove", ph, error ) ); + ec = error; } BOOST_FS_FUNC(unsigned long) remove_all( const Path & ph ) @@ -693,10 +689,8 @@ namespace boost const wpath & from_ph, system::error_code & ec ) { return create_symlink( to_ph, from_ph, ec ); } - inline bool remove( const path & ph ) - { return remove( ph ); } - inline bool remove( const wpath & ph ) - { return remove( ph ); } + inline void remove( const path & ph ) { remove( ph ); } + inline void remove( const wpath & ph ) { remove( ph ); } inline unsigned long remove_all( const path & ph ) { return remove_all( ph ); } diff --git a/src/operations.cpp b/src/operations.cpp index 17daabc..7afd288 100644 --- a/src/operations.cpp +++ b/src/operations.cpp @@ -110,6 +110,8 @@ namespace std { using ::strcmp; using ::remove; using ::rename; } namespace { + const error_code ok; + const fs::directory_iterator end_itr; bool is_empty_directory( const std::string & dir_path ) { @@ -200,19 +202,19 @@ namespace || (ec.value() == ERROR_BAD_PATHNAME) // "//nosuch" on Win64 || (ec.value() == ERROR_BAD_NETPATH)) // "//nosuch" on Win32 { - ec = error_code(); // these are not considered errors; + ec = ok; // these are not considered errors; // the status is considered not found return fs::file_status( fs::file_not_found ); } else if ((ec.value() == ERROR_SHARING_VIOLATION)) { - ec = error_code(); // these are not considered errors; + ec = ok; // these are not considered errors; // the file exists but the type is not known return fs::file_status( fs::type_unknown ); } return fs::file_status( fs::status_unknown ); } - ec = error_code();; + ec = ok;; return (attr & FILE_ATTRIBUTE_DIRECTORY) ? fs::file_status( fs::directory_file ) : fs::file_status( fs::regular_file ); @@ -229,7 +231,7 @@ namespace WIN32_FILE_ATTRIBUTE_DATA fad; if ( get_file_attributes_ex( ph.c_str(), fad ) == 0 ) return std::make_pair( error_code( ::GetLastError(), system_category ), false ); - return std::make_pair( error_code(), + return std::make_pair( ok, ( fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ? is_empty_directory( ph ) :( !fad.nFileSizeHigh && !fad.nFileSizeLow ) ); @@ -295,7 +297,7 @@ namespace { if ( p1.handle != INVALID_HANDLE_VALUE || p2.handle != INVALID_HANDLE_VALUE ) - { return std::make_pair( error_code(), false ); } + { return std::make_pair( ok, false ); } assert( p1.handle == INVALID_HANDLE_VALUE && p2.handle == INVALID_HANDLE_VALUE ); { return std::make_pair( error_code( error1, system_category), false ); } @@ -309,7 +311,7 @@ namespace // In theory, volume serial numbers are sufficient to distinguish between // devices, but in practice VSN's are sometimes duplicated, so last write // time and file size are also checked. - return std::make_pair( error_code(), + return std::make_pair( ok, info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber && info1.nFileIndexHigh == info2.nFileIndexHigh && info1.nFileIndexLow == info2.nFileIndexLow @@ -331,7 +333,7 @@ namespace return std::make_pair( error_code( ::GetLastError(), system_category ), 0 ); if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 ) return std::make_pair( error_code( ERROR_FILE_NOT_FOUND, system_category), 0 ); - return std::make_pair( error_code(), + return std::make_pair( ok, (static_cast(fad.nFileSizeHigh) << (sizeof(fad.nFileSizeLow)*8)) + fad.nFileSizeLow ); @@ -349,7 +351,7 @@ namespace boost::filesystem::detail::space_pair result; if ( get_free_disk_space( ph, &avail, &total, &free ) ) { - result.first = error_code(); + result.first = ok; result.second.capacity = (static_cast(total.HighPart) << 32) + total.LowPart; @@ -385,7 +387,7 @@ namespace if ( get_current_directory( sz, buf.get() ) == 0 ) return error_code( ::GetLastError(), system_category ); ph = buf.get(); - return error_code(); + return ok; } inline bool set_current_directory( const char * buf ) @@ -426,11 +428,11 @@ namespace == 0 ) return error_code( ::GetLastError(), system_category ); big_buf[len] = '\0'; target = big_buf.get(); - return error_code(); + return ok; } buf[len] = '\0'; target = buf; - return error_code(); + return ok; } template @@ -517,9 +519,14 @@ namespace error_code remove_template( const String & ph ) { + // TODO: test this code in the presence of Vista symlinks, + // including dangling, self-referal, and cyclic symlinks error_code ec; fs::file_status sf( fs::detail::status_api( ph, ec ) ); - if ( ec ) return ec; + if ( ec ) + return ec; + if ( sf.type() == fs::file_not_found ) + return ok; if ( fs::is_directory( sf ) ) { if ( !remove_directory( ph ) ) @@ -529,7 +536,7 @@ namespace { if ( !delete_file( ph ) ) return error_code(::GetLastError(), system_category); } - return error_code(); + return ok; } inline bool create_directory( const std::string & dir ) @@ -545,7 +552,7 @@ namespace // an error here may simply mean the postcondition is already met if ( error.value() == ERROR_ALREADY_EXISTS && fs::is_directory( fs::detail::status_api( dir_ph, dummy ) ) ) - return std::make_pair( error_code(), false ); + return std::make_pair( ok, false ); return std::make_pair( error, false ); } @@ -566,6 +573,24 @@ namespace } #endif +#else // BOOST_POSIX_API + + int posix_remove( const char * p ) + { +# if defined(__QNXNTO__) || (defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))) + // Some Metrowerks C library versions fail on directories because of a + // known Metrowerks coding error in ::remove. Workaround is to call + // rmdir() or unlink() as indicated. + // Same bug also reported for QNX, with the same fix. + int err = ::unlink( p ); + if ( err != EPERM ) + return err; + return ::rmdir( p ) +# else + std::remove( p ); +# endif + } + #endif } // unnamed namespace @@ -575,6 +600,7 @@ namespace boost { namespace detail { + BOOST_FILESYSTEM_DECL system::error_code throws; // free functions ----------------------------------------------------------// @@ -763,7 +789,7 @@ namespace boost if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { sf.type( directory_file ); symlink_sf.type( directory_file ); } else { sf.type( regular_file ); symlink_sf.type( regular_file ); } - return error_code(); + return ok; } BOOST_FILESYSTEM_DECL error_code @@ -781,7 +807,7 @@ namespace boost if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { sf.type( directory_file ); symlink_sf.type( directory_file ); } else { sf.type( regular_file ); symlink_sf.type( regular_file ); } - return error_code(); + return ok; } # endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY @@ -889,7 +915,7 @@ namespace boost if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { sf.type( directory_file ); symlink_sf.type( directory_file ); } else { sf.type( regular_file ); symlink_sf.type( regular_file ); } - return error_code(); + return ok; } BOOST_FILESYSTEM_DECL error_code @@ -901,7 +927,7 @@ namespace boost handle = 0; return error_code( ok ? 0 : ::GetLastError(), system_category ); } - return error_code(); + return ok; } BOOST_FILESYSTEM_DECL error_code @@ -919,7 +945,7 @@ namespace boost if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { sf.type( directory_file ); symlink_sf.type( directory_file ); } else { sf.type( regular_file ); symlink_sf.type( regular_file ); } - return error_code(); + return ok; } # else // BOOST_POSIX_API @@ -932,13 +958,13 @@ namespace boost { if ( errno == ENOENT || errno == ENOTDIR ) { - ec = error_code(); + ec = ok; return fs::file_status( fs::file_not_found ); } ec = error_code( errno, system_category ); return fs::file_status( fs::status_unknown ); } - ec = error_code(); + ec = ok; if ( S_ISDIR( path_stat.st_mode ) ) return fs::file_status( fs::directory_file ); if ( S_ISREG( path_stat.st_mode ) ) @@ -962,13 +988,13 @@ namespace boost { if ( errno == ENOENT || errno == ENOTDIR ) { - ec = error_code(); + ec = ok; return fs::file_status( fs::file_not_found ); } ec = error_code( errno, system_category ); return fs::file_status( fs::status_unknown ); } - ec = error_code(); + ec = ok; if ( S_ISREG( path_stat.st_mode ) ) return fs::file_status( fs::regular_file ); if ( S_ISDIR( path_stat.st_mode ) ) @@ -1001,7 +1027,7 @@ namespace boost struct stat path_stat; if ( (::stat( ph.c_str(), &path_stat )) != 0 ) return std::make_pair( error_code( errno, system_category ), false ); - return std::make_pair( error_code(), S_ISDIR( path_stat.st_mode ) + return std::make_pair( ok, S_ISDIR( path_stat.st_mode ) ? is_empty_directory( ph ) : path_stat.st_size == 0 ); } @@ -1016,7 +1042,7 @@ namespace boost if ( e1 != 0 || e2 != 0 ) return std::make_pair( error_code( e1 != 0 && e2 != 0 ? errno : 0, system_category ), false ); // at this point, both stats are known to be valid - return std::make_pair( error_code(), + return std::make_pair( ok, s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino // According to the POSIX stat specs, "The st_ino and st_dev fields @@ -1034,7 +1060,7 @@ namespace boost return std::make_pair( error_code( errno, system_category ), 0 ); if ( !S_ISREG( path_stat.st_mode ) ) return std::make_pair( error_code( EPERM, system_category ), 0 ); - return std::make_pair( error_code(), + return std::make_pair( ok, static_cast(path_stat.st_size) ); } @@ -1051,7 +1077,7 @@ namespace boost } else { - result.first = error_code(); + result.first = ok; result.second.capacity = static_cast(vfs.f_blocks) * BOOST_STATVFS_F_FRSIZE; result.second.free @@ -1068,7 +1094,7 @@ namespace boost struct stat path_stat; if ( ::stat( ph.c_str(), &path_stat ) != 0 ) return std::make_pair( error_code( errno, system_category ), 0 ); - return std::make_pair( error_code(), path_stat.st_mtime ); + return std::make_pair( ok, path_stat.st_mtime ); } BOOST_FILESYSTEM_DECL error_code @@ -1105,7 +1131,7 @@ namespace boost break; } } - return error_code(); + return ok; } BOOST_FILESYSTEM_DECL error_code @@ -1119,13 +1145,13 @@ namespace boost create_directory_api( const std::string & ph ) { if ( ::mkdir( ph.c_str(), S_IRWXU|S_IRWXG|S_IRWXO ) == 0 ) - { return std::make_pair( error_code(), true ); } + { return std::make_pair( ok, true ); } int ec=errno; error_code dummy; if ( ec != EEXIST || !fs::is_directory( status_api( ph, dummy ) ) ) { return std::make_pair( error_code( ec, system_category ), false ); } - return std::make_pair( error_code(), false ); + return std::make_pair( ok, false ); } BOOST_FILESYSTEM_DECL error_code @@ -1147,28 +1173,19 @@ namespace boost BOOST_FILESYSTEM_DECL error_code remove_api( const std::string & ph ) { -# if defined(__QNXNTO__) || (defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))) - // Some Metrowerks C library versions fail on directories because of a - // known Metrowerks coding error in ::remove. Workaround is to call - // rmdir() or unlink() as indicated. - // Same bug also reported for QNX, with the same fix. - if ( (is_directory( ph ) - ? ::rmdir( ph.c_str() ) - : ::unlink( ph.c_str() )) != 0 ) -# else - // note that the POSIX behavior for symbolic links is what we want; - // the link rather than what it points to is deleted - if ( std::remove( ph.c_str() ) != 0 ) -# endif - { - int error = errno; - // POSIX says "If the directory is not an empty directory, rmdir() - // shall fail and set errno to EEXIST or ENOTEMPTY." - // Linux uses ENOTEMPTY, Solaris uses EEXIST. - if ( error == EEXIST ) error = ENOTEMPTY; - return error_code( error, system_category ); - } - return error_code(); + if ( posix_remove( ph.c_str() ) == 0 ) + return ok; + int error = errno; + // POSIX says "If the directory is not an empty directory, rmdir() + // shall fail and set errno to EEXIST or ENOTEMPTY." + // Linux uses ENOTEMPTY, Solaris uses EEXIST. + if ( error == EEXIST ) error = ENOTEMPTY; + + error_code ec; + + // ignore errors if post-condition satisfied + return status_api(ph, ec).type() == file_not_found + ? ok : error_code( error, system_category ) ; } BOOST_FILESYSTEM_DECL error_code @@ -1249,7 +1266,7 @@ namespace boost else max = static_cast( tmp + 1 ); // relative root } result = max; - return error_code(); + return ok; } BOOST_FILESYSTEM_DECL error_code @@ -1268,7 +1285,7 @@ namespace boost dirent de; buffer = std::malloc( (sizeof(dirent) - sizeof(de.d_name)) + path_size + 1 ); // + 1 for "/0" - return error_code(); + return ok; } BOOST_FILESYSTEM_DECL error_code @@ -1276,7 +1293,7 @@ namespace boost { std::free( buffer ); buffer = 0; - if ( handle == 0 ) return error_code(); + if ( handle == 0 ) return ok; DIR * h( static_cast(handle) ); handle = 0; return error_code( ::closedir( h ) == 0 ? 0 : errno, system_category ); @@ -1339,7 +1356,7 @@ namespace boost # else sf = symlink_sf = fs::file_status( fs::status_unknown ); # endif - return error_code(); + return ok; } # endif diff --git a/test/operations_test.cpp b/test/operations_test.cpp index ae7bc3d..9504796 100644 --- a/test/operations_test.cpp +++ b/test/operations_test.cpp @@ -769,18 +769,18 @@ int test_main( int argc, char * argv[] ) BOOST_CHECK( !fs::exists( d2 / "d20" ) ); BOOST_CHECK( fs::exists( d1 / "f2" ) ); - // remove() tests on file + // remove() file file_ph = dir / "shortlife"; BOOST_CHECK( !fs::exists( file_ph ) ); create_file( file_ph, "" ); BOOST_CHECK( fs::exists( file_ph ) ); BOOST_CHECK( !fs::is_directory( file_ph ) ); - BOOST_CHECK( fs::remove( file_ph ) ); + fs::remove( file_ph ); BOOST_CHECK( !fs::exists( file_ph ) ); - BOOST_CHECK( !fs::remove( "no-such-file" ) ); - BOOST_CHECK( !fs::remove( "no-such-directory/no-such-file" ) ); + fs::remove( "no-such-file" ); + fs::remove( "no-such-directory/no-such-file" ); - // remove() test on directory + // remove() directory d1 = dir / "shortlife_dir"; BOOST_CHECK( !fs::exists( d1 ) ); fs::create_directory( d1 ); @@ -789,44 +789,68 @@ int test_main( int argc, char * argv[] ) BOOST_CHECK( BOOST_FS_IS_EMPTY( d1 ) ); bad_remove_dir = dir; BOOST_CHECK( CHECK_EXCEPTION( bad_remove, ENOTEMPTY ) ); - BOOST_CHECK( fs::remove( d1 ) ); + fs::remove( d1 ); BOOST_CHECK( !fs::exists( d1 ) ); -// STLport is allergic to std::system, so don't use runtime platform test -# ifdef BOOST_POSIX + if ( create_symlink_ok ) // only if symlinks supported + { + // remove() dangling symbolic link + fs::path link( "dangling_link" ); + fs::remove( link ); + BOOST_CHECK( !fs::is_symlink( link ) ); + BOOST_CHECK( !fs::exists( link ) ); + fs::create_symlink( "nowhere", link ); + BOOST_CHECK( !fs::exists( link ) ); + BOOST_CHECK( fs::is_symlink( link ) ); + fs::remove( link ); + BOOST_CHECK( !fs::is_symlink( link ) ); - // remove() test on dangling symbolic link - fs::path link( "dangling_link" ); - fs::remove( link ); - BOOST_CHECK( !fs::is_symlink( link ) ); - BOOST_CHECK( !fs::exists( link ) ); - std::system("ln -s nowhere dangling_link"); - BOOST_CHECK( !fs::exists( link ) ); - BOOST_CHECK( fs::is_symlink( link ) ); - BOOST_CHECK( fs::remove( link ) ); - BOOST_CHECK( !fs::is_symlink( link ) ); + // remove() self-refering symbolic link + link = "link_to_self"; + fs::remove( link ); + BOOST_CHECK( !fs::is_symlink( link ) ); + BOOST_CHECK( !fs::exists( link ) ); + fs::create_symlink( link, link ); + fs::remove( link ); + BOOST_CHECK( !fs::exists( link ) ); + BOOST_CHECK( !fs::is_symlink( link ) ); - // remove() test on symbolic link to a file - file_ph = "link_target"; - fs::remove( file_ph ); - BOOST_CHECK( !fs::exists( file_ph ) ); - create_file( file_ph, "" ); - BOOST_CHECK( fs::exists( file_ph ) ); - BOOST_CHECK( !fs::is_directory( file_ph ) ); - BOOST_CHECK( fs::is_regular( file_ph ) ); - std::system("ln -s link_target non_dangling_link"); - link = "non_dangling_link"; - BOOST_CHECK( fs::exists( link ) ); - BOOST_CHECK( !fs::is_directory( link ) ); - BOOST_CHECK( fs::is_regular( link ) ); - BOOST_CHECK( fs::is_symlink( link ) ); - BOOST_CHECK( fs::remove( link ) ); - BOOST_CHECK( fs::exists( file_ph ) ); - BOOST_CHECK( !fs::exists( link ) ); - BOOST_CHECK( !fs::is_symlink( link ) ); - BOOST_CHECK( fs::remove( file_ph ) ); - BOOST_CHECK( !fs::exists( file_ph ) ); -# endif + // remove() cyclic symbolic link + link = "link_to_a"; + fs::path link2( "link_to_b" ); + fs::remove( link ); + fs::remove( link2 ); + BOOST_CHECK( !fs::is_symlink( link ) ); + BOOST_CHECK( !fs::exists( link ) ); + fs::create_symlink( link, link2 ); + fs::create_symlink( link2, link ); + fs::remove( link ); + fs::remove( link2 ); + BOOST_CHECK( !fs::exists( link ) ); + BOOST_CHECK( !fs::exists( link2 ) ); + BOOST_CHECK( !fs::is_symlink( link ) ); + + // remove() symbolic link to file + file_ph = "link_target"; + fs::remove( file_ph ); + BOOST_CHECK( !fs::exists( file_ph ) ); + create_file( file_ph, "" ); + BOOST_CHECK( fs::exists( file_ph ) ); + BOOST_CHECK( !fs::is_directory( file_ph ) ); + BOOST_CHECK( fs::is_regular( file_ph ) ); + link = "non_dangling_link"; + fs::create_symlink( file_ph, link ); + BOOST_CHECK( fs::exists( link ) ); + BOOST_CHECK( !fs::is_directory( link ) ); + BOOST_CHECK( fs::is_regular( link ) ); + BOOST_CHECK( fs::is_symlink( link ) ); + fs::remove( link ); + BOOST_CHECK( fs::exists( file_ph ) ); + BOOST_CHECK( !fs::exists( link ) ); + BOOST_CHECK( !fs::is_symlink( link ) ); + fs::remove( file_ph ); + BOOST_CHECK( !fs::exists( file_ph ) ); + } // write time tests