use android_hardware_security_see_storage::aidl::android::hardware::security::see::storage::{ CreationMode::CreationMode, FileMode::FileMode, ISecureStorage as SecureStorage, IStorageSession::IStorageSession, OpenOptions::OpenOptions, ReadIntegrity::ReadIntegrity, }; use binder::ExceptionCode; use test::{assert_ok, expect, fail}; mod helpers; use helpers::{ensure_deleted, Exists}; const CREATE_EXCLUSIVE: &'static OpenOptions = &OpenOptions { createMode: CreationMode::CREATE_EXCLUSIVE, accessMode: FileMode::READ_WRITE, readIntegrity: ReadIntegrity::NO_TAMPER, truncateOnOpen: true, allowWritesDuringAbUpdate: false, }; const NO_CREATE: &'static OpenOptions = &OpenOptions { createMode: CreationMode::NO_CREATE, accessMode: FileMode::READ_WRITE, readIntegrity: ReadIntegrity::NO_TAMPER, truncateOnOpen: false, allowWritesDuringAbUpdate: false, }; pub(crate) fn create_delete(ss: &(impl IStorageSession + ?Sized)) { let fname = "test_create_delete_file"; assert_ok!(ensure_deleted(ss, fname, Exists::Unknown)); { // Create file let _file = assert_ok!(ss.openFile(fname, CREATE_EXCLUSIVE)); assert_ok!(ss.commitChanges()); // Try to create file again match ss.openFile(fname, CREATE_EXCLUSIVE) { Ok(_) => fail!("openFile (on existing file) unexpectedly succeeded"), Err(e) if e.exception_code() == ExceptionCode::SERVICE_SPECIFIC && e.service_specific_error() == SecureStorage::ERR_ALREADY_EXISTS => { () } Err(e) => fail!("openFile (on existing file) failed with: {}", e), }; assert_ok!(ss.commitChanges()); // File closes } assert_ok!(ensure_deleted(ss, fname, Exists::Must)); } pub(crate) fn create_move_delete(ss: &(impl IStorageSession + ?Sized)) { let fname1 = "test_create_move_delete_1_file"; let fname2 = "test_create_move_delete_2_file"; assert_ok!(ensure_deleted(ss, fname1, Exists::Unknown)); assert_ok!(ensure_deleted(ss, fname2, Exists::Unknown)); { // Create file let file = assert_ok!(ss.openFile(fname1, CREATE_EXCLUSIVE)); assert_ok!(ss.commitChanges()); // Move fname1 -> fname2 assert_ok!(file.rename(fname2, CreationMode::CREATE_EXCLUSIVE)); assert_ok!(ss.commitChanges()); // Try to create fname2, with file still alive match ss.openFile(fname2, CREATE_EXCLUSIVE) { Ok(_) => fail!("openFile unexpectedly succeeded"), Err(e) if e.exception_code() == ExceptionCode::SERVICE_SPECIFIC && e.service_specific_error() == SecureStorage::ERR_ALREADY_EXISTS => { () } Err(e) => fail!("openFile failed with unexpected error: {}", e), }; assert_ok!(ss.commitChanges()); // file closes } // Try to create fname2, now that file is closed match ss.openFile(fname2, CREATE_EXCLUSIVE) { Ok(_) => fail!("openFile unexpectedly succeeded"), Err(e) if e.exception_code() == ExceptionCode::SERVICE_SPECIFIC && e.service_specific_error() == SecureStorage::ERR_ALREADY_EXISTS => { () } Err(e) => fail!("openFile failed with unexpected error: {}", e), }; assert_ok!(ss.commitChanges()); { // Recreate fname1 let file = assert_ok!(ss.openFile(fname1, CREATE_EXCLUSIVE)); assert_ok!(ss.commitChanges()); // Move doesn't work now that fname2 exists match file.rename(fname2, CreationMode::CREATE_EXCLUSIVE) { Ok(_) => panic!("rename unexpectedly succeeded"), Err(e) if e.exception_code() == ExceptionCode::SERVICE_SPECIFIC && e.service_specific_error() == SecureStorage::ERR_ALREADY_EXISTS => { () } Err(e) => panic!("rename failed with unexpected error: {}", e), }; // file closes } assert_ok!(ensure_deleted(ss, fname1, Exists::Must)); assert_ok!(ensure_deleted(ss, fname2, Exists::Must)); } pub(crate) fn file_list(ss: &(impl IStorageSession + ?Sized)) { use core::array::from_fn; use std::collections::HashSet; let fnames: [String; 100] = from_fn(|i| format!("test_file_list_{:02}_file", i)); for fname in &fnames { assert_ok!(ensure_deleted(ss, fname, Exists::Unknown), "file: {}", fname); } { let dir = assert_ok!(ss.openDir("", ReadIntegrity::NO_TAMPER)); let filenames = assert_ok!(dir.readNextFilenames(0)); expect!(filenames.is_empty(), "Found unexpected files: {:?}", filenames); } // Create, commit, and close file #0 { let _file = assert_ok!(ss.openFile(&fnames[0], CREATE_EXCLUSIVE)); assert_ok!(ss.commitChanges()); } // Create and close (don't commit) other files for fname in &fnames[1..] { let _file = assert_ok!(ss.openFile(fname, CREATE_EXCLUSIVE), "for fname {}", fname); } let mut read_file_names = HashSet::new(); { let dir = assert_ok!(ss.openDir("", ReadIntegrity::NO_TAMPER)); let mut filenames = assert_ok!(dir.readNextFilenames(0)); while !filenames.is_empty() { for filename in filenames { let existed = read_file_names.replace(filename); if let Some(previous) = existed { fail!("NextFilenameResult returned file more than once: {}", previous); } } filenames = assert_ok!(dir.readNextFilenames(0)); } } assert_ok!(ss.commitChanges()); // Clean up for fname in &fnames { assert_ok!(ensure_deleted(ss, fname, Exists::Must), "file: {}", fname); } let expected = fnames.into_iter().collect::>(); let missing = expected.difference(&read_file_names).collect::>(); let extra = read_file_names.difference(&expected).collect::>(); expect!(missing.is_empty(), "Did not find the following expected files: {:?}", missing); expect!(extra.is_empty(), "Found the following unexpected files: {:?}", extra); } pub(crate) fn write_read_sequential(ss: &(impl IStorageSession + ?Sized)) { use helpers::{read_pattern, write_pattern}; let fname = "test_write_read_sequential"; let chunks = 32; let chunk_len = 2048; assert_ok!(ensure_deleted(ss, fname, Exists::Unknown)); { let file = assert_ok!(ss.openFile(fname, CREATE_EXCLUSIVE)); assert_ok!(ss.commitChanges()); assert_ok!(write_pattern(&*file, 0, chunks, chunk_len)); assert_ok!(ss.commitChanges()); assert_ok!(read_pattern(&*file, 0, chunks, chunk_len)); } // file closes { let file = assert_ok!(ss.openFile(fname, NO_CREATE)); assert_ok!(read_pattern(&*file, 0, chunks, chunk_len)); } // file closes assert_ok!(ensure_deleted(ss, fname, Exists::Must)); }