From 4f44290a480f7404707dc72ae3678bd9c6a610d7 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Thu, 23 Mar 2023 14:09:18 -0400 Subject: [PATCH] Add optional owner to node creation from data --- Cargo.lock | 1 + tar/Cargo.toml | 1 + tar/src/error.rs | 7 ++- tar/src/header.rs | 134 +++++++++++++++++++++++++++++++++++++++++++--- tar/src/lib.rs | 5 +- tar/src/node.rs | 12 ++--- 6 files changed, 143 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb4a555..d5b9a6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,6 +548,7 @@ name = "tar" version = "0.1.0" dependencies = [ "deku", + "libc", "thiserror", ] diff --git a/tar/Cargo.toml b/tar/Cargo.toml index fa6b9f9..e031ebb 100644 --- a/tar/Cargo.toml +++ b/tar/Cargo.toml @@ -6,4 +6,5 @@ license = "GPL-3.0-only" [dependencies] deku = "0.16" +libc = "0.2" thiserror = "1.0.40" diff --git a/tar/src/error.rs b/tar/src/error.rs index 76a8583..52f9f3a 100644 --- a/tar/src/error.rs +++ b/tar/src/error.rs @@ -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), } diff --git a/tar/src/header.rs b/tar/src/header.rs index a74aa43..85b5488 100644 --- a/tar/src/header.rs +++ b/tar/src/header.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { 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 { + pub fn new_from_meta(filename: &str, meta: &Metadata, owner: Option) -> Result { 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() +} + diff --git a/tar/src/lib.rs b/tar/src/lib.rs index fea90c7..82ea03a 100644 --- a/tar/src/lib.rs +++ b/tar/src/lib.rs @@ -1,4 +1,7 @@ -use std::{fs::File, io::{self, BufReader}}; +use std::{ + fs::File, + io::{self, BufReader}, +}; mod error; mod header; diff --git a/tar/src/node.rs b/tar/src/node.rs index 06397fa..f53d7cd 100644 --- a/tar/src/node.rs +++ b/tar/src/node.rs @@ -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(self, mut input: T) -> Result { + pub fn write(self, mut input: T) -> Result { 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(mut input: T) -> Result { + pub fn read(mut input: T) -> Result { 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 { - let header = Header::new_from_meta(filename, meta)?; + pub fn read_data_to_tar(data: &[u8], filename: &str, meta: &Metadata, owner: Option) -> Result { + let header = Header::new_from_meta(filename, meta, owner)?; if header.link_indicator[0] != FileType::Normal as u8 { return Ok(Node { header,