rust

failed to read whole buffer error using read_exact method of Read trait


Can somebody explain why I'm getting an error like Failed to read from dst file: Error { kind: UnexpectedEof, message: "failed to fill whole buffer" }. Is it because of the corrupted file. If it is, I was expecting the write_to_file method to also fail, since it's creating a copy of the same file with different name. I checked the size of both the files after copy and they both are same.

use std::fs::File;
use std::path::Path;
use std::io::{Error, ErrorKind, Read, Write};
use std::ffi::OsStr;

use tar::{Archive, Entry};
use flate2::read::GzDecoder;

enum ArchiveType {
    Tar,
    TarGz,
}

fn get_archive(archive_type: ArchiveType, archive: &Path) -> Archive<Box<dyn Read>> {
    let file = File::open(archive).expect(&format!("Failed to open archive {:?}", archive));
    match archive_type {
        ArchiveType::Tar => Archive::new(Box::new(file)),
        ArchiveType::TarGz => Archive::new(Box::new(GzDecoder::new(file))),
    }
}

fn get_entry<'a, R: Read>(
    archive: &'a mut Archive<R>,
    entry_name: &'a str,
) -> Result<Entry<'a, R>, Error> {
    let normalized_entry_name = entry_name.trim_start_matches("./");
    for entry in archive.entries()? {
        let entry = entry?;
        // comparison between 'entry_name' and path of 'entry'
        // is done only with file_name() we ignore subfolders.
        // if multiple entries with same name, only first will be found.
        if entry
            .path()
            .as_ref()
            .ok() // Convert Result to Option<Cow<Path>> (ignoring errors)
            .and_then(|path| path.file_name()) // Extract file name (Option<&OsStr>)
            .and_then(OsStr::to_str) // Convert to Option<&str> safely
            .map(|name| {
                println!("{}: {}", name, normalized_entry_name);
                name == normalized_entry_name}) // Compare
            .unwrap_or(false)
        {
            return Ok(entry);
        }
    }
    Err(Error::new(ErrorKind::Other, "failed to get entry from the archive"))
}

fn write_to_file<R: Read, W: Write>(mut reader: R, mut writer: W) {
    let mut buf = vec![0; 1024];
    let mut bytes_read = buf.capacity();
    let mut total = 0;
    while bytes_read > 0 {
        bytes_read = reader.read(&mut buf).expect("failed to read from the file");
        if bytes_read > 0 {
            writer.write_all(&buf[..bytes_read]).expect("failed to write to the file");
            total += bytes_read;
        }
    }
    println!("wrote {} bytes", total);
    writer.flush().expect("failed to flush the writer"); 
}

fn compare<R: Read>(
    mut reader: R,
    read_size: usize,
    dst: &Path,
) {
    let mut dst_file = File::open(dst).expect("failed to open dst file");
    let mut buffer_chunk = vec![0; read_size]; // Buffer to hold data chunks
    let mut dst_block = vec![0; read_size];
    let mut bytes_read = read_size;

    while bytes_read > 0 {
        bytes_read = reader.read(&mut buffer_chunk).expect("failed to read from src file");
        if bytes_read > 0 {
            dst_file.read_exact(&mut dst_block).expect("Failed to read from dst file");
            
            // compare blocks
            if buffer_chunk[..bytes_read] != dst_block[..bytes_read] {
                eprintln!("contents are different");
            }
        }
    }

    println!("same");
}

fn main() {
    let file_name = "disk.img";
    {
        let mut archive = 
        get_archive(ArchiveType::TarGz, Path::new("/home/harry/ty/abc.tar.gz"));
        let mut entry = get_entry(&mut archive, &file_name).expect("error while getting entry");
        let dst_file = &File::create("/home/harry/ty/disk1.img").expect("unable to create the file");
        write_to_file(&mut entry, dst_file);
    }

    {
        let mut archive = 
        get_archive(ArchiveType::TarGz, Path::new("/home/harry/ty/abc.tar.gz"));
        let mut entry = get_entry(&mut archive, &file_name).expect("error while getting entry");
        compare(&mut entry, 512, &Path::new("/home/harry/ty/disk1.img"));
    }
}

Solution

  • You attempt to unconditionally read exactly read_size bytes no matter how many bytes were read from the source. But there is no guarantee at all there are as many bytes there, the source only gave you bytes_read bytes. You probably meant to read dst_file.read_exact(&mut dst_block[..bytes_read]) because that's how many bytes were read from the source.