fxfs/object_store/
volume.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::errors::FxfsError;
6use crate::filesystem::FxFilesystem;
7use crate::object_store::directory::Directory;
8use crate::object_store::transaction::{lock_keys, Options, Transaction};
9use crate::object_store::tree_cache::TreeCache;
10use crate::object_store::{
11    load_store_info, LockKey, NewChildStoreOptions, ObjectDescriptor, ObjectStore, StoreOwner,
12};
13use anyhow::{anyhow, bail, ensure, Context, Error};
14use fxfs_crypto::Crypt;
15use std::sync::{Arc, Weak};
16
17// Volumes are a grouping of an object store and a root directory within this object store. They
18// model a hierarchical tree of objects within a single store.
19//
20// Typically there will be one root volume which is referenced directly by the superblock. This root
21// volume stores references to all other volumes on the system (as volumes/foo, volumes/bar, ...).
22// For now, this hierarchy is only one deep.
23
24pub const VOLUMES_DIRECTORY: &str = "volumes";
25
26/// RootVolume is the top-level volume which stores references to all of the other Volumes.
27pub struct RootVolume {
28    _root_directory: Directory<ObjectStore>,
29    filesystem: Arc<FxFilesystem>,
30}
31
32impl RootVolume {
33    pub fn volume_directory(&self) -> &Directory<ObjectStore> {
34        self.filesystem.object_manager().volume_directory()
35    }
36
37    /// Creates a new volume under a transaction lock.
38    pub async fn new_volume(
39        &self,
40        volume_name: &str,
41        owner: Weak<dyn StoreOwner>,
42        crypt: Option<Arc<dyn Crypt>>,
43    ) -> Result<Arc<ObjectStore>, Error> {
44        let root_store = self.filesystem.root_store();
45        let store;
46        let mut transaction = self
47            .filesystem
48            .clone()
49            .new_transaction(
50                lock_keys![LockKey::object(
51                    root_store.store_object_id(),
52                    self.volume_directory().object_id(),
53                )],
54                Options::default(),
55            )
56            .await?;
57
58        ensure!(
59            matches!(self.volume_directory().lookup(volume_name).await?, None),
60            FxfsError::AlreadyExists
61        );
62        store = root_store
63            .new_child_store(
64                &mut transaction,
65                NewChildStoreOptions { owner, crypt, ..Default::default() },
66                Box::new(TreeCache::new()),
67            )
68            .await?;
69        store.set_trace(self.filesystem.trace());
70
71        // We must register the store here because create will add mutations for the store.
72        self.filesystem.object_manager().add_store(store.clone());
73
74        // If the transaction fails, we must unregister the store.
75        struct CleanUp<'a>(&'a ObjectStore);
76        impl Drop for CleanUp<'_> {
77            fn drop(&mut self) {
78                self.0.filesystem().object_manager().forget_store(self.0.store_object_id());
79            }
80        }
81        let clean_up = CleanUp(&store);
82
83        // Actually create the store in the transaction.
84        store.create(&mut transaction).await?;
85
86        self.volume_directory()
87            .add_child_volume(&mut transaction, volume_name, store.store_object_id())
88            .await?;
89        transaction.commit().await?;
90
91        std::mem::forget(clean_up);
92
93        Ok(store)
94    }
95
96    /// Returns the volume with the given name.  This is not thread-safe.
97    pub async fn volume(
98        &self,
99        volume_name: &str,
100        owner: Weak<dyn StoreOwner>,
101        crypt: Option<Arc<dyn Crypt>>,
102    ) -> Result<Arc<ObjectStore>, Error> {
103        self.volume_from_id(
104            match self.volume_directory().lookup(volume_name).await?.ok_or(FxfsError::NotFound)? {
105                (object_id, ObjectDescriptor::Volume) => object_id,
106                _ => bail!(anyhow!(FxfsError::Inconsistent).context("Expected volume")),
107            },
108            owner,
109            crypt,
110        )
111        .await
112    }
113
114    /// Returns the volume with the given id.  This is not thread-safe.
115    pub async fn volume_from_id(
116        &self,
117        store_object_id: u64,
118        owner: Weak<dyn StoreOwner>,
119        crypt: Option<Arc<dyn Crypt>>,
120    ) -> Result<Arc<ObjectStore>, Error> {
121        let store =
122            self.filesystem.object_manager().store(store_object_id).ok_or(FxfsError::NotFound)?;
123        store.set_trace(self.filesystem.trace());
124        if let Some(crypt) = crypt {
125            store.unlock(owner, crypt).await.context("Failed to unlock volume")?;
126        } else if store.is_locked() {
127            bail!(FxfsError::AccessDenied);
128        }
129        Ok(store)
130    }
131
132    /// Deletes the given volume.  Consumes |transaction| and runs |callback| during commit.
133    pub async fn delete_volume(
134        &self,
135        volume_name: &str,
136        mut transaction: Transaction<'_>,
137        callback: impl FnOnce() + Send,
138    ) -> Result<(), Error> {
139        let object_id =
140            match self.volume_directory().lookup(volume_name).await?.ok_or(FxfsError::NotFound)? {
141                (object_id, ObjectDescriptor::Volume) => object_id,
142                _ => bail!(anyhow!(FxfsError::Inconsistent).context("Expected volume")),
143            };
144        let root_store = self.filesystem.root_store();
145
146        // Delete all the layers and encrypted mutations stored in root_store for this volume.
147        // This includes the StoreInfo itself.
148        let mut objects_to_delete = load_store_info(&root_store, object_id).await?.parent_objects();
149        objects_to_delete.push(object_id);
150
151        for object_id in &objects_to_delete {
152            root_store.adjust_refs(&mut transaction, *object_id, -1).await?;
153        }
154        // Mark all volume data as deleted.
155        self.filesystem.allocator().mark_for_deletion(&mut transaction, object_id).await;
156        // Remove the volume entry from the VolumeDirectory.
157        self.volume_directory()
158            .delete_child_volume(&mut transaction, volume_name, object_id)
159            .await?;
160        transaction.commit_with_callback(|_| callback()).await.context("commit")?;
161        // Tombstone the deleted objects.
162        for object_id in &objects_to_delete {
163            root_store.tombstone_object(*object_id, Options::default()).await?;
164        }
165        Ok(())
166    }
167}
168
169/// Returns the root volume for the filesystem.
170pub async fn root_volume(filesystem: Arc<FxFilesystem>) -> Result<RootVolume, Error> {
171    let root_store = filesystem.root_store();
172    let root_directory = Directory::open(&root_store, root_store.root_directory_object_id())
173        .await
174        .context("Unable to open root volume directory")?;
175    Ok(RootVolume { _root_directory: root_directory, filesystem })
176}
177
178/// Returns the object IDs for all volumes.
179pub async fn list_volumes(volume_directory: &Directory<ObjectStore>) -> Result<Vec<u64>, Error> {
180    let layer_set = volume_directory.store().tree().layer_set();
181    let mut merger = layer_set.merger();
182    let mut iter = volume_directory.iter(&mut merger).await?;
183    let mut object_ids = vec![];
184    while let Some((_, id, _)) = iter.get() {
185        object_ids.push(id);
186        iter.advance().await?;
187    }
188    Ok(object_ids)
189}
190
191#[cfg(test)]
192mod tests {
193    use super::root_volume;
194    use crate::filesystem::{FxFilesystem, JournalingObject, SyncOptions};
195    use crate::object_handle::{ObjectHandle, WriteObjectHandle};
196    use crate::object_store::directory::Directory;
197    use crate::object_store::transaction::{lock_keys, Options};
198    use crate::object_store::{LockKey, NO_OWNER};
199    use fxfs_insecure_crypto::InsecureCrypt;
200    use std::sync::Arc;
201    use storage_device::fake_device::FakeDevice;
202    use storage_device::DeviceHolder;
203
204    #[fuchsia::test]
205    async fn test_lookup_nonexistent_volume() {
206        let device = DeviceHolder::new(FakeDevice::new(8192, 512));
207        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
208        let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
209        root_volume
210            .volume("vol", NO_OWNER, Some(Arc::new(InsecureCrypt::new())))
211            .await
212            .err()
213            .expect("Volume shouldn't exist");
214        filesystem.close().await.expect("Close failed");
215    }
216
217    #[fuchsia::test]
218    async fn test_add_volume() {
219        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
220        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
221        let crypt = Arc::new(InsecureCrypt::new());
222        {
223            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
224            let store = root_volume
225                .new_volume("vol", NO_OWNER, Some(crypt.clone()))
226                .await
227                .expect("new_volume failed");
228            let mut transaction = filesystem
229                .clone()
230                .new_transaction(
231                    lock_keys![LockKey::object(
232                        store.store_object_id(),
233                        store.root_directory_object_id()
234                    )],
235                    Options::default(),
236                )
237                .await
238                .expect("new transaction failed");
239            let root_directory = Directory::open(&store, store.root_directory_object_id())
240                .await
241                .expect("open failed");
242            root_directory
243                .create_child_file(&mut transaction, "foo")
244                .await
245                .expect("create_child_file failed");
246            transaction.commit().await.expect("commit failed");
247            filesystem.sync(SyncOptions::default()).await.expect("sync failed");
248        };
249        {
250            filesystem.close().await.expect("Close failed");
251            let device = filesystem.take_device().await;
252            device.reopen(false);
253            let filesystem = FxFilesystem::open(device).await.expect("open failed");
254            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
255            let volume =
256                root_volume.volume("vol", NO_OWNER, Some(crypt)).await.expect("volume failed");
257            let root_directory = Directory::open(&volume, volume.root_directory_object_id())
258                .await
259                .expect("open failed");
260            root_directory.lookup("foo").await.expect("lookup failed").expect("not found");
261            filesystem.close().await.expect("Close failed");
262        };
263    }
264
265    #[fuchsia::test]
266    async fn test_delete_volume() {
267        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
268        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
269        let crypt = Arc::new(InsecureCrypt::new());
270        let store_object_id;
271        let parent_objects;
272        // Add volume and a file (some data).
273        {
274            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
275            let store = root_volume
276                .new_volume("vol", NO_OWNER, Some(crypt.clone()))
277                .await
278                .expect("new_volume failed");
279            store_object_id = store.store_object_id();
280            let mut transaction = filesystem
281                .clone()
282                .new_transaction(
283                    lock_keys![LockKey::object(store_object_id, store.root_directory_object_id())],
284                    Options::default(),
285                )
286                .await
287                .expect("new transaction failed");
288            let root_directory = Directory::open(&store, store.root_directory_object_id())
289                .await
290                .expect("open failed");
291            let handle = root_directory
292                .create_child_file(&mut transaction, "foo")
293                .await
294                .expect("create_child_file failed");
295            transaction.commit().await.expect("commit failed");
296
297            let mut buf = handle.allocate_buffer(8192).await;
298            buf.as_mut_slice().fill(0xaa);
299            handle.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
300            store.flush().await.expect("flush failed");
301            filesystem.sync(SyncOptions::default()).await.expect("sync failed");
302            parent_objects = store.parent_objects();
303            // Confirm parent objects exist.
304            for object_id in &parent_objects {
305                let _ = filesystem
306                    .root_store()
307                    .get_file_size(*object_id)
308                    .await
309                    .expect("Layer file missing? Bug in test.");
310            }
311        }
312        filesystem.close().await.expect("Close failed");
313        let device = filesystem.take_device().await;
314        device.reopen(false);
315        let filesystem = FxFilesystem::open(device).await.expect("open failed");
316        {
317            // Expect 8kiB accounted to the new volume.
318            assert_eq!(
319                filesystem.allocator().get_owner_allocated_bytes().get(&store_object_id),
320                Some(&8192)
321            );
322            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
323            let transaction = filesystem
324                .clone()
325                .new_transaction(
326                    lock_keys![LockKey::object(
327                        root_volume.volume_directory().store().store_object_id(),
328                        root_volume.volume_directory().object_id(),
329                    )],
330                    Options { borrow_metadata_space: true, ..Default::default() },
331                )
332                .await
333                .expect("new_transaction failed");
334            root_volume.delete_volume("vol", transaction, || {}).await.expect("delete_volume");
335            // Confirm data allocation is gone.
336            assert_eq!(
337                filesystem
338                    .allocator()
339                    .get_owner_allocated_bytes()
340                    .get(&store_object_id)
341                    .unwrap_or(&0),
342                &0,
343            );
344            // Confirm volume entry is gone.
345            root_volume
346                .volume("vol", NO_OWNER, Some(crypt.clone()))
347                .await
348                .err()
349                .expect("volume shouldn't exist anymore.");
350        }
351        filesystem.close().await.expect("Close failed");
352        let device = filesystem.take_device().await;
353        device.reopen(false);
354        // All artifacts of the original volume should be gone.
355        let filesystem = FxFilesystem::open(device).await.expect("open failed");
356        for object_id in &parent_objects {
357            let _ = filesystem
358                .root_store()
359                .get_file_size(*object_id)
360                .await
361                .err()
362                .expect("File wasn't deleted.");
363        }
364        filesystem.close().await.expect("Close failed");
365    }
366}