Check disk permission

This commit is contained in:
2025-09-20 04:13:24 +09:00
parent 3582cca274
commit c98913b0fa
5 changed files with 399 additions and 2 deletions

View File

@@ -108,7 +108,7 @@
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\lib\libwebm;$(ProjectDir)..\..\..\lib\dav1d;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>webm-debug.lib;dav1d-debug.lib</AdditionalDependencies>
<AdditionalDependencies>webm-debug.lib;dav1d-debug.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
@@ -117,7 +117,7 @@
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\lib\libwebm;$(ProjectDir)..\..\..\lib\dav1d;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>webm.lib;dav1d.lib</AdditionalDependencies>
<AdditionalDependencies>webm.lib;dav1d.lib;shell32.lib;user32.lib;advapi32.lib;ole32.lib</AdditionalDependencies>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
@@ -140,6 +140,7 @@
</ClInclude>
<ClInclude Include="src\Common\VideoTypes.h" />
<ClInclude Include="src\Common\FramePool.h" />
<ClInclude Include="src\Common\PermissionUtils.h" />
<ClInclude Include="src\Decoder\IVideoDecoder.h" />
<ClInclude Include="src\Decoder\VideoDecoderFactory.h" />
<ClInclude Include="src\Decoder\AV1Decoder.h" />
@@ -165,6 +166,7 @@
</ClCompile>
<ClCompile Include="src\Decoder\VideoDecoderFactory.cpp" />
<ClCompile Include="src\Common\FramePool.cpp" />
<ClCompile Include="src\Common\PermissionUtils.cpp" />
<ClCompile Include="src\Decoder\AV1Decoder.cpp" />
<ClCompile Include="src\FileIO\WebMFileReader.cpp" />
<ClCompile Include="src\Pipeline\FrameBuffer.cpp" />

View File

@@ -0,0 +1,253 @@
#include "pch.h"
#include "PermissionUtils.h"
#include <windows.h>
#include <shlobj.h>
#include <shellapi.h>
#include <sddl.h>
#include <aclapi.h>
#include <fstream>
#include <random>
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<uintptr_t>(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<std::filesystem::path> 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

View File

@@ -0,0 +1,44 @@
#pragma once
#include <windows.h>
#include <filesystem>
#include <string>
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

View File

@@ -1,5 +1,6 @@
#include "pch.h"
#include "FileOutput.h"
#include "../Common/PermissionUtils.h"
#include <iostream>
#include <fstream>
#include <algorithm>
@@ -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

View File

@@ -1,5 +1,6 @@
#pragma once
#include "../Common/VideoTypes.h"
#include "../Common/PermissionUtils.h"
#include <string>
#include <filesystem>
#include <functional>
@@ -64,6 +65,10 @@ public:
bool CreateOutputDirectory();
void ClearOutputDirectory();
// 권한 체크 및 처리
bool CheckAndSetupPermissions();
bool HandlePermissionDenied();
// 통계 정보
struct OutputStats {
uint64_t frames_saved = 0;