Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read a C struct from a binary file in Rust? [duplicate]

I have read How to read a struct from file in Rust?, but std::slice::raw::mut_buf_as_slice has been deprecated, hence I want to re-ask this question.

I want to read a struct utmp from /var/run/utmp, and I have tried the following code:

fn read_utmp() -> Utmp {
    let mut reader = BufReader::new(File::open("/var/run/utmp").unwrap());
    let mut ut = vec![];
    let size = mem::size_of::<Utmp>();
    reader.take(size as u64).read_to_end(&mut ut);
    unsafe {
        std::mem::transmute(ut)
    }
}

And as expected, the compiler complaint:

error: transmute called with differently sized types: std::vec::Vec (192 bits) to Utmp (3056 bits) [E0512]

How could I do this?

like image 913
knight42 Avatar asked Jul 12 '16 17:07

knight42


People also ask

Is it safe to read a binary file in rust?

Beware this is not a foolproof process, as if the file is changed, you can could get a SIGBUS error. Reading a binary file is not really different from an ASCII file. But you should be aware of any endianess issues, and use the byteorder crate, although not really related to the Rust read methods per se.

How do I read ASCII files in rust?

Basically, there're 3 ways of reading ASCII files in Rust, and an additional possibly more harmful. 1.loading the entire file in a String. This is done using the std::fs::read_to_string () method. If you're familiar with Python or Ruby, this method is as convenient as Python's read () function or Ruby's File.read () methods.

How do you write to a struct in C?

Prerequisite: Structure in C For writing in file, it is easy to write string or int to file using fprintf and putc, but you might have faced difficulty when writing contents of struct. fwrite and fread make task easier when you want to write and read blocks of data. fwrite : Following is the declaration of fwrite function

How to read a binary file in C?

This article will demonstrate multiple methods of how to read a binary file in C. fread is part of the C standard library input/output facilities, and it can be utilized to read binary data from regular files. The C standard library implements a user-buffered I/O along with a platform-independent solution to handle reading/writing binary file data.


1 Answers

I believe that raw::mut_buf_as_slice was replaced with slice::from_raw_parts_mut.


Note that the following code does not take into account any endianness or padding issues and is intended to be used with POD types. struct utmp should be safe in this case.


Here is a function that can read a struct (of a pod type) from a file:

use std::io::{self, BufReader, Read};
use std::fs::{self, File};
use std::path::Path;
use std::slice;

fn read_struct<T, R: Read>(mut read: R) -> io::Result<T> {
    let num_bytes = ::std::mem::size_of::<T>();
    unsafe {
        let mut s = ::std::mem::uninitialized();
        let mut buffer = slice::from_raw_parts_mut(&mut s as *mut T as *mut u8, num_bytes);
        match read.read_exact(buffer) {
            Ok(()) => Ok(s),
            Err(e) => {
                ::std::mem::forget(s);
                Err(e)
            }
        }
    }
}

// use
// read_struct::<Utmp>(reader)

If you want to read all utmp structs from the utmp file, you can execute read_struct multiple times or read all the file at once:

fn read_structs<T, P: AsRef<Path>>(path: P) -> io::Result<Vec<T>> {
    let path = path.as_ref();
    let struct_size = ::std::mem::size_of::<T>();
    let num_bytes = try!(fs::metadata(path)).len() as usize;
    let num_structs = num_bytes / struct_size;
    let mut reader = BufReader::new(try!(File::open(path)));
    let mut r = Vec::<T>::with_capacity(num_structs);
    unsafe {
        let mut buffer = slice::from_raw_parts_mut(r.as_mut_ptr() as *mut u8, num_bytes);
        try!(reader.read_exact(buffer));
        r.set_len(num_structs);
    }
    Ok(r)
}

// use
// read_structs::<Utmp, _>("/var/run/utmp"))
like image 132
malbarbo Avatar answered Sep 24 '22 07:09

malbarbo