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"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deku",
|
"deku",
|
||||||
|
"libc",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user