Directory.cpp 5.85 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

23
24
25
26
#if HAVE_CONFIG_H
# include "config.h"
#endif

27
#include "Directory.h"
28
#include "utils/Charsets.h"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
29
#include "utils/Filename.h"
30
#include "utils/Url.h"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
31
#include "medialibrary/filesystem/IFileSystemFactory.h"
32
#include "File.h"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
33
#include "logging/Logger.h"
34
35
36
37

#include <sys/types.h>
#include <sys/stat.h>
#include <windows.h>
38
#include <winapifamily.h>
39

40
41
42
namespace medialibrary
{

43
44
45
namespace fs
{

46
Directory::Directory( const std::string& mrl , fs::IFileSystemFactory& fsFactory )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
47
    : CommonDirectory( fsFactory )
48
{
49
    m_path = utils::file::toFolderPath( toAbsolute( utils::file::toLocalPath( mrl ) ) );
50
    assert( *m_path.crbegin() == '/' || *m_path.crbegin() == '\\' );
51
    m_mrl = utils::file::toMrl( m_path );
52
53
}

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
54
55
56
57
58
const std::string& Directory::mrl() const
{
    return m_mrl;
}

59
void Directory::read() const
60
{
61
#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
62
    WIN32_FIND_DATA f;
63
    auto pattern = m_path + '*';
64
65
    auto wpattern = charset::ToWide( pattern.c_str() );
    auto h = FindFirstFile( wpattern.get(), &f );
66
    if ( h == INVALID_HANDLE_VALUE )
67
    {
68
        LOG_ERROR( "Failed to browse ", m_path );
69
        throw std::system_error( GetLastError(), std::generic_category(), "Failed to browse through directory" );
70
    }
71
    do
72
    {
73
        auto file = charset::FromWide( f.cFileName );
74
        if ( file[0] == '.' && strcasecmp( file.get(), ".nomedia" ) )
75
            continue;
76
        auto fullpath = m_path + file.get();
77
        if ( ( f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 )
78
79
            m_dirs.emplace_back( m_fsFactory.createDirectory(
                                     m_mrl + utils::url::encode( file.get() ) ) );
80
        else
81
            m_files.emplace_back( std::make_shared<File>( fullpath ) );
82
    } while ( FindNextFile( h, &f ) != 0 );
83
    FindClose( h );
84
85
86
87
#else
    // We must remove the trailing /
    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
    // «Do not use a trailing backslash (\), which indicates the root directory of a drive»
88
    auto tmpPath = path.substr( 0, m_path.length() - 1 );
89
    auto wpath = charset::ToWide( tmpPath.c_str() );
90
91
92
93

    CREATEFILE2_EXTENDED_PARAMETERS params{};
    params.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
    auto handle = CreateFile2( wpath.get(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, &params );
94
95
    if ( handle == INVALID_HANDLE_VALUE )
    {
96
        LOG_ERROR( "Failed to open directory ", m_path );
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
        throw std::system_error( GetLastError(), std::generic_category(), "Failed to open directory" );
    }

    std::unique_ptr<typename std::remove_pointer<HANDLE>::type,
            decltype(&CloseHandle)> handlePtr( handle, &CloseHandle );

    // Allocating a 32 bytes buffer to contain the file name. If more is required, we'll allocate
    size_t buffSize = sizeof( FILE_FULL_DIR_INFO ) + 32;
    std::unique_ptr<FILE_FULL_DIR_INFO, void(*)(FILE_FULL_DIR_INFO*)> dirInfo(
                reinterpret_cast<FILE_FULL_DIR_INFO*>( malloc( buffSize ) ),
                [](FILE_FULL_DIR_INFO* ptr) { free( ptr ); } );
    if ( dirInfo == nullptr )
        throw std::bad_alloc();

    while ( true )
    {
        auto h = GetFileInformationByHandleEx( handle, FileFullDirectoryInfo, dirInfo.get(), buffSize );
        if ( h == 0 )
        {
            auto error = GetLastError();
            if ( error == ERROR_FILE_NOT_FOUND )
                break;
            else if ( error == ERROR_MORE_DATA )
            {
                buffSize *= 2;
                dirInfo.reset( reinterpret_cast<FILE_FULL_DIR_INFO*>( malloc( buffSize ) ) );
                if ( dirInfo == nullptr )
                    throw std::bad_alloc();
                continue;
            }
127
            LOG_ERROR( "Failed to browse ", m_path, ". GetLastError(): ", GetLastError() );
128
129
130
131
            throw std::system_error( GetLastError(), std::generic_category(), "Failed to browse through directory" );
        }

        auto file = charset::FromWide( dirInfo->FileName );
132
        if ( file[0] == '.' && strcasecmp( file.get(), ".nomedia" ) )
133
134
            continue;
        if ( ( dirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 )
135
            m_dirs.emplace_back( m_fsFactory.createDirectory( m_mrl + utils::url::encode( file.get() ) ) );
136
        else
137
            m_files.emplace_back( std::make_shared<File>( m_path + file.get()) );
138
139
    }
#endif
140
141
142
143
144
}

std::string Directory::toAbsolute( const std::string& path )
{
    TCHAR buff[MAX_PATH];
145
146
    auto wpath = charset::ToWide( path.c_str() );
    if ( GetFullPathName( wpath.get(), MAX_PATH, buff, nullptr ) == 0 )
147
    {
148
        LOG_ERROR( "Failed to convert ", path, " to absolute path" );
149
        throw std::system_error( GetLastError(), std::generic_category(), "Failed to convert to absolute path" );
150
    }
151
152
    auto upath = charset::FromWide( buff );
    return std::string( upath.get() );
153
154
155
}

}
156
157

}