Index: boost/filesystem/operations.hpp =================================================================== --- boost/filesystem/operations.hpp (revision 43591) +++ boost/filesystem/operations.hpp (working copy) @@ -143,10 +143,8 @@ BOOST_FILESYSTEM_DECL file_status status_api( const std::string & ph, system::error_code & ec ); -# ifndef BOOST_WINDOWS_API BOOST_FILESYSTEM_DECL file_status symlink_status_api( const std::string & ph, system::error_code & ec ); -# endif BOOST_FILESYSTEM_DECL query_pair is_empty_api( const std::string & ph ); BOOST_FILESYSTEM_DECL query_pair @@ -187,6 +185,8 @@ BOOST_FILESYSTEM_DECL boost::filesystem::file_status status_api( const std::wstring & ph, system::error_code & ec ); + BOOST_FILESYSTEM_DECL file_status + symlink_status_api( const std::wstring & ph, system::error_code & ec ); BOOST_FILESYSTEM_DECL query_pair is_empty_api( const std::wstring & ph ); BOOST_FILESYSTEM_DECL query_pair @@ -255,11 +255,7 @@ BOOST_INLINE_FS_FUNC(file_status) symlink_status( const Path & ph, system::error_code & ec ) -# ifdef BOOST_WINDOWS_API - { return detail::status_api( ph.external_file_string(), ec ); } -# else { return detail::symlink_status_api( ph.external_file_string(), ec ); } -# endif BOOST_FS_FUNC(file_status) symlink_status( const Path & ph ) @@ -318,11 +314,6 @@ } BOOST_FS_FUNC(bool) is_symlink( -# ifdef BOOST_WINDOWS_API - const Path & ) - { - return false; -# else const Path & ph) { system::error_code ec; @@ -331,7 +322,6 @@ boost::throw_exception( basic_filesystem_error( "boost::filesystem::is_symlink", ph, ec ) ); return is_symlink( result ); -# endif } // VC++ 7.0 and earlier has a serious namespace bug that causes a clash Index: libs/filesystem/test/operations_test.cpp =================================================================== --- libs/filesystem/test/operations_test.cpp (revision 43591) +++ libs/filesystem/test/operations_test.cpp (working copy) @@ -670,6 +670,47 @@ BOOST_CHECK( fs::create_symlink( "doesnotexist", "", ec ) ); BOOST_CHECK( ec ); + // directory symbolic link tests + from_ph = dir / "d4"; + BOOST_CHECK( !fs::exists( from_ph ) ); + fs::path to_ph( dir / "d5" ); + BOOST_CHECK( fs::create_directory( to_ph ) ); + BOOST_CHECK( fs::exists( to_ph ) ); + bool create_dir_symlink_ok(true); + try { fs::create_symlink( to_ph, from_ph ); } + catch ( const fs::filesystem_error & ex ) + { + create_dir_symlink_ok = false; + std::cout + << "create_symlink() attempt failed\n" + << "filesystem_error.what() reports: " << ex.what() << '\n' + << "create_symlink() for directories may not be supported on this file system\n"; + } + + if ( create_dir_symlink_ok ) + { + std::cout << "create_symlink() succeeded for the directory\n"; + BOOST_CHECK( fs::exists( from_ph ) ); + BOOST_CHECK( fs::is_symlink( from_ph ) ); + BOOST_CHECK( fs::exists( to_ph ) ); + BOOST_CHECK( fs::equivalent( from_ph, to_ph ) ); + stat = fs::symlink_status( from_ph ); + BOOST_CHECK( fs::exists( stat ) ); + BOOST_CHECK( !fs::is_directory( stat ) ); + BOOST_CHECK( !fs::is_regular( stat ) ); + BOOST_CHECK( !fs::is_other( stat ) ); + BOOST_CHECK( fs::is_symlink( stat ) ); + + // remove_all() test on symbolic link to a directory + fs::path child_ph( to_ph / "child"); + create_file( child_ph, "foobar1" ); + BOOST_CHECK( fs::exists( child_ph ) ); + BOOST_CHECK( fs::exists( from_ph / "child" ) ); + BOOST_CHECK( fs::remove_all( from_ph ) ); + BOOST_CHECK( !fs::exists( from_ph ) ); + BOOST_CHECK( fs::exists( child_ph ) ); + } + // there was an inital bug in directory_iterator that caused premature // close of an OS handle. This block will detect regression. { Index: libs/filesystem/src/operations.cpp =================================================================== --- libs/filesystem/src/operations.cpp (revision 43591) +++ libs/filesystem/src/operations.cpp (working copy) @@ -54,6 +54,9 @@ # if defined(BOOST_WINDOWS_API) # include +# if _WIN32_WINNT >= 0x0500 +# include +# endif # if defined(__BORLANDC__) || defined(__MWERKS__) # if defined(__BORLANDC__) using std::time_t; @@ -117,6 +120,71 @@ } #ifdef BOOST_WINDOWS_API + +# if _WIN32_WINNT >= 0x0500 + const DWORD mount_point_tag = 0xA0000003; + + struct reparse_data_header + { + DWORD tag; + WORD length; + WORD reserved; + }; + + struct mount_point_header + { + WORD sub_name_offset; + WORD sub_name_length; + WORD print_name_offset; + WORD print_name_length; + }; + + error_code set_mount_point( HANDLE handle, + const wchar_t* sub_name, std::size_t sub_name_length, + const wchar_t* print_name, std::size_t print_name_length ) + { + std::size_t full_size = + sizeof(reparse_data_header) + sizeof(mount_point_header) + + (sub_name_length+1 + print_name_length+1) * sizeof(wchar_t); + + boost::scoped_array buf( new char[full_size] ); + char * buf_ptr = buf.get(); + + reparse_data_header top_head; + top_head.tag = mount_point_tag; + top_head.length = full_size - sizeof(top_head); + top_head.reserved = 0; + std::memcpy( buf_ptr, &top_head, sizeof(top_head) ); + buf_ptr += sizeof(top_head); + + mount_point_header mt_head; + mt_head.sub_name_offset = 0; + mt_head.sub_name_length = sub_name_length * sizeof(wchar_t); + mt_head.print_name_offset = mt_head.sub_name_length + sizeof(wchar_t); + mt_head.print_name_length = print_name_length * sizeof(wchar_t); + std::memcpy( buf_ptr, &mt_head, sizeof(mt_head) ); + buf_ptr += sizeof(mt_head); + + std::memcpy( buf_ptr, sub_name, sub_name_length * sizeof(wchar_t) ); + buf_ptr += sub_name_length * sizeof(wchar_t); + std::memset( buf_ptr, 0, sizeof(wchar_t) ); + buf_ptr += sizeof(wchar_t); + + std::memcpy( buf_ptr, print_name, print_name_length * sizeof(wchar_t) ); + buf_ptr += print_name_length * sizeof(wchar_t); + std::memset( buf_ptr, 0, sizeof(wchar_t) ); + + DWORD dummy = 0; + if ( ::DeviceIoControl( handle, FSCTL_SET_REPARSE_POINT, + buf.get(), full_size, 0, 0, &dummy, 0 ) == 0 ) + { + return error_code( ::GetLastError(), system_category ); + } + + return error_code(); + } +# endif + // For Windows, the xxxA form of various function names is used to avoid // inadvertently getting wide forms of the functions. (The undecorated @@ -182,38 +250,100 @@ inline bool create_hard_link( const std::wstring & to_ph, const std::wstring & from_ph ) { return ::CreateHardLinkW( from_ph.c_str(), to_ph.c_str(), 0 ) != 0; } + + error_code create_symbolic_link( const std::wstring & from_ph, + const std::wstring & to_ph, DWORD flags ) + { + typedef BOOL (APIENTRY * func_type)( LPCWSTR, LPCWSTR, DWORD ); + + const wchar_t* from_s = from_ph.c_str(); + const wchar_t* to_s = to_ph.c_str(); + + error_code ec( ERROR_NOT_SUPPORTED, system_category ); + HMODULE dll( ::LoadLibraryA("kernel32.dll") ); + func_type func = reinterpret_cast( + ::GetProcAddress( dll, "CreateSymbolicLinkW" ) ); + if ( func ) + { + BOOL res = (*func)(from_s, to_s, flags); + if ( res ) { ec = error_code(); } + else { ec = error_code( ::GetLastError(), system_category ); } + } + ::FreeLibrary( dll ); + return ec; + } + + error_code set_mount_point( HANDLE handle, const std::wstring & ph ) + { + std::wstring sub_name(L"\\\?\?\\"); + if ((ph[0] == L'\\') && (ph[1] == L'\\')) + sub_name += L"UNC"; + sub_name += ph; + + return set_mount_point( + handle, sub_name.c_str(), sub_name.size(), ph.c_str(), ph.size() ); + } #endif # endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY + inline fs::file_status make_status_error( error_code & ec ) + { + ec = error_code( ::GetLastError(), system_category ); + if ((ec.value() == ERROR_FILE_NOT_FOUND) + || (ec.value() == ERROR_PATH_NOT_FOUND) + || (ec.value() == ERROR_INVALID_NAME) // "tools/jam/src/:sys:stat.h", "//foo" + || (ec.value() == ERROR_INVALID_PARAMETER) // ":sys:stat.h" + || (ec.value() == ERROR_BAD_PATHNAME) // "//nosuch" on Win64 + || (ec.value() == ERROR_BAD_NETPATH)) // "//nosuch" on Win32 + { + ec = error_code(); // 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; + // the file exists but the type is not known + return fs::file_status( fs::type_unknown ); + } + return fs::file_status( fs::status_unknown ); + } + template< class String > - fs::file_status status_template( const String & ph, error_code & ec ) + fs::file_status symlink_status_template( const String & ph, error_code & ec ) { DWORD attr( get_file_attributes( ph.c_str() ) ); if ( attr == 0xFFFFFFFF ) + { return make_status_error( ec ); } + ec = error_code();; + if (attr & FILE_ATTRIBUTE_REPARSE_POINT) + return fs::file_status( fs::symlink_file ); + return (attr & FILE_ATTRIBUTE_DIRECTORY) + ? fs::file_status( fs::directory_file ) + : fs::file_status( fs::regular_file ); + } + + template< class String > + fs::file_status status_template( const String & ph, error_code & ec ) + { + fs::file_status sf( symlink_status_template( ph, ec ) ); + if ( !fs::is_symlink( sf ) ) + return sf; + + handle_wrapper hw( create_file( ph.c_str(), 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ) ); + if ( hw.handle == INVALID_HANDLE_VALUE ) + { return make_status_error( ec ); } + BY_HANDLE_FILE_INFORMATION info; + if ( !::GetFileInformationByHandle( hw.handle, &info ) ) { ec = error_code( ::GetLastError(), system_category ); - if ((ec.value() == ERROR_FILE_NOT_FOUND) - || (ec.value() == ERROR_PATH_NOT_FOUND) - || (ec.value() == ERROR_INVALID_NAME) // "tools/jam/src/:sys:stat.h", "//foo" - || (ec.value() == ERROR_INVALID_PARAMETER) // ":sys:stat.h" - || (ec.value() == ERROR_BAD_PATHNAME) // "//nosuch" on Win64 - || (ec.value() == ERROR_BAD_NETPATH)) // "//nosuch" on Win32 - { - ec = error_code(); // 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; - // 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();; - return (attr & FILE_ATTRIBUTE_DIRECTORY) + ec = error_code(); + return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? fs::file_status( fs::directory_file ) : fs::file_status( fs::regular_file ); } @@ -223,6 +353,13 @@ { return ::GetFileAttributesExA( ph, ::GetFileExInfoStandard, &fad ); } template< class String > + bool symbolic_link_exists_template( const String & ph ) + { + DWORD attr( get_file_attributes( ph.c_str() ) ); + return ( attr != 0xFFFFFFFF ) && (attr & FILE_ATTRIBUTE_REPARSE_POINT); + } + + template< class String > boost::filesystem::detail::query_pair is_empty_template( const String & ph ) { @@ -517,11 +654,15 @@ error_code remove_template( const String & ph ) { - error_code ec; - fs::file_status sf( fs::detail::status_api( ph, ec ) ); - if ( ec ) return ec; - if ( fs::is_directory( sf ) ) + DWORD attr( get_file_attributes( ph.c_str() ) ); + if ( attr == 0xFFFFFFFF ) { + error_code ec; + make_status_error( ec ); + return ec; + } + if (attr & FILE_ATTRIBUTE_DIRECTORY) + { if ( !remove_directory( ph ) ) return error_code(::GetLastError(), system_category); } @@ -544,7 +685,7 @@ error = error_code( ::GetLastError(), system_category ); // 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 ) ) ) + && fs::is_directory( fs::detail::symlink_status_api( dir_ph, dummy ) ) ) return std::make_pair( error_code(), false ); return std::make_pair( error, false ); } @@ -553,6 +694,60 @@ inline bool create_hard_link( const std::string & to_ph, const std::string & from_ph ) { return ::CreateHardLinkA( from_ph.c_str(), to_ph.c_str(), 0 ) != 0; } + + error_code create_symbolic_link( const std::string & from_ph, + const std::string & to_ph, DWORD flags ) + { + typedef BOOL (APIENTRY * func_type)( LPCSTR, LPCSTR, DWORD ); + + const char* from_s = from_ph.c_str(); + const char* to_s = to_ph.c_str(); + + error_code ec( ERROR_NOT_SUPPORTED, system_category ); + HMODULE dll( ::LoadLibraryA("kernel32.dll") ); + func_type func = reinterpret_cast( + ::GetProcAddress( dll, "CreateSymbolicLinkA" ) ); + if ( func ) + { + BOOL res = (*func)(from_s, to_s, flags); + if ( res ) { ec = error_code(); } + else { ec = error_code( ::GetLastError(), system_category ); } + } + ::FreeLibrary( dll ); + return ec; + } + + error_code set_mount_point( HANDLE handle, const std::string & ph ) + { + int w_size = + ::MultiByteToWideChar( CP_ACP, 0, ph.c_str(), ph.size(), 0, 0 ); + if (w_size == 0) + return error_code( ::GetLastError(), system_category ); + + bool is_unc = (ph[0] == '\\') && (ph[1] == '\\'); + + std::size_t prefix_length = is_unc ? 7 : 4; + std::size_t sub_name_length = prefix_length + w_size; + + boost::scoped_array sub_name( new wchar_t[sub_name_length] ); + + wchar_t * ptr = sub_name.get(); + std::memcpy(ptr, L"\\\?\?\\", 4*sizeof(wchar_t)); + ptr += 4; + if (is_unc) + { + std::memcpy(ptr, L"UNC", 3*sizeof(wchar_t)); + ptr += 3; + } + + w_size = + ::MultiByteToWideChar( CP_ACP, 0, ph.c_str(), ph.size(), ptr, w_size ); + if (w_size == 0) + return error_code( ::GetLastError(), system_category ); + + return set_mount_point( + handle, &sub_name[0], sub_name_length, ptr, w_size ); + } #endif #if _WIN32_WINNT >= 0x500 @@ -564,6 +759,73 @@ return error_code( create_hard_link( to_ph.c_str(), from_ph.c_str() ) ? 0 : ::GetLastError(), system_category ); } + + template + class remove_directory_monitor + { + public: + explicit remove_directory_monitor( const String & ph ) + : m_path(ph), m_need_remove(true) + { + } + + ~remove_directory_monitor() + { + if (m_need_remove) + remove_directory( m_path ); + } + + void release() + { + m_need_remove = false; + } + + private: + const String & m_path; + bool m_need_remove; + }; + + template + error_code + create_directory_symlink_template( + const String & to_ph, const String & from_ph ) + { + if ( !create_directory( from_ph ) ) + return error_code( ::GetLastError(), system_category ); + + remove_directory_monitor guard( from_ph ); + + handle_wrapper hw( + create_file( + from_ph.c_str(), GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, 0 ) ); + + error_code ec = set_mount_point( hw.handle, to_ph ); + if ( !ec ) + guard.release(); + return ec; + } + + template + error_code + create_symlink_template( + const String & to_ph, const String & from_ph ) + { + error_code ec; + fs::file_status sf( fs::detail::symlink_status_api( to_ph, ec ) ); + if ( ec ) return ec; + if ( fs::is_directory( sf ) ) + { + ec = create_symbolic_link( from_ph, to_ph, 0 ); + if ( ec && ( ec == error_code( ERROR_NOT_SUPPORTED, system_category ) ) ) + { return create_directory_symlink_template( to_ph, from_ph ); } + else + { return ec; } + } + else + return create_symbolic_link( from_ph, to_ph, 0 ); + } #endif #endif @@ -603,15 +865,24 @@ status_api( const std::string & ph, error_code & ec ) { return status_template( ph, ec ); } + BOOST_FILESYSTEM_DECL fs::file_status + symlink_status_api( const std::string & ph, error_code & ec ) + { return symlink_status_template( ph, ec ); } + # ifndef BOOST_FILESYSTEM_NARROW_ONLY BOOST_FILESYSTEM_DECL fs::file_status status_api( const std::wstring & ph, error_code & ec ) { return status_template( ph, ec ); } - BOOST_FILESYSTEM_DECL bool symbolic_link_exists_api( const std::wstring & ) - { return false; } + BOOST_FILESYSTEM_DECL fs::file_status + symlink_status_api( const std::wstring & ph, error_code & ec ) + { return symlink_status_template( ph, ec ); } + BOOST_FILESYSTEM_DECL bool + symbolic_link_exists_api( const std::wstring & ph ) + { return symbolic_link_exists_template( ph ); } + BOOST_FILESYSTEM_DECL fs::detail::query_pair is_empty_api( const std::wstring & ph ) { return is_empty_template( ph ); } @@ -662,10 +933,17 @@ { return create_hard_link_template( to_ph, from_ph ); } #endif +#if _WIN32_WINNT >= 0x500 BOOST_FILESYSTEM_DECL error_code + create_symlink_api( const std::wstring & to_ph, + const std::wstring & from_ph ) + { return create_symlink_template( to_ph, from_ph ); } +#else + BOOST_FILESYSTEM_DECL error_code create_symlink_api( const std::wstring & /*to_ph*/, const std::wstring & /*from_ph*/ ) { return error_code( ERROR_NOT_SUPPORTED, system_category ); } +#endif BOOST_FILESYSTEM_DECL error_code remove_api( const std::wstring & ph ) { return remove_template( ph ); } @@ -787,8 +1065,9 @@ # endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY // suggested by Walter Landry - BOOST_FILESYSTEM_DECL bool symbolic_link_exists_api( const std::string & ) - { return false; } + BOOST_FILESYSTEM_DECL bool + symbolic_link_exists_api( const std::string & ph ) + { return symbolic_link_exists_template( ph ); } BOOST_FILESYSTEM_DECL fs::detail::query_pair is_empty_api( const std::string & ph ) @@ -842,10 +1121,19 @@ } #endif +#if _WIN32_WINNT >= 0x500 BOOST_FILESYSTEM_DECL error_code + create_symlink_api( const std::string & to_ph, + const std::string & from_ph ) + { + return create_symlink_template( to_ph, from_ph ); + } +#else + BOOST_FILESYSTEM_DECL error_code create_symlink_api( const std::string & /*to_ph*/, const std::string & /*from_ph*/ ) { return error_code( ERROR_NOT_SUPPORTED, system_category ); } +#endif BOOST_FILESYSTEM_DECL error_code remove_api( const std::string & ph ) { return remove_template( ph ); }