From c98913b0fac61037d1a36728256ef9796f3db8c8 Mon Sep 17 00:00:00 2001 From: ened Date: Sat, 20 Sep 2025 04:13:24 +0900 Subject: [PATCH] Check disk permission --- vav2/Vav2Player/Vav2Player/Vav2Player.vcxproj | 6 +- .../Vav2Player/src/Common/PermissionUtils.cpp | 253 ++++++++++++++++++ .../Vav2Player/src/Common/PermissionUtils.h | 44 +++ .../Vav2Player/src/Output/FileOutput.cpp | 93 +++++++ .../Vav2Player/src/Output/FileOutput.h | 5 + 5 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.cpp create mode 100644 vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.h diff --git a/vav2/Vav2Player/Vav2Player/Vav2Player.vcxproj b/vav2/Vav2Player/Vav2Player/Vav2Player.vcxproj index c74f4dc..272ee60 100644 --- a/vav2/Vav2Player/Vav2Player/Vav2Player.vcxproj +++ b/vav2/Vav2Player/Vav2Player/Vav2Player.vcxproj @@ -108,7 +108,7 @@ $(ProjectDir)..\..\..\lib\libwebm;$(ProjectDir)..\..\..\lib\dav1d;%(AdditionalLibraryDirectories) - webm-debug.lib;dav1d-debug.lib + webm-debug.lib;dav1d-debug.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib @@ -117,7 +117,7 @@ $(ProjectDir)..\..\..\lib\libwebm;$(ProjectDir)..\..\..\lib\dav1d;%(AdditionalLibraryDirectories) - webm.lib;dav1d.lib + webm.lib;dav1d.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib true true @@ -140,6 +140,7 @@ + @@ -165,6 +166,7 @@ + diff --git a/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.cpp b/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.cpp new file mode 100644 index 0000000..ce3b233 --- /dev/null +++ b/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.cpp @@ -0,0 +1,253 @@ +#include "pch.h" +#include "PermissionUtils.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Vav2Player { + +PermissionUtils::PermissionResult PermissionUtils::CheckDirectoryCreatePermission(const std::filesystem::path& directory_path) { + try { + // 1. 디렉토리가 이미 존재하면 쓰기 권한 체크 + if (std::filesystem::exists(directory_path)) { + if (TestDirectoryWrite(directory_path)) { + OutputDebugStringA(("[PermissionUtils] Directory exists and writable: " + directory_path.string() + "\n").c_str()); + return PermissionResult::Granted; + } else { + OutputDebugStringA(("[PermissionUtils] Directory exists but not writable: " + directory_path.string() + "\n").c_str()); + return PermissionResult::Denied; + } + } + + // 2. 부모 디렉토리에서 생성 권한 체크 + std::filesystem::path parent_path = directory_path.parent_path(); + if (!std::filesystem::exists(parent_path)) { + // 부모 디렉토리가 없으면 재귀적으로 체크 + auto parent_result = CheckDirectoryCreatePermission(parent_path); + if (parent_result != PermissionResult::Granted) { + return parent_result; + } + } + + // 3. 실제로 임시 디렉토리 생성 테스트 + std::filesystem::path test_dir = parent_path / ("test_permission_" + std::to_string(GetTickCount64())); + + std::error_code ec; + bool created = std::filesystem::create_directory(test_dir, ec); + + if (created && !ec) { + // 생성 성공, 정리 + std::filesystem::remove(test_dir, ec); + OutputDebugStringA(("[PermissionUtils] Directory creation test successful: " + directory_path.string() + "\n").c_str()); + return PermissionResult::Granted; + } else { + OutputDebugStringA(("[PermissionUtils] Directory creation test failed: " + directory_path.string() + ", Error: " + ec.message() + "\n").c_str()); + return PermissionResult::Denied; + } + + } catch (const std::exception& e) { + OutputDebugStringA(("[PermissionUtils] Exception in CheckDirectoryCreatePermission: " + std::string(e.what()) + "\n").c_str()); + return PermissionResult::Error; + } +} + +PermissionUtils::PermissionResult PermissionUtils::CheckFileWritePermission(const std::filesystem::path& file_path) { + try { + // 1. 디렉토리 존재 확인 + std::filesystem::path parent_dir = file_path.parent_path(); + if (!std::filesystem::exists(parent_dir)) { + auto dir_result = CheckDirectoryCreatePermission(parent_dir); + if (dir_result != PermissionResult::Granted) { + return dir_result; + } + } + + // 2. 임시 파일 생성 테스트 + std::filesystem::path test_file = parent_dir / ("test_write_" + std::to_string(GetTickCount64()) + ".tmp"); + + std::ofstream test_stream(test_file, std::ios::binary); + if (test_stream.is_open()) { + test_stream << "permission test"; + test_stream.close(); + + // 정리 + std::error_code ec; + std::filesystem::remove(test_file, ec); + + OutputDebugStringA(("[PermissionUtils] File write test successful: " + file_path.string() + "\n").c_str()); + return PermissionResult::Granted; + } else { + OutputDebugStringA(("[PermissionUtils] File write test failed: " + file_path.string() + "\n").c_str()); + return PermissionResult::Denied; + } + + } catch (const std::exception& e) { + OutputDebugStringA(("[PermissionUtils] Exception in CheckFileWritePermission: " + std::string(e.what()) + "\n").c_str()); + return PermissionResult::Error; + } +} + +bool PermissionUtils::IsRunningAsAdmin() { + BOOL is_admin = FALSE; + PSID admin_group = nullptr; + SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY; + + if (AllocateAndInitializeSid(&nt_authority, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &admin_group)) { + + if (!::CheckTokenMembership(nullptr, admin_group, &is_admin)) { + is_admin = FALSE; + } + FreeSid(admin_group); + } + + OutputDebugStringA(is_admin ? "[PermissionUtils] Running as administrator\n" : "[PermissionUtils] Not running as administrator\n"); + return is_admin == TRUE; +} + +bool PermissionUtils::RequestAdminRestart() { + wchar_t exe_path[MAX_PATH]; + DWORD path_length = GetModuleFileNameW(nullptr, exe_path, MAX_PATH); + + if (path_length == 0 || path_length == MAX_PATH) { + OutputDebugStringA("[PermissionUtils] Failed to get executable path for restart\n"); + return false; + } + + // ShellExecute로 관리자 권한 요청 + HINSTANCE result = ShellExecuteW(nullptr, L"runas", exe_path, nullptr, nullptr, SW_SHOWNORMAL); + + bool success = reinterpret_cast(result) > 32; + if (success) { + OutputDebugStringA("[PermissionUtils] Requested admin restart\n"); + // 현재 프로세스 종료 + PostQuitMessage(0); + } else { + OutputDebugStringA("[PermissionUtils] Failed to request admin restart\n"); + } + + return success; +} + +bool PermissionUtils::ShowPermissionDialog(const std::wstring& message, const std::wstring& title) { + int result = MessageBoxW(nullptr, message.c_str(), title.c_str(), + MB_YESNO | MB_ICONWARNING | MB_TOPMOST); + + return result == IDYES; +} + +std::filesystem::path PermissionUtils::GetSafeOutputDirectory() { + // 우선순위 순서로 안전한 디렉토리 시도 + std::vector safe_directories; + + // 1. 사용자 문서 폴더 + wchar_t* documents_path = nullptr; + if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &documents_path))) { + safe_directories.push_back(std::filesystem::path(documents_path) / "Vav2Player"); + CoTaskMemFree(documents_path); + } + + // 2. 사용자 임시 폴더 + wchar_t temp_path[MAX_PATH]; + if (GetTempPathW(MAX_PATH, temp_path)) { + safe_directories.push_back(std::filesystem::path(temp_path) / "Vav2Player"); + } + + // 3. 현재 사용자 프로필 폴더 + wchar_t* profile_path = nullptr; + if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &profile_path))) { + safe_directories.push_back(std::filesystem::path(profile_path) / "Vav2Player"); + CoTaskMemFree(profile_path); + } + + // 4. 실행파일과 같은 디렉토리 + wchar_t exe_path[MAX_PATH]; + if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH)) { + std::filesystem::path exe_dir = std::filesystem::path(exe_path).parent_path(); + safe_directories.push_back(exe_dir / "output"); + } + + // 각 디렉토리 권한 체크 + for (const auto& dir : safe_directories) { + if (CheckDirectoryCreatePermission(dir) == PermissionResult::Granted) { + OutputDebugStringA(("[PermissionUtils] Found safe directory: " + dir.string() + "\n").c_str()); + return dir; + } + } + + // 모든 시도 실패 시 기본값 + OutputDebugStringA("[PermissionUtils] No safe directory found, using fallback\n"); + return std::filesystem::path("C:\\temp\\Vav2Player"); +} + +PermissionUtils::PermissionResult PermissionUtils::CheckAndHandlePermissions(const std::filesystem::path& output_directory) { + OutputDebugStringA(("[PermissionUtils] Checking permissions for: " + output_directory.string() + "\n").c_str()); + + // 1. 기본 권한 체크 + auto result = CheckDirectoryCreatePermission(output_directory); + + if (result == PermissionResult::Granted) { + return result; + } + + // 2. 권한 없는 경우 처리 + std::wstring message; + + if (result == PermissionResult::Denied) { + std::wstring dir_path = std::wstring(output_directory.string().begin(), output_directory.string().end()); + message = L"출력 디렉토리에 쓰기 권한이 없습니다:\n" + dir_path + + L"\n\n관리자 권한으로 다시 시작하시겠습니까?\n(아니오를 선택하면 안전한 위치를 사용합니다)"; + } else { + std::wstring dir_path = std::wstring(output_directory.string().begin(), output_directory.string().end()); + message = L"디렉토리 권한 확인 중 오류가 발생했습니다:\n" + dir_path + + L"\n\n관리자 권한으로 다시 시작하시겠습니까?"; + } + + // 3. 사용자에게 선택 제공 + if (ShowPermissionDialog(message, L"디렉토리 권한 필요")) { + // 관리자 권한으로 재시작 요청 + if (RequestAdminRestart()) { + // 재시작 요청 성공 (앱이 종료될 예정) + return PermissionResult::Granted; + } else { + // 재시작 실패 - 안전한 디렉토리 사용 + return PermissionResult::Denied; + } + } else { + // 사용자가 관리자 권한 거부 - 안전한 디렉토리 사용 + return PermissionResult::Denied; + } +} + +bool PermissionUtils::TestDirectoryWrite(const std::filesystem::path& directory_path) { + try { + std::filesystem::path test_file = directory_path / ("write_test_" + std::to_string(GetTickCount64()) + ".tmp"); + + std::ofstream test_stream(test_file, std::ios::binary); + if (!test_stream.is_open()) { + return false; + } + + test_stream << "write permission test"; + test_stream.close(); + + // 정리 + std::error_code ec; + std::filesystem::remove(test_file, ec); + + return true; + + } catch (const std::exception&) { + return false; + } +} + + +} // namespace Vav2Player \ No newline at end of file diff --git a/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.h b/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.h new file mode 100644 index 0000000..0f960b1 --- /dev/null +++ b/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include +#include + +namespace Vav2Player { + +class PermissionUtils { +public: + // 권한 체크 결과 + enum class PermissionResult { + Granted, // 권한 있음 + Denied, // 권한 없음 + Error // 체크 중 오류 발생 + }; + + // 디렉토리 생성 권한 체크 + static PermissionResult CheckDirectoryCreatePermission(const std::filesystem::path& directory_path); + + // 파일 쓰기 권한 체크 + static PermissionResult CheckFileWritePermission(const std::filesystem::path& file_path); + + // 관리자 권한으로 실행 중인지 확인 + static bool IsRunningAsAdmin(); + + // 관리자 권한으로 재시작 요청 + static bool RequestAdminRestart(); + + // 사용자에게 권한 관련 메시지 표시 + static bool ShowPermissionDialog(const std::wstring& message, const std::wstring& title = L"권한 필요"); + + // 안전한 출력 디렉토리 제안 + static std::filesystem::path GetSafeOutputDirectory(); + + // 권한 체크 및 처리 (통합 함수) + static PermissionResult CheckAndHandlePermissions(const std::filesystem::path& output_directory); + +private: + // 디렉토리에 대한 실제 쓰기 테스트 + static bool TestDirectoryWrite(const std::filesystem::path& directory_path); + +}; + +} // namespace Vav2Player \ No newline at end of file diff --git a/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.cpp b/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.cpp index 1da0246..0c142e7 100644 --- a/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.cpp +++ b/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "FileOutput.h" +#include "../Common/PermissionUtils.h" #include #include #include @@ -31,6 +32,11 @@ FileOutput::FileOutput(const OutputConfig& config) OutputDebugStringA("[FileOutput] YUV->RGB conversion will use lookup table optimization\n"); } + // 권한 체크 및 설정 + if (!CheckAndSetupPermissions()) { + OutputDebugStringA("[FileOutput] Warning: Failed to setup proper permissions for output directory\n"); + } + InitializeCache(); } @@ -764,4 +770,91 @@ void FileOutput::StoreRGB24_SIMD(uint8_t* dst, __m256i r_vec, __m256i g_vec, __m } } +bool FileOutput::CheckAndSetupPermissions() { + try { + std::filesystem::path exe_path = GetProcessPath(); + std::filesystem::path full_output_path = exe_path / m_config.output_directory; + + OutputDebugStringA(("[FileOutput] Checking permissions for output directory: " + full_output_path.string() + "\n").c_str()); + + // 권한 체크 및 처리 + auto permission_result = PermissionUtils::CheckAndHandlePermissions(full_output_path); + + switch (permission_result) { + case PermissionUtils::PermissionResult::Granted: + OutputDebugStringA("[FileOutput] Permissions granted for output directory\n"); + return true; + + case PermissionUtils::PermissionResult::Denied: + OutputDebugStringA("[FileOutput] Permissions denied, attempting to use safe directory\n"); + return HandlePermissionDenied(); + + case PermissionUtils::PermissionResult::Error: + OutputDebugStringA("[FileOutput] Error checking permissions, attempting to use safe directory\n"); + return HandlePermissionDenied(); + + default: + return false; + } + + } catch (const std::exception& e) { + OutputDebugStringA(("[FileOutput] Exception in CheckAndSetupPermissions: " + std::string(e.what()) + "\n").c_str()); + return HandlePermissionDenied(); + } +} + +bool FileOutput::HandlePermissionDenied() { + try { + // 안전한 출력 디렉토리 찾기 + std::filesystem::path safe_directory = PermissionUtils::GetSafeOutputDirectory(); + + OutputDebugStringA(("[FileOutput] Using safe output directory: " + safe_directory.string() + "\n").c_str()); + + // 설정 업데이트 + m_config.output_directory = safe_directory; + + // 캐시 재초기화 + InitializeCache(); + + // 권한 재체크 + auto permission_result = PermissionUtils::CheckDirectoryCreatePermission(safe_directory); + + if (permission_result == PermissionUtils::PermissionResult::Granted) { + OutputDebugStringA("[FileOutput] Safe directory permissions verified\n"); + + // 사용자에게 알림 + std::wstring safe_dir_wstr = std::wstring(safe_directory.string().begin(), safe_directory.string().end()); + std::wstring message = L"원래 출력 디렉토리에 권한이 없어 다음 위치를 사용합니다:\n" + safe_dir_wstr; + PermissionUtils::ShowPermissionDialog(message, L"출력 디렉토리 변경"); + + return true; + } else { + OutputDebugStringA("[FileOutput] Safe directory also lacks permissions\n"); + + // 마지막 수단: 임시 디렉토리 + wchar_t temp_path[MAX_PATH]; + if (GetTempPathW(MAX_PATH, temp_path)) { + std::filesystem::path temp_dir = std::filesystem::path(temp_path) / "Vav2Player_Emergency"; + m_config.output_directory = temp_dir; + InitializeCache(); + + OutputDebugStringA(("[FileOutput] Using emergency temp directory: " + temp_dir.string() + "\n").c_str()); + + std::wstring temp_dir_wstr = std::wstring(temp_dir.string().begin(), temp_dir.string().end()); + std::wstring message = L"안전한 출력 디렉토리를 찾을 수 없어 임시 폴더를 사용합니다:\n" + temp_dir_wstr; + PermissionUtils::ShowPermissionDialog(message, L"긴급 출력 디렉토리"); + + return true; + } else { + OutputDebugStringA("[FileOutput] Failed to get temp directory\n"); + return false; + } + } + + } catch (const std::exception& e) { + OutputDebugStringA(("[FileOutput] Exception in HandlePermissionDenied: " + std::string(e.what()) + "\n").c_str()); + return false; + } +} + } // namespace Vav2Player \ No newline at end of file diff --git a/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.h b/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.h index 2b28d1e..7d05d01 100644 --- a/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.h +++ b/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.h @@ -1,5 +1,6 @@ #pragma once #include "../Common/VideoTypes.h" +#include "../Common/PermissionUtils.h" #include #include #include @@ -64,6 +65,10 @@ public: bool CreateOutputDirectory(); void ClearOutputDirectory(); + // 권한 체크 및 처리 + bool CheckAndSetupPermissions(); + bool HandlePermissionDenied(); + // 통계 정보 struct OutputStats { uint64_t frames_saved = 0;