mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-08 14:23:13 +00:00
fix(ecstore): enforce the NAME_MAX segment budget on the write path too (#5826)
#5804 added the on-disk segment budget to check_bucket_and_object_names, but PUT validates through check_put_object_args, which has its own checks and never calls it. An over-NAME_MAX key therefore still reached the disk layer and came back to the client as ENAMETOOLONG → InternalError 500, exactly the behavior #5785 reported.
Caught by re-running the acceptance suite against the locked build 4b2d79f5d, which contains #5804: S3-003 still failed with a 512-byte key.
Multipart is unaffected — check_new_multipart_args and check_multipart_object_args both route through check_object_args → check_bucket_and_object_names, which already carries the budget.
Verification: new test pins the same boundaries on check_put_object_args (255 ok / 256 rejected, byte-based via CJK, multi-segment long keys ok, __XLDIR__ budget for directory keys); cargo test -p rustfs-ecstore --lib -- bucket::utils 17 passed; cargo clippy -p rustfs-ecstore --all-targets clean; make pre-commit green.
This commit is contained in:
@@ -409,6 +409,14 @@ pub fn check_put_object_args(bucket: &str, object: &str) -> Result<()> {
|
||||
return Err(StorageError::ObjectNameInvalid(bucket.to_string(), object.to_string()));
|
||||
}
|
||||
|
||||
// The write path validates arguments here rather than through
|
||||
// check_bucket_and_object_names, so the on-disk segment budget has to be
|
||||
// enforced in both places or an over-NAME_MAX key still reaches the disk
|
||||
// layer and escapes as ENAMETOOLONG → InternalError 500 (rustfs#5785).
|
||||
if !object_key_segments_fit_on_disk(object) {
|
||||
return Err(StorageError::ObjectNameInvalid(bucket.to_string(), object.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -448,6 +456,31 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
/// rustfs#5785 follow-up: the write path validates through
|
||||
/// check_put_object_args, not check_bucket_and_object_names, so the same
|
||||
/// budget has to hold there — otherwise an over-NAME_MAX PUT still reached
|
||||
/// the disk layer and came back as InternalError 500.
|
||||
#[test]
|
||||
fn put_object_args_enforce_the_same_segment_budget() {
|
||||
assert!(check_put_object_args("bucket", &"a".repeat(255)).is_ok());
|
||||
assert!(matches!(
|
||||
check_put_object_args("bucket", &"a".repeat(256)),
|
||||
Err(StorageError::ObjectNameInvalid(_, _))
|
||||
));
|
||||
assert!(matches!(
|
||||
check_put_object_args("bucket", &"\u{4e2d}".repeat(100)),
|
||||
Err(StorageError::ObjectNameInvalid(_, _))
|
||||
));
|
||||
let segmented = ["b".repeat(200), "c".repeat(200), "d".repeat(200)].join("/");
|
||||
assert!(check_put_object_args("bucket", &segmented).is_ok());
|
||||
let dir_budget = 255 - rustfs_utils::path::GLOBAL_DIR_SUFFIX.len();
|
||||
assert!(check_put_object_args("bucket", &format!("{}/", "e".repeat(dir_budget))).is_ok());
|
||||
assert!(matches!(
|
||||
check_put_object_args("bucket", &format!("{}/", "e".repeat(dir_budget + 1))),
|
||||
Err(StorageError::ObjectNameInvalid(_, _))
|
||||
));
|
||||
}
|
||||
|
||||
// Test validation functions
|
||||
#[test]
|
||||
fn test_is_valid_object_name() {
|
||||
|
||||
Reference in New Issue
Block a user