Ticket #1681: ntfs_symlink.patch

File ntfs_symlink.patch, 19.2 KB (added by Takeshi Mouri <takeshi.mouri.net@…>, 15 years ago)

patch for rev.43591

  • boost/filesystem/operations.hpp

     
    143143
    144144      BOOST_FILESYSTEM_DECL file_status
    145145        status_api( const std::string & ph, system::error_code & ec );
    146 #   ifndef BOOST_WINDOWS_API
    147146      BOOST_FILESYSTEM_DECL file_status
    148147        symlink_status_api( const std::string & ph, system::error_code & ec );
    149 #   endif
    150148      BOOST_FILESYSTEM_DECL query_pair
    151149        is_empty_api( const std::string & ph );
    152150      BOOST_FILESYSTEM_DECL query_pair
     
    187185
    188186      BOOST_FILESYSTEM_DECL  boost::filesystem::file_status
    189187        status_api( const std::wstring & ph, system::error_code & ec );
     188      BOOST_FILESYSTEM_DECL file_status
     189        symlink_status_api( const std::wstring & ph, system::error_code & ec );
    190190      BOOST_FILESYSTEM_DECL query_pair
    191191        is_empty_api( const std::wstring & ph );
    192192      BOOST_FILESYSTEM_DECL query_pair
     
    255255
    256256    BOOST_INLINE_FS_FUNC(file_status)
    257257    symlink_status( const Path & ph, system::error_code & ec )
    258 #   ifdef BOOST_WINDOWS_API
    259       { return detail::status_api( ph.external_file_string(), ec ); }
    260 #   else
    261258      { return detail::symlink_status_api( ph.external_file_string(), ec ); }
    262 #   endif
    263259
    264260    BOOST_FS_FUNC(file_status)
    265261    symlink_status( const Path & ph )
     
    318314    }
    319315
    320316    BOOST_FS_FUNC(bool) is_symlink(
    321 #   ifdef BOOST_WINDOWS_API
    322       const Path & )
    323     {
    324       return false;
    325 #   else
    326317      const Path & ph)
    327318    {
    328319      system::error_code ec;
     
    331322        boost::throw_exception( basic_filesystem_error<Path>(
    332323          "boost::filesystem::is_symlink", ph, ec ) );
    333324      return is_symlink( result );
    334 #   endif
    335325    }
    336326
    337327    // VC++ 7.0 and earlier has a serious namespace bug that causes a clash
  • libs/filesystem/test/operations_test.cpp

     
    670670  BOOST_CHECK( fs::create_symlink( "doesnotexist", "", ec ) );
    671671  BOOST_CHECK( ec );
    672672
     673  // directory symbolic link tests
     674  from_ph = dir / "d4";
     675  BOOST_CHECK( !fs::exists( from_ph ) );
     676  fs::path to_ph( dir / "d5" );
     677  BOOST_CHECK( fs::create_directory( to_ph ) );
     678  BOOST_CHECK( fs::exists( to_ph ) );
     679  bool create_dir_symlink_ok(true);
     680  try { fs::create_symlink( to_ph, from_ph ); }
     681  catch ( const fs::filesystem_error & ex )
     682  {
     683    create_dir_symlink_ok = false;
     684    std::cout
     685      << "create_symlink() attempt failed\n"
     686      << "filesystem_error.what() reports: " << ex.what() << '\n'
     687      << "create_symlink() for directories may not be supported on this file system\n";
     688  }
     689
     690  if ( create_dir_symlink_ok )
     691  {
     692    std::cout << "create_symlink() succeeded for the directory\n";
     693    BOOST_CHECK( fs::exists( from_ph ) );
     694    BOOST_CHECK( fs::is_symlink( from_ph ) );
     695    BOOST_CHECK( fs::exists( to_ph ) );
     696    BOOST_CHECK( fs::equivalent( from_ph, to_ph ) );
     697    stat = fs::symlink_status( from_ph );
     698    BOOST_CHECK( fs::exists( stat ) );
     699    BOOST_CHECK( !fs::is_directory( stat ) );
     700    BOOST_CHECK( !fs::is_regular( stat ) );
     701    BOOST_CHECK( !fs::is_other( stat ) );
     702    BOOST_CHECK( fs::is_symlink( stat ) );
     703
     704    // remove_all() test on symbolic link to a directory
     705    fs::path child_ph( to_ph / "child");
     706    create_file( child_ph, "foobar1" );
     707    BOOST_CHECK( fs::exists( child_ph ) );
     708    BOOST_CHECK( fs::exists( from_ph / "child" ) );
     709    BOOST_CHECK( fs::remove_all( from_ph ) );
     710    BOOST_CHECK( !fs::exists( from_ph ) );
     711    BOOST_CHECK( fs::exists( child_ph ) );
     712  }
     713
    673714  // there was an inital bug in directory_iterator that caused premature
    674715  // close of an OS handle. This block will detect regression.
    675716  {
  • libs/filesystem/src/operations.cpp

     
    5454
    5555# if defined(BOOST_WINDOWS_API)
    5656#   include <windows.h>
     57#   if _WIN32_WINNT >= 0x0500
     58#     include <winioctl.h>
     59#   endif
    5760#   if defined(__BORLANDC__) || defined(__MWERKS__)
    5861#     if defined(__BORLANDC__)
    5962        using std::time_t;
     
    117120  }
    118121
    119122#ifdef BOOST_WINDOWS_API
     123
     124# if _WIN32_WINNT >= 0x0500
     125  const DWORD mount_point_tag = 0xA0000003;
     126
     127  struct reparse_data_header
     128  {
     129    DWORD tag;
     130    WORD length;
     131    WORD reserved;
     132  };
     133
     134  struct mount_point_header
     135  {
     136    WORD sub_name_offset;
     137    WORD sub_name_length;
     138    WORD print_name_offset;
     139    WORD print_name_length;
     140  };
     141
     142  error_code set_mount_point( HANDLE handle,
     143    const wchar_t* sub_name, std::size_t sub_name_length,
     144    const wchar_t* print_name, std::size_t print_name_length )
     145  {
     146    std::size_t full_size =
     147        sizeof(reparse_data_header) + sizeof(mount_point_header) +
     148        (sub_name_length+1 + print_name_length+1) * sizeof(wchar_t);
     149
     150    boost::scoped_array<char> buf( new char[full_size] );
     151    char * buf_ptr = buf.get();
     152
     153    reparse_data_header top_head;
     154    top_head.tag = mount_point_tag;
     155    top_head.length = full_size - sizeof(top_head);
     156    top_head.reserved = 0;
     157    std::memcpy( buf_ptr, &top_head, sizeof(top_head) );
     158    buf_ptr += sizeof(top_head);
     159
     160    mount_point_header mt_head;
     161    mt_head.sub_name_offset = 0;
     162    mt_head.sub_name_length = sub_name_length * sizeof(wchar_t);
     163    mt_head.print_name_offset = mt_head.sub_name_length + sizeof(wchar_t);
     164    mt_head.print_name_length = print_name_length * sizeof(wchar_t);
     165    std::memcpy( buf_ptr, &mt_head, sizeof(mt_head) );
     166    buf_ptr += sizeof(mt_head);
     167
     168    std::memcpy( buf_ptr, sub_name, sub_name_length * sizeof(wchar_t) );
     169    buf_ptr += sub_name_length * sizeof(wchar_t);
     170    std::memset( buf_ptr, 0, sizeof(wchar_t) );
     171    buf_ptr += sizeof(wchar_t);
     172
     173    std::memcpy( buf_ptr, print_name, print_name_length * sizeof(wchar_t) );
     174    buf_ptr += print_name_length * sizeof(wchar_t);
     175    std::memset( buf_ptr, 0, sizeof(wchar_t) );
     176
     177    DWORD dummy = 0;
     178    if ( ::DeviceIoControl( handle, FSCTL_SET_REPARSE_POINT,
     179      buf.get(), full_size, 0, 0, &dummy, 0 ) == 0 )
     180    {
     181        return error_code( ::GetLastError(), system_category );
     182    }
     183
     184    return error_code();
     185  }
     186# endif
     187
    120188 
    121189// For Windows, the xxxA form of various function names is used to avoid
    122190// inadvertently getting wide forms of the functions. (The undecorated
     
    182250  inline bool create_hard_link( const std::wstring & to_ph,
    183251    const std::wstring & from_ph )
    184252    {  return ::CreateHardLinkW( from_ph.c_str(), to_ph.c_str(), 0 ) != 0; }
     253
     254  error_code create_symbolic_link( const std::wstring & from_ph,
     255    const std::wstring & to_ph, DWORD flags )
     256  {
     257      typedef BOOL (APIENTRY * func_type)( LPCWSTR, LPCWSTR, DWORD );
     258
     259      const wchar_t* from_s = from_ph.c_str();
     260      const wchar_t* to_s = to_ph.c_str();
     261
     262      error_code ec( ERROR_NOT_SUPPORTED, system_category );
     263      HMODULE dll( ::LoadLibraryA("kernel32.dll") );
     264      func_type func = reinterpret_cast<func_type>(
     265        ::GetProcAddress( dll, "CreateSymbolicLinkW" ) );
     266      if ( func )
     267      {
     268        BOOL res = (*func)(from_s, to_s, flags);
     269        if ( res ) { ec = error_code(); }
     270        else { ec = error_code( ::GetLastError(), system_category ); }
     271      }
     272      ::FreeLibrary( dll );
     273      return ec;
     274  }
     275
     276  error_code set_mount_point( HANDLE handle, const std::wstring & ph )
     277  {
     278    std::wstring sub_name(L"\\\?\?\\");
     279    if ((ph[0] == L'\\') && (ph[1] == L'\\'))
     280        sub_name += L"UNC";
     281    sub_name += ph;
     282
     283    return set_mount_point(
     284      handle, sub_name.c_str(), sub_name.size(), ph.c_str(), ph.size() );
     285  }
    185286#endif
    186287 
    187288# endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY
    188289
     290  inline fs::file_status make_status_error( error_code & ec )
     291  {
     292    ec = error_code( ::GetLastError(), system_category );
     293    if ((ec.value() == ERROR_FILE_NOT_FOUND)
     294      || (ec.value() == ERROR_PATH_NOT_FOUND)
     295      || (ec.value() == ERROR_INVALID_NAME) // "tools/jam/src/:sys:stat.h", "//foo"
     296      || (ec.value() == ERROR_INVALID_PARAMETER) // ":sys:stat.h"
     297      || (ec.value() == ERROR_BAD_PATHNAME) // "//nosuch" on Win64
     298      || (ec.value() == ERROR_BAD_NETPATH)) // "//nosuch" on Win32
     299    {
     300      ec = error_code(); // these are not considered errors;
     301                         // the status is considered not found
     302      return fs::file_status( fs::file_not_found );
     303    }
     304    else if ((ec.value() == ERROR_SHARING_VIOLATION))
     305    {
     306      ec = error_code(); // these are not considered errors;
     307                         // the file exists but the type is not known
     308      return fs::file_status( fs::type_unknown );
     309    }
     310    return fs::file_status( fs::status_unknown );
     311  }
     312
    189313  template< class String >
    190   fs::file_status status_template( const String & ph, error_code & ec )
     314  fs::file_status symlink_status_template( const String & ph, error_code & ec )
    191315  {
    192316    DWORD attr( get_file_attributes( ph.c_str() ) );
    193317    if ( attr == 0xFFFFFFFF )
     318      { return make_status_error( ec ); }
     319    ec = error_code();;
     320    if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
     321      return fs::file_status( fs::symlink_file );
     322    return (attr & FILE_ATTRIBUTE_DIRECTORY)
     323      ? fs::file_status( fs::directory_file )
     324      : fs::file_status( fs::regular_file );
     325  }
     326
     327  template< class String >
     328  fs::file_status status_template( const String & ph, error_code & ec )
     329  {
     330    fs::file_status sf( symlink_status_template( ph, ec ) );
     331    if ( !fs::is_symlink( sf ) )
     332      return sf;
     333
     334    handle_wrapper hw( create_file( ph.c_str(), 0,
     335      FILE_SHARE_READ|FILE_SHARE_WRITE, 0,
     336      OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ) );
     337    if ( hw.handle == INVALID_HANDLE_VALUE )
     338      { return make_status_error( ec ); }
     339    BY_HANDLE_FILE_INFORMATION info;
     340    if ( !::GetFileInformationByHandle( hw.handle, &info ) )
    194341    {
    195342      ec = error_code( ::GetLastError(), system_category );
    196       if ((ec.value() == ERROR_FILE_NOT_FOUND)
    197         || (ec.value() == ERROR_PATH_NOT_FOUND)
    198         || (ec.value() == ERROR_INVALID_NAME) // "tools/jam/src/:sys:stat.h", "//foo"
    199         || (ec.value() == ERROR_INVALID_PARAMETER) // ":sys:stat.h"
    200         || (ec.value() == ERROR_BAD_PATHNAME) // "//nosuch" on Win64
    201         || (ec.value() == ERROR_BAD_NETPATH)) // "//nosuch" on Win32
    202       {
    203         ec = error_code(); // these are not considered errors;
    204                            // the status is considered not found
    205         return fs::file_status( fs::file_not_found );
    206       }
    207       else if ((ec.value() == ERROR_SHARING_VIOLATION))
    208       {
    209         ec = error_code(); // these are not considered errors;
    210                            // the file exists but the type is not known
    211         return fs::file_status( fs::type_unknown );
    212       }
    213343      return fs::file_status( fs::status_unknown );
    214344    }
    215     ec = error_code();;
    216     return (attr & FILE_ATTRIBUTE_DIRECTORY)
     345    ec = error_code();
     346    return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    217347      ? fs::file_status( fs::directory_file )
    218348      : fs::file_status( fs::regular_file );
    219349  }
     
    223353  { return ::GetFileAttributesExA( ph, ::GetFileExInfoStandard, &fad ); }
    224354
    225355  template< class String >
     356  bool symbolic_link_exists_template( const String & ph )
     357  {
     358    DWORD attr( get_file_attributes( ph.c_str() ) );
     359    return ( attr != 0xFFFFFFFF ) && (attr & FILE_ATTRIBUTE_REPARSE_POINT);
     360  }
     361
     362  template< class String >
    226363  boost::filesystem::detail::query_pair
    227364  is_empty_template( const String & ph )
    228365  {
     
    517654  error_code
    518655  remove_template( const String & ph )
    519656  {
    520     error_code ec;
    521     fs::file_status sf( fs::detail::status_api( ph, ec ) );
    522     if ( ec ) return ec;
    523     if ( fs::is_directory( sf ) )
     657    DWORD attr( get_file_attributes( ph.c_str() ) );
     658    if ( attr == 0xFFFFFFFF )
    524659    {
     660      error_code ec;
     661      make_status_error( ec );
     662      return ec;
     663    }
     664    if (attr & FILE_ATTRIBUTE_DIRECTORY)
     665    {
    525666      if ( !remove_directory( ph ) )
    526667        return error_code(::GetLastError(), system_category);
    527668    }
     
    544685    error = error_code( ::GetLastError(), system_category );
    545686    // an error here may simply mean the postcondition is already met
    546687    if ( error.value() == ERROR_ALREADY_EXISTS
    547       && fs::is_directory( fs::detail::status_api( dir_ph, dummy ) ) )
     688      && fs::is_directory( fs::detail::symlink_status_api( dir_ph, dummy ) ) )
    548689      return std::make_pair( error_code(), false );
    549690    return std::make_pair( error, false );
    550691  }
     
    553694  inline bool create_hard_link( const std::string & to_ph,
    554695    const std::string & from_ph )
    555696    {  return ::CreateHardLinkA( from_ph.c_str(), to_ph.c_str(), 0 ) != 0; }
     697
     698  error_code create_symbolic_link( const std::string & from_ph,
     699    const std::string & to_ph, DWORD flags )
     700  {
     701      typedef BOOL (APIENTRY * func_type)( LPCSTR, LPCSTR, DWORD );
     702
     703      const char* from_s = from_ph.c_str();
     704      const char* to_s = to_ph.c_str();
     705
     706      error_code ec( ERROR_NOT_SUPPORTED, system_category );
     707      HMODULE dll( ::LoadLibraryA("kernel32.dll") );
     708      func_type func = reinterpret_cast<func_type>(
     709        ::GetProcAddress( dll, "CreateSymbolicLinkA" ) );
     710      if ( func )
     711      {
     712        BOOL res = (*func)(from_s, to_s, flags);
     713        if ( res ) { ec = error_code(); }
     714        else { ec = error_code( ::GetLastError(), system_category ); }
     715      }
     716      ::FreeLibrary( dll );
     717      return ec;
     718  }
     719
     720  error_code set_mount_point( HANDLE handle, const std::string & ph )
     721  {
     722    int w_size =
     723      ::MultiByteToWideChar( CP_ACP, 0, ph.c_str(), ph.size(), 0, 0 );
     724    if (w_size == 0)
     725      return error_code( ::GetLastError(), system_category );
     726
     727    bool is_unc = (ph[0] == '\\') && (ph[1] == '\\');
     728
     729    std::size_t prefix_length = is_unc ? 7 : 4;
     730    std::size_t sub_name_length = prefix_length + w_size;
     731
     732    boost::scoped_array<wchar_t> sub_name( new wchar_t[sub_name_length] );
     733
     734    wchar_t * ptr = sub_name.get();
     735    std::memcpy(ptr, L"\\\?\?\\", 4*sizeof(wchar_t));
     736    ptr += 4;
     737    if (is_unc)
     738    {
     739      std::memcpy(ptr, L"UNC", 3*sizeof(wchar_t));
     740      ptr += 3;
     741    }
     742
     743    w_size =
     744      ::MultiByteToWideChar( CP_ACP, 0, ph.c_str(), ph.size(), ptr, w_size );
     745    if (w_size == 0)
     746      return error_code( ::GetLastError(), system_category );
     747
     748    return set_mount_point(
     749      handle, &sub_name[0], sub_name_length, ptr, w_size );
     750  }
    556751#endif
    557752 
    558753#if _WIN32_WINNT >= 0x500
     
    564759    return error_code( create_hard_link( to_ph.c_str(), from_ph.c_str() )
    565760      ? 0 : ::GetLastError(), system_category );
    566761  }
     762
     763  template<class String>
     764  class remove_directory_monitor
     765  {
     766  public:
     767    explicit remove_directory_monitor( const String & ph )
     768      : m_path(ph), m_need_remove(true)
     769    {
     770    }
     771
     772    ~remove_directory_monitor()
     773    {
     774      if (m_need_remove)
     775        remove_directory( m_path );
     776    }
     777
     778    void release()
     779    {
     780        m_need_remove = false;
     781    }
     782
     783  private:
     784    const String & m_path;
     785    bool m_need_remove;
     786  };
     787
     788  template<class String>
     789  error_code
     790  create_directory_symlink_template(
     791    const String & to_ph, const String & from_ph )
     792  {
     793    if ( !create_directory( from_ph ) )
     794      return error_code( ::GetLastError(), system_category );
     795
     796    remove_directory_monitor<String> guard( from_ph );
     797
     798    handle_wrapper hw(
     799      create_file(
     800        from_ph.c_str(), GENERIC_WRITE,
     801        FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING,
     802        FILE_FLAG_BACKUP_SEMANTICS, 0 ) );
     803
     804    error_code ec = set_mount_point( hw.handle, to_ph );
     805    if ( !ec )
     806      guard.release();
     807    return ec;
     808  }
     809
     810  template<class String>
     811  error_code
     812  create_symlink_template(
     813    const String & to_ph, const String & from_ph )
     814  {
     815    error_code ec;
     816    fs::file_status sf( fs::detail::symlink_status_api( to_ph, ec ) );
     817    if ( ec ) return ec;
     818    if ( fs::is_directory( sf ) )
     819    {
     820      ec = create_symbolic_link( from_ph, to_ph, 0 );
     821      if ( ec && ( ec == error_code( ERROR_NOT_SUPPORTED, system_category ) ) )
     822        { return create_directory_symlink_template( to_ph, from_ph ); }
     823      else
     824        { return ec; }
     825    }
     826    else
     827      return create_symbolic_link( from_ph, to_ph, 0 );
     828  }
    567829#endif
    568830
    569831#endif
     
    603865        status_api( const std::string & ph, error_code & ec )
    604866        { return status_template( ph, ec ); }
    605867
     868      BOOST_FILESYSTEM_DECL fs::file_status
     869        symlink_status_api( const std::string & ph, error_code & ec )
     870        { return symlink_status_template( ph, ec ); }
     871
    606872#     ifndef BOOST_FILESYSTEM_NARROW_ONLY
    607873
    608874      BOOST_FILESYSTEM_DECL fs::file_status
    609875      status_api( const std::wstring & ph, error_code & ec )
    610876        { return status_template( ph, ec ); }
    611877
    612       BOOST_FILESYSTEM_DECL bool symbolic_link_exists_api( const std::wstring & )
    613         { return false; }
     878      BOOST_FILESYSTEM_DECL fs::file_status
     879      symlink_status_api( const std::wstring & ph, error_code & ec )
     880        { return symlink_status_template( ph, ec ); }
    614881
     882      BOOST_FILESYSTEM_DECL bool
     883      symbolic_link_exists_api( const std::wstring & ph )
     884        { return symbolic_link_exists_template( ph ); }
     885
    615886      BOOST_FILESYSTEM_DECL
    616887      fs::detail::query_pair is_empty_api( const std::wstring & ph )
    617888        { return is_empty_template( ph ); }
     
    662933        { return create_hard_link_template( to_ph, from_ph ); }
    663934#endif
    664935     
     936#if _WIN32_WINNT >= 0x500
    665937      BOOST_FILESYSTEM_DECL error_code
     938      create_symlink_api( const std::wstring & to_ph,
     939        const std::wstring & from_ph )
     940        { return create_symlink_template( to_ph, from_ph ); }
     941#else
     942      BOOST_FILESYSTEM_DECL error_code
    666943      create_symlink_api( const std::wstring & /*to_ph*/,
    667944        const std::wstring & /*from_ph*/ )
    668945        { return error_code( ERROR_NOT_SUPPORTED, system_category ); }
     946#endif
    669947
    670948      BOOST_FILESYSTEM_DECL error_code
    671949      remove_api( const std::wstring & ph ) { return remove_template( ph ); }
     
    7871065#     endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY
    7881066
    7891067      // suggested by Walter Landry
    790       BOOST_FILESYSTEM_DECL bool symbolic_link_exists_api( const std::string & )
    791         { return false; }
     1068      BOOST_FILESYSTEM_DECL bool
     1069      symbolic_link_exists_api( const std::string & ph )
     1070        { return symbolic_link_exists_template( ph ); }
    7921071
    7931072      BOOST_FILESYSTEM_DECL
    7941073      fs::detail::query_pair is_empty_api( const std::string & ph )
     
    8421121      }
    8431122#endif
    8441123
     1124#if _WIN32_WINNT >= 0x500
    8451125      BOOST_FILESYSTEM_DECL error_code
     1126      create_symlink_api( const std::string & to_ph,
     1127        const std::string & from_ph )
     1128      {
     1129        return create_symlink_template( to_ph, from_ph );
     1130      }
     1131#else
     1132      BOOST_FILESYSTEM_DECL error_code
    8461133      create_symlink_api( const std::string & /*to_ph*/,
    8471134        const std::string & /*from_ph*/ )
    8481135        { return error_code( ERROR_NOT_SUPPORTED, system_category ); }
     1136#endif
    8491137
    8501138      BOOST_FILESYSTEM_DECL error_code
    8511139      remove_api( const std::string & ph ) { return remove_template( ph ); }