chore: connect shared file contracts for Bash validation
This commit is contained in:
@@ -48,6 +48,10 @@ pub struct CheckoutRequest {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(tag = "operation", rename_all = "snake_case", deny_unknown_fields)]
|
||||
pub enum CheckoutOperation {
|
||||
ReadBytes {
|
||||
offset: u64,
|
||||
max_bytes: usize,
|
||||
},
|
||||
Read {
|
||||
offset: usize,
|
||||
limit: usize,
|
||||
@@ -71,7 +75,7 @@ pub enum CheckoutOperation {
|
||||
impl CheckoutOperation {
|
||||
pub fn capability(&self) -> WorkdirSessionCapability {
|
||||
match self {
|
||||
Self::Read { .. } => WorkdirSessionCapability::Read,
|
||||
Self::Read { .. } | Self::ReadBytes { .. } => WorkdirSessionCapability::Read,
|
||||
Self::Edit { .. } => WorkdirSessionCapability::Edit,
|
||||
Self::Write { .. } | Self::Create { .. } => WorkdirSessionCapability::Write,
|
||||
}
|
||||
@@ -86,6 +90,7 @@ impl CheckoutOperation {
|
||||
)]
|
||||
pub enum CheckoutOutput {
|
||||
Read(ReadResult),
|
||||
ReadBytes(crate::ReadBytesResult),
|
||||
Write(WriteResult),
|
||||
Edit(EditResult),
|
||||
}
|
||||
|
||||
@@ -186,6 +186,11 @@ fn validate_external_operation(operation: &WorkdirSessionOperation) -> Result<()
|
||||
&& !request.validator.is_empty()
|
||||
&& request.validator.len() <= 256
|
||||
&& match &request.operation {
|
||||
crate::CheckoutOperation::ReadBytes { offset, max_bytes } => {
|
||||
*offset <= 9_007_199_254_740_991
|
||||
&& *max_bytes > 0
|
||||
&& *max_bytes <= BoundedReadLimits::EXTERNAL_DEFAULT.max_response_bytes
|
||||
}
|
||||
crate::CheckoutOperation::Read {
|
||||
limit, max_bytes, ..
|
||||
} => {
|
||||
@@ -375,6 +380,13 @@ fn validate_external_result(result: &WorkdirSessionOperationResult) -> Result<()
|
||||
WorkdirSessionOperationResult::CheckoutExecute(result) => {
|
||||
valid_checkout_observation(&result.observation)
|
||||
&& match &result.output {
|
||||
crate::CheckoutOutput::ReadBytes(read) => {
|
||||
read.path == result.observation.path
|
||||
&& validate_external_result(&WorkdirSessionOperationResult::ReadBytes(
|
||||
read.clone(),
|
||||
))
|
||||
.is_ok()
|
||||
}
|
||||
crate::CheckoutOutput::Read(read) => {
|
||||
read.path == result.observation.path
|
||||
&& validate_external_result(&WorkdirSessionOperationResult::Read(
|
||||
|
||||
@@ -693,6 +693,12 @@ mod client {
|
||||
let mutation = request.operation.capability() != crate::WorkdirSessionCapability::Read;
|
||||
let expected_path = request.target.clone();
|
||||
let expected_output = request.operation.capability();
|
||||
let byte_range = match &request.operation {
|
||||
crate::CheckoutOperation::ReadBytes { offset, max_bytes } => {
|
||||
Some((*offset, *max_bytes))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
match self
|
||||
.operate(WorkdirSessionOperation::CheckoutExecute(request))
|
||||
.await
|
||||
@@ -702,11 +708,30 @@ mod client {
|
||||
&& !result.observation.validator.is_empty()
|
||||
&& result.observation.validator.len() <= 256
|
||||
&& match (&result.output, expected_output) {
|
||||
(
|
||||
crate::CheckoutOutput::ReadBytes(read),
|
||||
crate::WorkdirSessionCapability::Read,
|
||||
) => byte_range.is_some_and(|(offset, max)| {
|
||||
read.path == expected_path
|
||||
&& read.offset == offset
|
||||
&& read.bytes.len() <= max
|
||||
&& read
|
||||
.offset
|
||||
.checked_add(read.bytes.len() as u64)
|
||||
.is_some_and(|end| {
|
||||
end <= read.total_bytes
|
||||
|| (read.bytes.is_empty() && read.eof)
|
||||
})
|
||||
&& read.eof
|
||||
== (read.offset.saturating_add(read.bytes.len() as u64)
|
||||
>= read.total_bytes)
|
||||
}),
|
||||
(
|
||||
crate::CheckoutOutput::Read(read),
|
||||
crate::WorkdirSessionCapability::Read,
|
||||
) => {
|
||||
read.path == expected_path
|
||||
byte_range.is_none()
|
||||
&& read.path == expected_path
|
||||
&& read.bytes.len()
|
||||
<= crate::BoundedReadLimits::EXTERNAL_DEFAULT
|
||||
.max_response_bytes
|
||||
|
||||
@@ -1550,6 +1550,20 @@ impl WorkdirSession for LocalWorkdirSession {
|
||||
}
|
||||
let is_create = matches!(&request.operation, Op::Create { .. });
|
||||
let output = match request.operation {
|
||||
Op::ReadBytes { offset, max_bytes } => fs_operation::run_read_bytes_bounded(
|
||||
&self.inner.root,
|
||||
ReadBytesRequest {
|
||||
path: path.clone(),
|
||||
offset,
|
||||
max_bytes,
|
||||
expected_hash: None,
|
||||
},
|
||||
&pinned,
|
||||
self.inner
|
||||
.read_limits
|
||||
.unwrap_or(BoundedReadLimits::EXTERNAL_DEFAULT),
|
||||
)
|
||||
.map(Output::ReadBytes),
|
||||
Op::Read {
|
||||
offset,
|
||||
limit,
|
||||
@@ -1642,7 +1656,7 @@ impl WorkdirSession for LocalWorkdirSession {
|
||||
checkout_execute_error(&path, WorkdirError::from(error))
|
||||
}
|
||||
})?;
|
||||
if matches!(&output, Output::Read(_)) {
|
||||
if matches!(&output, Output::Read(_) | Output::ReadBytes(_)) {
|
||||
pinned.confirm().map_err(|e| checkout_io(&path, e))?;
|
||||
return Ok(crate::CheckoutResult {
|
||||
observation: self
|
||||
|
||||
@@ -1260,6 +1260,9 @@ impl WorkdirSession for ScopedWorkdirSession {
|
||||
.intersection(WorkdirSessionCapabilities::READ_ONLY);
|
||||
}
|
||||
if let crate::CheckoutOutput::Read(read) = &mut result.output {
|
||||
read.path = result_path.clone();
|
||||
}
|
||||
if let crate::CheckoutOutput::ReadBytes(read) = &mut result.output {
|
||||
read.path = result_path;
|
||||
}
|
||||
Ok(result)
|
||||
|
||||
Reference in New Issue
Block a user