FSA: Preserve save picker filesystem errors
MERGED2026contentfile_system_accessfix
2026. 9. 5.
zbnerd
showSaveFilePicker()에서 파일 생성이나 truncate가 실패했을 때 원래 파일 시스템 오류를 보존하도록 수정했습니다. 웹사이트가 쓰기 권한 부족이나 부모 디렉터리 부재를 사용자 취소와 구분할 수 있도록 개선했습니다.
문제 설명
저장 파일 선택기는 파일 핸들을 반환하기 전에 선택한 파일을 생성하거나 기존 파일의 길이를 0으로 줄입니다. 기존 구현은 이 작업의 결과를 bool로 전달해 실패 원인을 잃어버렸습니다.
- 쓰기 권한 부족이나 부모 디렉터리 부재도 일반적인
AbortError로 전달됐습니다. - 사용자가 대화상자를 취소한 경우에도
AbortError가 발생하므로, 웹사이트가 취소와 파일 시스템 실패를 구분하기 어려웠습니다. - POSIX 로컬 파일 처리 경로와 storage backend 경로 모두에서 오류 정보를 유지해야 했습니다.
해결 내용
- POSIX 로컬 파일 헬퍼
CreateAndTruncateLocalFile()의 반환형을bool에서base::File::Error로 변경했습니다.open(),fstat(),SetLength()실패 시 오류를 반환하고, 일반 파일이 아닌 경우에는FILE_ERROR_NOT_A_FILE을 반환하도록 했습니다. CreateAndTruncateFile()과DidCreateFileToTruncate()의 비동기 콜백도base::File::Error를 전달하도록 변경해 backend가 반환한 오류를 보존했습니다.DidCreateAndTruncateSaveFile()에서 일반적인 실패 상태 대신 기존file_system_access_error::FromFileError()매핑을 사용하도록 수정했습니다.
if (result != base::File::FILE_OK) {
std::move(callback).Run(file_system_access_error::FromFileError(result),
std::move(result_entries));
return;
}쓰기 접근이 거부되면 NoModificationAllowedError, 부모 디렉터리가 없으면 NotFoundError를 전달합니다. 사용자가 선택을 취소하면 기존처럼 AbortError를 전달합니다.
외부 경로의 NativeFileUtil은 SetLength() 실패를 여전히 FILE_ERROR_FAILED로 반환합니다. 이번 변경은 backend가 제공하는 오류를 보존하며, 해당 backend 내부의 오류 세분화까지 변경하지는 않았습니다.
테스트 방법
다음 회귀 테스트를 추가했습니다.
FileSystemAccessManagerImplSaveFileTest를 로컬·외부 경로로 매개변수화해ReadOnlyFile,MissingParentDirectory,TruncateFailure를 검증합니다. 실패 시 오류 코드와 빈 결과 목록을 확인하고, 기존 파일의 내용이 보존되는지도 확인합니다.- Linux·ChromeOS의
TruncateFailure는 Landlock으로 truncate만 제한합니다. 파일 열기와fstat()이 성공하는지 먼저 확인해SetLength()실패 경로를 실제로 거치도록 구성했습니다. 제한을 해제할 수 없는 Landlock의 특성을 고려해 전용 파일 스레드에만 적용하며, 필요한 기능을 지원하지 않는 환경에서는 건너뜁니다. FileSystemChooserBrowserTest에SaveFile_CancelDialog,SaveFile_ReadOnlyFile,SaveFile_MissingParentDirectory를 추가해 JavaScript에서 관찰하는 예외 이름을 검증합니다.- 읽기 전용 파일 테스트는 실제 쓰기 접근이 차단되는지 먼저 확인합니다. Windows에서는 권한을 복원한 뒤 기존 파일의 내용을 확인하도록 했습니다.
Gerrit에서 Alex Moshchuk과 Rakina Zata Amni의 리뷰를 받았고, Amos Lim의 Commit Queue 제출 후 2026-09-11에 Chromium main에 병합됐습니다. 최종 커밋 위치는 refs/heads/main@{#1696213}입니다.
배운 점
- 하위 계층의 오류를
bool로 축약하면 상위 API에서 필요한 실패 원인을 복원할 수 없습니다. 비동기 콜백을 포함한 전체 전달 경로에서 오류 타입을 유지해야 합니다. - 내부 오류 코드 확인과 JavaScript에서 관찰하는 예외 확인을 함께 구성하면, 구현 계층과 사용자에게 보이는 API 동작을 연결해 검증할 수 있습니다.
- truncate 실패를 테스트하려면 앞선 파일 열기 단계에서 실패하지 않는 조건을 만들어야 합니다. Landlock으로 특정 연산만 제한하면 목표한 실패 경로를 검증할 수 있습니다.
- 파일 권한과 backend의 오류 표현은 플랫폼마다 다르므로, 테스트의 전제 조건과 지원 범위를 명시하고 기존 데이터 보존까지 확인해야 합니다.