Add optional owner to node creation from data

This commit is contained in:
Nathan Fisher 2023-03-23 14:09:18 -04:00
parent 405ffd194f
commit 4f44290a48
6 changed files with 143 additions and 17 deletions

1
Cargo.lock generated
View File

@ -548,6 +548,7 @@ name = "tar"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"deku", "deku",
"libc",
"thiserror", "thiserror",
] ]

View File

@ -6,4 +6,5 @@ license = "GPL-3.0-only"
[dependencies] [dependencies]
deku = "0.16" deku = "0.16"
libc = "0.2"
thiserror = "1.0.40" thiserror = "1.0.40"

View File

@ -1,7 +1,4 @@
use std::io; use std::{fmt, io, num::ParseIntError, str::Utf8Error};
use std::num::ParseIntError;
use std::str::Utf8Error;
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -20,4 +17,6 @@ pub enum Error {
InvalidMagic, InvalidMagic,
#[error("Invalid Checksum")] #[error("Invalid Checksum")]
InvalidChecksum, InvalidChecksum,
#[error("Parse int failed")]
Parse(#[from] fmt::Error),
} }

View File

@ -2,6 +2,7 @@ use crate::Error;
use deku::prelude::*; use deku::prelude::*;
use std::{ use std::{
env, env,
ffi::CStr,
fmt::{self, Write}, fmt::{self, Write},
fs::{self, Metadata}, fs::{self, Metadata},
ops::Deref, ops::Deref,
@ -48,6 +49,24 @@ where
} }
} }
pub struct Owner {
pub uid: u32,
pub gid: u32,
pub username: String,
pub groupname: String,
}
impl Default for Owner {
fn default() -> Self {
Self {
uid: 0,
gid: 0,
username: "root".into(),
groupname: "root".into(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, DekuRead, DekuWrite)] #[derive(Clone, Copy, Debug, PartialEq, DekuRead, DekuWrite)]
#[deku(endian = "little")] #[deku(endian = "little")]
pub struct Header { pub struct Header {
@ -107,6 +126,82 @@ impl Header {
Ok(s) Ok(s)
} }
pub fn mode(&self) -> Result<u32, Error> {
let mut s = String::new();
for c in self.mode {
if c != b'\0' {
write!(s, "{c}")?;
} else {
break;
}
}
let mode = u32::from_str_radix(&s, 8)?;
Ok(mode)
}
fn uid(&self) -> Result<u32, Error> {
let mut s = String::new();
for c in self.mode {
if c != b'\0' {
write!(s, "{c}")?;
} else {
break;
}
}
let uid = u32::from_str_radix(&s, 8)?;
Ok(uid)
}
fn gid(&self) -> Result<u32, Error> {
let mut s = String::new();
for c in self.mode {
if c != b'\0' {
write!(s, "{c}")?;
} else {
break;
}
}
let gid = u32::from_str_radix(&s, 8)?;
Ok(gid)
}
fn username(&self) -> Result<String, fmt::Error> {
let mut s = String::new();
for c in self.username {
if c != b'\0' {
write!(s, "{c}")?;
} else {
break;
}
}
Ok(s)
}
fn groupname(&self) -> Result<String, fmt::Error> {
let mut s = String::new();
for c in self.groupname {
if c != b'\0' {
write!(s, "{c}")?;
} else {
break;
}
}
Ok(s)
}
pub fn owner(&self) -> Result<Owner, Error> {
let uid = self.uid()?;
let gid = self.gid()?;
let username = self.username()?;
let groupname = self.groupname()?;
Ok(Owner {
uid,
gid,
username,
groupname,
})
}
pub fn new(filename: &str) -> Result<Self, Error> { pub fn new(filename: &str) -> Result<Self, Error> {
let mut header = Header::default(); let mut header = Header::default();
let meta = fs::symlink_metadata(filename)?; let meta = fs::symlink_metadata(filename)?;
@ -149,14 +244,23 @@ impl Header {
Ok(header) Ok(header)
} }
pub fn new_from_meta(filename: &str, meta: &Metadata) -> Result<Self, Error> { pub fn new_from_meta(filename: &str, meta: &Metadata, owner: Option<Owner>) -> Result<Self, Error> {
let mut header = Header::default(); let mut header = Header::default();
header.fname[..filename.len()].copy_from_slice(filename.as_bytes()); header.fname[..filename.len()].copy_from_slice(filename.as_bytes());
let mode = format!("{:07o}", meta.st_mode()); let mode = format!("{:07o}", meta.st_mode());
header.mode[..mode.len()].copy_from_slice(mode.as_bytes()); header.mode[..mode.len()].copy_from_slice(mode.as_bytes());
let uid = format!("{:07o}", 0); let owner = match owner {
Some(o) => o,
None => Owner {
uid: meta.st_uid(),
gid: meta.st_gid(),
username: get_username_for_uid(meta.st_uid())?.into(),
groupname: get_groupname_for_gid(meta.st_gid())?.into(),
}
};
let uid = format!("{:07o}", owner.uid);
header.uid[..uid.len()].copy_from_slice(uid.as_bytes()); header.uid[..uid.len()].copy_from_slice(uid.as_bytes());
let gid = format!("{:07o}", 0); let gid = format!("{:07o}", owner.gid);
header.gid[..gid.len()].copy_from_slice(gid.as_bytes()); header.gid[..gid.len()].copy_from_slice(gid.as_bytes());
let size = format!("{:011o}", meta.len()); let size = format!("{:011o}", meta.len());
header.size[..size.len()].copy_from_slice(size.as_bytes()); header.size[..size.len()].copy_from_slice(size.as_bytes());
@ -172,10 +276,9 @@ impl Header {
let minor = format!("{:07o}", meta.st_rdev()); let minor = format!("{:07o}", meta.st_rdev());
header.device_minor[..minor.len()].copy_from_slice(minor.as_bytes()); header.device_minor[..minor.len()].copy_from_slice(minor.as_bytes());
} }
let user = "root"; header.username[..owner.username.len()].copy_from_slice(owner.username.as_bytes());
header.username[..user.len()].copy_from_slice(user.as_bytes());
let group = "root"; let group = "root";
header.groupname[..group.len()].copy_from_slice(group.as_bytes()); header.groupname[..group.len()].copy_from_slice(owner.groupname.as_bytes());
header.update_checksum()?; header.update_checksum()?;
Ok(header) Ok(header)
} }
@ -244,3 +347,22 @@ impl Header {
Ok(checksum) Ok(checksum)
} }
} }
fn get_username_for_uid<'a>(uid: u32) -> Result<&'a str, std::str::Utf8Error> {
let user = unsafe {
let pw = libc::getpwuid(uid);
let name = (*pw).pw_name;
CStr::from_ptr(name)
};
user.to_str()
}
pub fn get_groupname_for_gid<'a>(gid: u32) -> Result<&'a str, std::str::Utf8Error> {
let group = unsafe {
let gr = libc::getgrgid(gid);
let name = (*gr).gr_name;
CStr::from_ptr(name)
};
group.to_str()
}

