Add optional owner to node creation from data
This commit is contained in:
parent
405ffd194f
commit
4f44290a48
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -548,6 +548,7 @@ name = "tar"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"deku",
|
||||
"libc",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -6,4 +6,5 @@ license = "GPL-3.0-only"
|
||||
|
||||
[dependencies]
|
||||
deku = "0.16"
|
||||
libc = "0.2"
|
||||
thiserror = "1.0.40"
|
||||
|
@ -1,7 +1,4 @@
|
||||
use std::io;
|
||||
use std::num::ParseIntError;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
use std::{fmt, io, num::ParseIntError, str::Utf8Error};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@ -20,4 +17,6 @@ pub enum Error {
|
||||
InvalidMagic,
|
||||
#[error("Invalid Checksum")]
|
||||
InvalidChecksum,
|
||||
#[error("Parse int failed")]
|
||||
Parse(#[from] fmt::Error),
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use crate::Error;
|
||||
use deku::prelude::*;
|
||||
use std::{
|
||||
env,
|
||||
ffi::CStr,
|
||||
fmt::{self, Write},
|
||||
fs::{self, Metadata},
|
||||
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)]
|
||||
#[deku(endian = "little")]
|
||||
pub struct Header {
|
||||
@ -107,6 +126,82 @@ impl Header {
|
||||
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> {
|
||||
let mut header = Header::default();
|
||||
let meta = fs::symlink_metadata(filename)?;
|
||||
@ -149,14 +244,23 @@ impl 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();
|
||||
header.fname[..filename.len()].copy_from_slice(filename.as_bytes());
|
||||
let mode = format!("{:07o}", meta.st_mode());
|
||||
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());
|
||||
let gid = format!("{:07o}", 0);
|
||||
let gid = format!("{:07o}", owner.gid);
|
||||
header.gid[..gid.len()].copy_from_slice(gid.as_bytes());
|
||||
let size = format!("{:011o}", meta.len());
|
||||
header.size[..size.len()].copy_from_slice(size.as_bytes());
|
||||
@ -172,10 +276,9 @@ impl Header {
|
||||
let minor = format!("{:07o}", meta.st_rdev());
|
||||
header.device_minor[..minor.len()].copy_from_slice(minor.as_bytes());
|
||||
}
|
||||
let user = "root";
|
||||
header.username[..user.len()].copy_from_slice(user.as_bytes());
|
||||
header.username[..owner.username.len()].copy_from_slice(owner.username.as_bytes());
|
||||
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()?;
|
||||
Ok(header)
|
||||
}
|
||||
@ -244,3 +347,22 @@ impl Header {
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
use std::{fs::File, io::{self, BufReader}};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufReader},
|
||||
};
|
||||
|
||||
mod error;
|
||||
mod header;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::{Error, FileType, Header};
|
||||
use crate::{Error, FileType, Header, header::Owner};
|
||||
use deku::prelude::*;
|
||||
use std::{
|
||||
fs::{File, Metadata},
|
||||
io::BufReader,
|
||||
io::{self, BufReader},
|
||||
str,
|
||||
};
|
||||
|
||||
@ -14,7 +14,7 @@ pub struct Node {
|
||||
|
||||
impl Node {
|
||||
/// 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()?)?;
|
||||
let mut written = 512;
|
||||
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.
|
||||
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];
|
||||
input.read_exact(&mut h)?;
|
||||
|
||||
@ -67,8 +67,8 @@ impl Node {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
let header = Header::new_from_meta(filename, meta)?;
|
||||
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, owner)?;
|
||||
if header.link_indicator[0] != FileType::Normal as u8 {
|
||||
return Ok(Node {
|
||||
header,
|
||||
|
Loading…
Reference in New Issue
Block a user