put: check the SFTP close status after uploads
ssh2::File's Drop discards the close-handshake error ('too late to
recover'), so a quota or server-side abort surfacing in the final ACKs
was recorded as a successful upload of a truncated file. Close upload
handles explicitly and propagate the error; also applies to the ssh
context driver's write_file and upload_recursive, which had the same
silent-drop issue.
This commit is contained in:
@@ -288,6 +288,13 @@ impl ContextDriver for SshDriver {
|
|||||||
}
|
}
|
||||||
let mut remote_file = sftp.create(path).map_err(io::Error::other)?;
|
let mut remote_file = sftp.create(path).map_err(io::Error::other)?;
|
||||||
remote_file.write_all(content.as_bytes())?;
|
remote_file.write_all(content.as_bytes())?;
|
||||||
|
// Close explicitly: the `Drop` impl of `ssh2::File` discards a
|
||||||
|
// close-time error ("too late to recover"), silently truncating the
|
||||||
|
// remote file. Writes are unbuffered (`Write::flush` is a no-op), so
|
||||||
|
// no flush is needed before closing.
|
||||||
|
remote_file.close().map_err(|e| {
|
||||||
|
io::Error::other(format!("Failed to close remote file {:?}: {}", path, e))
|
||||||
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,6 +334,18 @@ impl SshDriver {
|
|||||||
io::Error::other(format!("Failed to create remote file {:?}: {}", dest, e))
|
io::Error::other(format!("Failed to create remote file {:?}: {}", dest, e))
|
||||||
})?;
|
})?;
|
||||||
io::copy(&mut file, &mut remote_file)?;
|
io::copy(&mut file, &mut remote_file)?;
|
||||||
|
// Close explicitly: quota-exceeded and similar failures only
|
||||||
|
// surface in the final ACKs and the close handshake, and the
|
||||||
|
// `Drop` impl of `ssh2::File` discards that error ("too late to
|
||||||
|
// recover"), leaving a truncated remote file behind. Writes are
|
||||||
|
// unbuffered (`ssh2::File`'s `Write::flush` is a no-op), so no
|
||||||
|
// flush is needed before closing.
|
||||||
|
remote_file.close().map_err(|e| {
|
||||||
|
io::Error::other(format!(
|
||||||
|
"Failed to close remote file {:?} after upload: {}",
|
||||||
|
dest, e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -433,6 +433,16 @@ pub fn upload_file(
|
|||||||
bar.inc(n as u64);
|
bar.inc(n as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close explicitly: quota-exceeded and similar failures only surface in
|
||||||
|
// the final ACKs and the close handshake, and the `Drop` impl of
|
||||||
|
// `ssh2::File` discards that error ("too late to recover"), recording a
|
||||||
|
// truncated remote file as a successful upload. `ssh2::File::write` is
|
||||||
|
// unbuffered (`Write::flush` is a documented no-op) and `close`
|
||||||
|
// finalizes the pending writes server-side, so no flush is needed.
|
||||||
|
remote_file
|
||||||
|
.close()
|
||||||
|
.map_err(|e| format!("failed to close remote file '{remote}' after upload: {e}"))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user