View File

@ -1,4 +1,7 @@
use std::{fs::File, io::{self, BufReader}}; use std::{
fs::File,
io::{self, BufReader},
};
mod error; mod error;
mod header; mod header;

View File

@ -1,8 +1,8 @@
use crate::{Error, FileType, Header}; use crate::{Error, FileType, Header, header::Owner};
use deku::prelude::*; use deku::prelude::*;
use std::{ use std::{
fs::{File, Metadata}, fs::{File, Metadata},
io::BufReader, io::{self, BufReader},
str, str,
}; };
@ -14,7 +14,7 @@ pub struct Node {
impl Node { impl Node {
/// Write out a single file within the tar to a file or something with a ``std::io::Write`` trait. /// Write out a single file within the tar to a file or something with a ``std::io::Write`` trait.
pub fn write<T: std::io::Write>(self, mut input: T) -> Result<usize, Error> { pub fn write<T: io::Write>(self, mut input: T) -> Result<usize, Error> {
input.write_all(&self.header.to_bytes()?)?; input.write_all(&self.header.to_bytes()?)?;
let mut written = 512; let mut written = 512;
for d in self.data { for d in self.data {
@ -26,7 +26,7 @@ impl Node {
} }
/// Read a TarNode in from a file or something with a ``std::io::Read`` trait. /// Read a TarNode in from a file or something with a ``std::io::Read`` trait.
pub fn read<T: std::io::Read>(mut input: T) -> Result<Node, Error> { pub fn read<T: io::Read>(mut input: T) -> Result<Node, Error> {
let mut h = vec![0u8; 512]; let mut h = vec![0u8; 512];
input.read_exact(&mut h)?; input.read_exact(&mut h)?;
@ -67,8 +67,8 @@ impl Node {
} }
/// Create a Node from in memory data, given the filename and metadata /// Create a Node from in memory data, given the filename and metadata
pub fn read_data_to_tar(data: &[u8], filename: &str, meta: &Metadata) -> Result<Node, Error> { pub fn read_data_to_tar(data: &[u8], filename: &str, meta: &Metadata, owner: Option<Owner>) -> Result<Node, Error> {
let header = Header::new_from_meta(filename, meta)?; let header = Header::new_from_meta(filename, meta, owner)?;
if header.link_indicator[0] != FileType::Normal as u8 { if header.link_indicator[0] != FileType::Normal as u8 {
return Ok(Node { return Ok(Node {
header, header,