1 // Copyright (C) 2024 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use super::BootToken; 16 use zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeroes, Ref}; 17 18 use liberror::Error; 19 20 /// Tracks whether slot metadata differs from on-disk representation. 21 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 22 pub enum CacheStatus { 23 /// Slot metadata is the same as on disk 24 Clean, 25 /// Slot metadata has been modified 26 Dirty, 27 } 28 29 /// Trait that describes the operations all slot metadata implementations must support 30 /// to be used as the backing store in a SlotBlock. 31 pub trait MetadataBytes: Copy + AsBytes + FromBytes + FromZeroes + Default { 32 /// Returns a zerocopy reference to Self if buffer 33 /// represents a valid serialization of Self. 34 /// Implementors should check for invariants, 35 /// e.g. checksums, magic numbers, and version numbers. 36 /// 37 /// Returns Err if the buffer does not represent a valid structure. validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, Self>, Error>38 fn validate<B: ByteSlice>(buffer: B) -> Result<Ref<B, Self>, Error>; 39 40 /// Called right before writing metadata back to disk. 41 /// Implementors should restore invariants, 42 /// update checksums, or take other appropriate actions. prepare_for_sync(&mut self)43 fn prepare_for_sync(&mut self); 44 } 45 46 /// Generalized description of a partition-backed ABR metadata structure. 47 pub struct SlotBlock<MB: MetadataBytes> { 48 // Internally tracked cache clean/dirty info 49 cache_status: CacheStatus, 50 // SlotBlock holds the boot token until mark_boot_attempt gets called. 51 boot_token: Option<BootToken>, 52 // Serialized slot metadata 53 data: MB, 54 } 55 56 impl<'a, MB: MetadataBytes> SlotBlock<MB> { 57 /// Note to those implementing Manager for SlotBlock<CustomType>: 58 /// Be very, very careful with custody of the boot token. 59 /// If you release it outside of the implementation of Manager::mark_boot_attempt, 60 /// mark_boot_attempt will fail and the kernel may boot without tracking the attempt. 61 /// If you lose the token, the only way to get it back is to reboot the device. take_boot_token(&mut self) -> Option<BootToken>62 pub fn take_boot_token(&mut self) -> Option<BootToken> { 63 self.boot_token.take() 64 } 65 66 /// Returns a mutable reference to the slot metadata and marks the cache as dirty. get_mut_data(&mut self) -> &mut MB67 pub fn get_mut_data(&mut self) -> &mut MB { 68 self.cache_status = CacheStatus::Dirty; 69 &mut self.data 70 } 71 72 /// Returns an immutable reference to the slot metadata get_data(&self) -> &MB73 pub fn get_data(&self) -> &MB { 74 &self.data 75 } 76 77 #[cfg(test)] 78 /// Returns the cache status cache_status(&self) -> CacheStatus79 pub fn cache_status(&self) -> CacheStatus { 80 self.cache_status 81 } 82 83 /// Attempt to deserialize a slot control block 84 /// 85 /// # Returns 86 /// * `SlotBlock` - returns either the deserialized 87 /// representation of the slot control block 88 /// OR a fresh, default valued slot control block 89 /// if there was an internal error. 90 /// 91 /// TODO(b/329116902): errors are logged deserialize<B: ByteSlice>(buffer: B, boot_token: BootToken) -> Self92 pub fn deserialize<B: ByteSlice>(buffer: B, boot_token: BootToken) -> Self { 93 // TODO(b/329116902): log failures 94 // validate(buffer) 95 // .inspect_err(|e| { 96 // eprintln!("ABR metadata failed verification, using metadata defaults: {e}") 97 // }) 98 let (data, cache_status) = match MB::validate(buffer) { 99 Ok(data) => (*data, CacheStatus::Clean), 100 Err(_) => (Default::default(), CacheStatus::Dirty), 101 }; 102 103 SlotBlock { cache_status, boot_token: Some(boot_token), data } 104 } 105 106 /// Write back slot metadata to disk. 107 /// 108 /// The MetadataBytes type should reestablish any invariants when `prepare_for_sync` is called, 109 /// e.g. recalculating checksums. 110 /// 111 /// Does NOT write back to disk if no changes have been made and the cache is clean. 112 /// Panics if the write attempt fails. 113 /// 114 /// # Args 115 /// 116 /// * `persist`: A user provided closure for persisting a given slot metadata bytes to storage. sync_to_disk(&mut self, persist: &mut dyn FnMut(&mut [u8]) -> Result<(), Error>)117 pub fn sync_to_disk(&mut self, persist: &mut dyn FnMut(&mut [u8]) -> Result<(), Error>) { 118 if self.cache_status == CacheStatus::Clean { 119 return; 120 } 121 122 self.data.prepare_for_sync(); 123 124 match persist(self.get_mut_data().as_bytes_mut()) { 125 Ok(_) => self.cache_status = CacheStatus::Clean, 126 Err(e) => panic!("{}", e), 127 }; 128 } 129 } 130 131 #[cfg(test)] 132 impl<MB: MetadataBytes> Default for SlotBlock<MB> { 133 /// Returns a default valued SlotBlock. 134 /// Only used in tests because BootToken cannot be constructed out of crate. default() -> Self135 fn default() -> Self { 136 Self { 137 cache_status: CacheStatus::Clean, 138 boot_token: Some(BootToken(())), 139 data: Default::default(), 140 } 141 } 142 } 143