Skip to content

Commit bca6bae

Browse files
committed
Remove unsafe from sync and check
The blob existence invariant is gone and these operations are now safe. Signed-off-by: J Robert Ray <[email protected]>
1 parent 54bcfcf commit bca6bae

File tree

2 files changed

+27
-101
lines changed

2 files changed

+27
-101
lines changed

crates/spfs/src/check.rs

Lines changed: 21 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,7 @@ where
254254
}
255255
Err(err) => Err(err),
256256
Ok((obj, fallback)) => {
257-
let mut res = unsafe {
258-
// Safety: it's unsafe to call this unless the object
259-
// is known to exist, but we just loaded it from the repo
260-
// or had it synced via the callback
261-
self.check_object_with_perms_opt(obj, perms).await?
262-
};
257+
let mut res = self.check_object_with_perms_opt(obj, perms).await?;
263258
if matches!(fallback, Fallback::Repaired) {
264259
tracing::trace!(?digest, ?res, "setting repaired flag");
265260
res.set_repaired();
@@ -272,29 +267,16 @@ where
272267
/// Validate that the identified object's children all exist.
273268
///
274269
/// To also check if the object exists, use [`Self::check_digest`]
275-
///
276-
/// # Safety
277-
///
278-
/// This function may sync payloads without checking blob data,
279-
/// which is unsafe. This function is unsafe to call unless the object
280-
/// is known to exist in the repository being checked
281-
pub async unsafe fn check_object(&self, obj: graph::Object) -> Result<CheckObjectResult> {
282-
// Safety: unsafe unless the object exists, we pass this up to the caller
283-
unsafe { self.check_object_with_perms_opt(obj, None).await }
270+
pub async fn check_object(&self, obj: graph::Object) -> Result<CheckObjectResult> {
271+
self.check_object_with_perms_opt(obj, None).await
284272
}
285273

286274
/// Validate that all children of this object exist.
287275
///
288276
/// Any provided permissions are associated with the blob when
289277
/// syncing it. See [`tracking::BlobRead::permissions`].
290-
///
291-
/// # Safety
292-
///
293-
/// This function may sync payloads without checking blob data,
294-
/// which is unsafe. This function is unsafe to call unless the object
295-
/// is known to exist in the repository being checked
296278
#[async_recursion::async_recursion]
297-
async unsafe fn check_object_with_perms_opt(
279+
async fn check_object_with_perms_opt(
298280
&self,
299281
obj: graph::Object,
300282
perms: Option<u32>,
@@ -310,11 +292,9 @@ where
310292
let res = match obj.into_enum() {
311293
Enum::Layer(obj) => CheckObjectResult::Layer(self.check_layer(obj).await?.into()),
312294
Enum::Platform(obj) => CheckObjectResult::Platform(self.check_platform(obj).await?),
313-
Enum::Blob(obj) => CheckObjectResult::Blob(unsafe {
314-
// Safety: it is unsafe to call this function unless the blob
315-
// is known to exist, which is the same rule we pass up to the caller
316-
self.must_check_blob_with_perms_opt(&obj, perms).await?
317-
}),
295+
Enum::Blob(obj) => {
296+
CheckObjectResult::Blob(self.must_check_blob_with_perms_opt(&obj, perms).await?)
297+
}
318298
Enum::Manifest(obj) => CheckObjectResult::Manifest(self.check_manifest(obj).await?),
319299
};
320300
self.reporter.checked_object(&res);
@@ -415,51 +395,31 @@ where
415395
}
416396

417397
/// Validate that the identified blob has its payload.
418-
///
419-
/// To also check if the blob object exists, use [`Self::check_digest`]
420-
///
421-
/// # Safety
422-
/// This function may sync a payload without
423-
/// syncing the blob, which is unsafe unless the blob
424-
/// is known to exist in the repository being checked
425-
pub async unsafe fn check_blob(&self, blob: &graph::Blob) -> Result<CheckBlobResult> {
398+
pub async fn check_blob(&self, blob: &graph::Blob) -> Result<CheckBlobResult> {
426399
let digest = blob.digest();
427400
if let Some(CheckProgress::CheckStarted) = self
428401
.processed_digests
429402
.insert(*digest, CheckProgress::CheckStarted)
430403
{
431404
return Ok(CheckBlobResult::Duplicate);
432405
}
433-
// Safety: this function may sync a payload and so
434-
// is unsafe to call unless we know the blob exists,
435-
// which is why this is an unsafe function
436-
unsafe { self.must_check_blob_with_perms_opt(blob, None).await }
406+
self.must_check_blob_with_perms_opt(blob, None).await
437407
}
438408

439409
/// Checks a blob, ignoring whether it has already been checked and
440410
/// without logging that it has been checked.
441411
///
442412
/// Any provided permissions are associated with the blob when
443413
/// syncing it. See [`tracking::BlobRead::permissions`].
444-
///
445-
/// # Safety
446-
///
447-
/// This function may sync a payload without
448-
/// syncing the blob, which is unsafe unless the blob
449-
/// is known to exist in the repository being checked
450-
async unsafe fn must_check_blob_with_perms_opt(
414+
async fn must_check_blob_with_perms_opt(
451415
&self,
452416
blob: &graph::Blob,
453417
perms: Option<u32>,
454418
) -> Result<CheckBlobResult> {
455419
self.reporter.visit_blob(blob);
456-
let result = unsafe {
457-
// Safety: this function may sync a payload and so
458-
// is unsafe to call unless we know the blob exists,
459-
// which is why this is an unsafe function
460-
self.check_payload_with_perms_opt(*blob.payload(), perms)
461-
.await?
462-
};
420+
let result = self
421+
.check_payload_with_perms_opt(*blob.payload(), perms)
422+
.await?;
463423
let res = CheckBlobResult::Checked {
464424
blob: blob.to_owned(),
465425
result,
@@ -470,29 +430,13 @@ where
470430
}
471431

472432
/// Check a payload with the provided digest, repairing it if possible.
473-
///
474-
/// # Safety
475-
///
476-
/// This function may repair a payload, which
477-
/// is unsafe to do if the associated blob is not synced
478-
/// with it or already present.
479-
pub async unsafe fn check_payload(
480-
&self,
481-
digest: encoding::Digest,
482-
) -> Result<CheckPayloadResult> {
483-
// Safety: unsafe unless the blob exists, we pass this up to the caller
484-
unsafe { self.check_payload_with_perms_opt(digest, None).await }
433+
pub async fn check_payload(&self, digest: encoding::Digest) -> Result<CheckPayloadResult> {
434+
self.check_payload_with_perms_opt(digest, None).await
485435
}
486436

487437
/// Any provided permissions are associated with the blob when
488438
/// syncing it. See [`tracking::BlobRead::permissions`].
489-
///
490-
/// # Safety
491-
///
492-
/// This function may repair a payload, which
493-
/// is unsafe to do if the associated blob is not synced
494-
/// with it or already present.
495-
async unsafe fn check_payload_with_perms_opt(
439+
async fn check_payload_with_perms_opt(
496440
&self,
497441
digest: encoding::Digest,
498442
perms: Option<u32>,
@@ -501,13 +445,11 @@ where
501445
let mut result = CheckPayloadResult::Missing(digest);
502446
if self.repo.has_payload(digest).await {
503447
result = CheckPayloadResult::Ok;
504-
} else if let Some(syncer) = &self.repair_with {
505-
// Safety: this sync is unsafe unless the blob is also created
506-
// or exists. We pass this rule up to the caller.
507-
if let Ok(r) = unsafe { syncer.sync_payload_with_perms_opt(digest, perms).await } {
508-
self.reporter.repaired_payload(&r);
509-
result = CheckPayloadResult::Repaired;
510-
}
448+
} else if let Some(syncer) = &self.repair_with
449+
&& let Ok(r) = syncer.sync_payload_with_perms_opt(digest, perms).await
450+
{
451+
self.reporter.repaired_payload(&r);
452+
result = CheckPayloadResult::Repaired;
511453
}
512454
self.reporter.checked_payload(&result);
513455
Ok(result)

crates/spfs/src/sync.rs

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -455,12 +455,9 @@ impl<'src, 'dst> Syncer<'src, 'dst> {
455455
return Ok(SyncBlobResult::Skipped);
456456
}
457457
self.reporter.visit_blob(blob);
458-
// Safety: sync_payload is unsafe to call unless the blob
459-
// is synced with it, which is the purpose of this function.
460-
let result = unsafe {
461-
self.sync_payload_with_perms_opt(*blob.payload(), perms)
462-
.await?
463-
};
458+
let result = self
459+
.sync_payload_with_perms_opt(*blob.payload(), perms)
460+
.await?;
464461
self.processed_digests.insert(*digest);
465462
let res = SyncBlobResult::Synced {
466463
blob: blob.to_owned(),
@@ -471,26 +468,13 @@ impl<'src, 'dst> Syncer<'src, 'dst> {
471468
}
472469

473470
/// Sync a payload with the provided digest
474-
///
475-
/// # Safety
476-
///
477-
/// It is unsafe to call this sync function on its own,
478-
/// as any payload should be synced alongside its
479-
/// corresponding Blob instance - use [`Self::sync_blob`] instead
480-
pub async unsafe fn sync_payload(&self, digest: encoding::Digest) -> Result<SyncPayloadResult> {
481-
// Safety: these concerns are passed on to the caller
482-
unsafe { self.sync_payload_with_perms_opt(digest, None).await }
471+
pub async fn sync_payload(&self, digest: encoding::Digest) -> Result<SyncPayloadResult> {
472+
self.sync_payload_with_perms_opt(digest, None).await
483473
}
484474

485475
/// Sync a payload with the provided digest and optional set
486476
/// of desired permissions.
487-
///
488-
/// # Safety
489-
///
490-
/// It is unsafe to call this sync function on its own,
491-
/// as any payload should be synced alongside its
492-
/// corresponding Blob instance - use [`Self::sync_blob`] instead
493-
pub(crate) async unsafe fn sync_payload_with_perms_opt(
477+
pub(crate) async fn sync_payload_with_perms_opt(
494478
&self,
495479
digest: encoding::Digest,
496480
perms: Option<u32>,

0 commit comments

Comments
 (0)