1use crate::errors::FxfsError;
6use crate::lsm_tree::Query;
7use crate::lsm_tree::types::{ItemRef, LayerIterator};
8use crate::object_store::transaction::{LockKey, Mutation, Options, lock_keys};
9use crate::object_store::{
10 BytesAndNodes, ObjectKey, ObjectKeyData, ObjectKind, ObjectStore, ObjectValue, ProjectProperty,
11};
12use anyhow::{Error, anyhow};
13use fprint::TypeFingerprint;
14use fxfs_macros::SerializeKey;
15use serde::{Deserialize, Serialize};
16use std::num::NonZeroU64;
17
18impl ObjectStore {
19 pub async fn set_project_limit(
22 &self,
23 project_id: ProjectId,
24 bytes: u64,
25 nodes: u64,
26 ) -> Result<(), Error> {
27 let root_id = self.root_directory_object_id();
28 let mut transaction = self
29 .new_transaction(
30 lock_keys![LockKey::ProjectId {
31 store_object_id: self.store_object_id,
32 project_id
33 }],
34 Options::default(),
35 )
36 .await?;
37 transaction.add(
38 self.store_object_id,
39 Mutation::replace_or_insert_object(
40 ObjectKey::project_limit(root_id, project_id),
41 ObjectValue::BytesAndNodes {
42 bytes: bytes.try_into().map_err(|_| FxfsError::TooBig)?,
43 nodes: nodes.try_into().map_err(|_| FxfsError::TooBig)?,
44 },
45 ),
46 );
47 transaction.commit().await?;
48 Ok(())
49 }
50
51 pub async fn clear_project_limit(&self, project_id: ProjectId) -> Result<(), Error> {
54 let root_id = self.root_directory_object_id();
55 let mut transaction = self
56 .new_transaction(
57 lock_keys![LockKey::ProjectId {
58 store_object_id: self.store_object_id,
59 project_id
60 }],
61 Options::default(),
62 )
63 .await?;
64 transaction.add(
65 self.store_object_id,
66 Mutation::replace_or_insert_object(
67 ObjectKey::project_limit(root_id, project_id),
68 ObjectValue::None,
69 ),
70 );
71 transaction.commit().await?;
72 Ok(())
73 }
74
75 pub async fn set_project_for_node(
77 &self,
78 node_id: u64,
79 project_id: ProjectId,
80 ) -> Result<(), Error> {
81 let root_id = self.root_directory_object_id();
82 let mut transaction = self
83 .new_transaction(
84 lock_keys![LockKey::object(self.store_object_id, node_id)],
85 Options::default(),
86 )
87 .await?;
88
89 let object_key = ObjectKey::object(node_id);
90 let (kind, mut attributes) =
91 match self.tree().find_value(&object_key).await?.ok_or(FxfsError::NotFound)? {
92 ObjectValue::Object { kind, attributes } => (kind, attributes),
93 _ => return Err(FxfsError::Inconsistent.into()),
94 };
95 match kind {
97 ObjectKind::File { .. } | ObjectKind::Directory { .. } => (),
98 ObjectKind::Symlink { .. } | ObjectKind::EncryptedSymlink { .. } => {
101 return Err(FxfsError::NotSupported.into());
102 }
103 ObjectKind::Graveyard => return Err(FxfsError::Inconsistent.into()),
104 }
105 let storage_size = attributes.allocated_size.try_into().map_err(|_| FxfsError::TooBig)?;
106 let old_project_id = attributes.project_id;
107 if old_project_id == Some(project_id) {
108 return Ok(());
109 }
110 attributes.project_id = Some(project_id);
111
112 transaction.add(
113 self.store_object_id,
114 Mutation::replace_or_insert_object(
115 object_key,
116 ObjectValue::Object { kind, attributes },
117 ),
118 );
119 transaction.merge_bytes_and_nodes(
120 self.store_object_id,
121 ObjectKey::project_usage(root_id, project_id),
122 BytesAndNodes { bytes: storage_size, nodes: 1 },
123 );
124 if let Some(old_project_id) = old_project_id {
125 transaction.merge_bytes_and_nodes(
126 self.store_object_id,
127 ObjectKey::project_usage(root_id, old_project_id),
128 BytesAndNodes { bytes: -storage_size, nodes: -1 },
129 );
130 }
131 transaction.commit().await?;
132 Ok(())
133 }
134
135 pub async fn get_project_for_node(&self, node_id: u64) -> Result<Option<ProjectId>, Error> {
137 let project_id = self
138 .tree()
139 .find_map(&ObjectKey::object(node_id), |item| match item.value {
140 ObjectValue::Object { attributes, .. } => Ok(attributes.project_id),
141 _ => Err(anyhow!(FxfsError::Inconsistent)),
142 })
143 .await?
144 .ok_or(FxfsError::NotFound)??;
145 Ok(project_id)
146 }
147
148 pub async fn clear_project_for_node(&self, node_id: u64) -> Result<(), Error> {
151 let root_id = self.root_directory_object_id();
152 let mut transaction = self
153 .new_transaction(
154 lock_keys![LockKey::object(self.store_object_id, node_id)],
155 Options::default(),
156 )
157 .await?;
158
159 let object_key = ObjectKey::object(node_id);
160 let (kind, mut attributes) =
161 match self.tree().find_value(&object_key).await?.ok_or(FxfsError::NotFound)? {
162 ObjectValue::Object { kind, attributes } => (kind, attributes),
163 _ => return Err(FxfsError::Inconsistent.into()),
164 };
165 let Some(old_project_id) = attributes.project_id else {
166 return Ok(());
167 };
168 match kind {
170 ObjectKind::File { .. } | ObjectKind::Directory { .. } => (),
171 ObjectKind::Symlink { .. } | ObjectKind::EncryptedSymlink { .. } => {
174 return Err(FxfsError::NotSupported.into());
175 }
176 ObjectKind::Graveyard => return Err(FxfsError::Inconsistent.into()),
177 }
178 attributes.project_id = None;
179 let storage_size = attributes.allocated_size;
180 transaction.add(
181 self.store_object_id,
182 Mutation::replace_or_insert_object(
183 object_key,
184 ObjectValue::Object { kind, attributes },
185 ),
186 );
187 transaction.merge_bytes_and_nodes(
190 self.store_object_id,
191 ObjectKey::project_usage(root_id, old_project_id),
192 BytesAndNodes {
193 bytes: -(storage_size.try_into().map_err(|_| FxfsError::TooBig)?),
194 nodes: -1,
195 },
196 );
197 transaction.commit().await?;
198 Ok(())
199 }
200
201 pub async fn list_projects(
206 &self,
207 start_id: Option<ProjectId>,
208 max_entries: usize,
209 ) -> Result<(Vec<ProjectId>, Option<ProjectId>), Error> {
210 let start_id = start_id.unwrap_or(ProjectId::SORTED_START);
211 let root_dir_id = self.root_directory_object_id();
212 let layer_set = self.tree().layer_set();
213 let mut merger = layer_set.merger();
214 let mut iter = merger
215 .query(Query::FullRange(&ObjectKey::project_limit(root_dir_id, start_id)))
216 .await?;
217 let mut entries = Vec::new();
218 let mut prev_entry: Option<ProjectId> = None;
219 let mut next_entry = None;
220 while let Some(ItemRef { key: ObjectKey { object_id, data: key_data }, value, .. }) =
221 iter.get()
222 {
223 if *object_id != root_dir_id {
225 break;
226 }
227 match key_data {
228 ObjectKeyData::Project { project_id, .. } => {
229 if *value != ObjectValue::None && prev_entry < Some(*project_id) {
231 if entries.len() == max_entries {
232 next_entry = Some(*project_id);
233 break;
234 }
235 prev_entry = Some(*project_id);
236 entries.push(*project_id);
237 }
238 }
239 _ => {
241 break;
242 }
243 }
244 iter.advance().await?;
245 }
246 Ok((entries, next_entry))
248 }
249
250 pub async fn project_info(
253 &self,
254 project_id: ProjectId,
255 ) -> Result<(Option<(u64, u64)>, Option<(u64, u64)>), Error> {
256 let root_id = self.root_directory_object_id();
257 let layer_set = self.tree().layer_set();
258 let mut merger = layer_set.merger();
259 let mut iter =
260 merger.query(Query::FullRange(&ObjectKey::project_limit(root_id, project_id))).await?;
261 let mut limit = None;
262 let mut usage = None;
263 while let Some(ItemRef { key: ObjectKey { object_id, data: key_data }, value, .. }) =
265 iter.get()
266 {
267 if *object_id != root_id {
269 break;
270 }
271 if let (
272 ObjectKeyData::Project { project_id: found_project_id, property },
273 ObjectValue::BytesAndNodes { bytes, nodes },
274 ) = (key_data, value)
275 {
276 if *found_project_id != project_id {
278 break;
279 }
280 let raw_value: (u64, u64) = (
281 (*bytes).try_into().map_err(|_| FxfsError::Inconsistent)?,
283 (*nodes).try_into().map_err(|_| FxfsError::Inconsistent)?,
284 );
285 match property {
286 ProjectProperty::Limit => limit = Some(raw_value),
287 ProjectProperty::Usage => usage = Some(raw_value),
288 }
289 } else {
290 break;
291 }
292 iter.advance().await?;
293 }
294 Ok((limit, usage))
295 }
296}
297
298#[derive(
299 Clone,
300 Copy,
301 PartialEq,
302 Eq,
303 PartialOrd,
304 Ord,
305 Debug,
306 Serialize,
307 Deserialize,
308 Hash,
309 TypeFingerprint,
310 SerializeKey,
311)]
312#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
313#[repr(transparent)]
314pub struct ProjectId(NonZeroU64);
315
316impl ProjectId {
317 pub const SORTED_START: Self = Self::new(1).unwrap();
318
319 pub const fn new(project_id: u64) -> Option<Self> {
320 match NonZeroU64::new(project_id) {
321 None => None,
322 Some(non_zero) => Some(Self(non_zero)),
323 }
324 }
325
326 pub const fn raw(self) -> u64 {
328 self.0.get()
329 }
330}
331
332impl log::kv::ToValue for ProjectId {
333 fn to_value(&self) -> log::kv::Value<'_> {
334 log::kv::Value::from(self.0)
335 }
336}
337
338impl std::fmt::Display for ProjectId {
339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340 std::fmt::Display::fmt(&self.0, f)
341 }
342}
343
344pub trait ProjectIdExt {
346 fn raw(self) -> u64;
348}
349
350impl ProjectIdExt for Option<ProjectId> {
351 fn raw(self) -> u64 {
352 match self {
353 None => 0,
354 Some(project_id) => project_id.raw(),
355 }
356 }
357}
358
359pub mod optional_project_id {
360 use super::{ProjectId, ProjectIdExt};
361 use serde::{Deserializer, Serializer};
362
363 pub fn serialize<S>(value: &Option<ProjectId>, serializer: S) -> Result<S::Ok, S::Error>
365 where
366 S: Serializer,
367 {
368 serializer.serialize_u64(value.raw())
369 }
370
371 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<ProjectId>, D::Error>
373 where
374 D: Deserializer<'de>,
375 {
376 deserializer.deserialize_u64(Visitor)
377 }
378
379 struct Visitor;
380 impl<'de> serde::de::Visitor<'de> for Visitor {
381 type Value = Option<ProjectId>;
382
383 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384 write!(formatter, "a u64",)
385 }
386
387 fn visit_u64<E>(self, raw: u64) -> Result<Self::Value, E>
388 where
389 E: serde::de::Error,
390 {
391 Ok(ProjectId::new(raw))
392 }
393 }
394
395 pub fn fingerprint<T>() -> String {
396 "u64".to_string()
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use super::{ProjectId, ProjectIdExt};
406 use crate::serialized_types::{LATEST_VERSION, Versioned};
407 use serde::{Deserialize, Serialize};
408
409 impl Versioned for ProjectId {}
412
413 #[test]
414 fn test_project_id_serialization_matches_u64() {
415 fn verify_matches(x: u64) {
416 let mut u64_buf = Vec::new();
418 x.serialize_into(&mut u64_buf).unwrap();
419
420 let project_id = ProjectId::new(x).unwrap();
422 let mut project_id_buf = Vec::new();
423 project_id.serialize_into(&mut project_id_buf).unwrap();
424
425 assert_eq!(u64_buf, project_id_buf);
427
428 let deserialized_project_id =
430 ProjectId::deserialize_from(&mut u64_buf.as_slice(), LATEST_VERSION).unwrap();
431 assert_eq!(deserialized_project_id, project_id);
432
433 let deserialized_u64 =
435 u64::deserialize_from(&mut project_id_buf.as_slice(), LATEST_VERSION).unwrap();
436 assert_eq!(deserialized_u64, x);
437 }
438
439 verify_matches(1);
440 verify_matches(2);
441 verify_matches(u16::MAX as u64 - 1);
442 verify_matches(u16::MAX as u64);
443 verify_matches(u16::MAX as u64 + 1);
444 verify_matches(u32::MAX as u64 - 1);
445 verify_matches(u32::MAX as u64);
446 verify_matches(u32::MAX as u64 + 1);
447 verify_matches(u64::MAX - 1);
448 verify_matches(u64::MAX);
449 }
450
451 #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Versioned)]
452 struct OptionWrapper {
453 #[serde(with = "super::optional_project_id")]
454 project_id: Option<ProjectId>,
455 }
456
457 #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Versioned)]
458 struct U64Wrapper {
459 project_id: u64,
460 }
461
462 #[test]
463 fn test_optional_project_id_serialization_matches_u64() {
464 fn verify_matches(x: u64) {
465 let u64_wrapper = U64Wrapper { project_id: x };
467 let mut u64_buf = Vec::new();
468 u64_wrapper.serialize_into(&mut u64_buf).unwrap();
469
470 let opt_project_id = OptionWrapper { project_id: ProjectId::new(x) };
472 let mut opt_buf = Vec::new();
473 opt_project_id.serialize_into(&mut opt_buf).unwrap();
474
475 assert_eq!(u64_buf, opt_buf);
477
478 let deserialized_opt =
480 OptionWrapper::deserialize_from(&mut u64_buf.as_slice(), LATEST_VERSION).unwrap();
481 assert_eq!(deserialized_opt, opt_project_id);
482
483 let deserialized_u64 =
485 U64Wrapper::deserialize_from(&mut opt_buf.as_slice(), LATEST_VERSION).unwrap();
486 assert_eq!(deserialized_u64, u64_wrapper);
487 }
488
489 verify_matches(0);
490 verify_matches(1);
491 verify_matches(2);
492 verify_matches(u16::MAX as u64 - 1);
493 verify_matches(u16::MAX as u64);
494 verify_matches(u16::MAX as u64 + 1);
495 verify_matches(u32::MAX as u64 - 1);
496 verify_matches(u32::MAX as u64);
497 verify_matches(u32::MAX as u64 + 1);
498 verify_matches(u64::MAX - 1);
499 verify_matches(u64::MAX);
500 }
501
502 #[test]
503 fn test_option_project_id_sorting() {
504 let none = ProjectId::new(0);
505 assert!(none.is_none());
506
507 let one = ProjectId::new(1);
508 let max = ProjectId::new(u64::MAX);
509
510 assert!(none < one);
512 assert!(none < max);
513 assert!(one < max);
514 }
515
516 #[test]
517 fn test_project_id_raw() {
518 assert_eq!(ProjectId::new(0).raw(), 0);
519 assert_eq!(ProjectId::new(1).raw(), 1);
520 assert_eq!(ProjectId::new(u64::MAX).raw(), u64::MAX);
521 }
522}