1 //! A library for encoding/decoding Unix archive files. 2 //! 3 //! This library provides utilities necessary to manage [Unix archive 4 //! files](https://en.wikipedia.org/wiki/Ar_(Unix)) (as generated by the 5 //! standard `ar` command line utility) abstracted over a reader or writer. 6 //! This library provides a streaming interface that avoids having to ever load 7 //! a full archive entry into memory. 8 //! 9 //! The API of this crate is meant to be similar to that of the 10 //! [`tar`](https://crates.io/crates/tar) crate. 11 //! 12 //! # Format variants 13 //! 14 //! Unix archive files come in several variants, of which three are the most 15 //! common: 16 //! 17 //! * The *common variant*, used for Debian package (`.deb`) files among other 18 //! things, which only supports filenames up to 16 characters. 19 //! * The *BSD variant*, used by the `ar` utility on BSD systems (including Mac 20 //! OS X), which is backwards-compatible with the common variant, but extends 21 //! it to support longer filenames and filenames containing spaces. 22 //! * The *GNU variant*, used by the `ar` utility on GNU and many other systems 23 //! (including Windows), which is similar to the common format, but which 24 //! stores filenames in a slightly different, incompatible way, and has its 25 //! own strategy for supporting long filenames. 26 //! 27 //! This crate supports reading and writing all three of these variants. 28 //! 29 //! # Example usage 30 //! 31 //! Writing an archive: 32 //! 33 //! ```no_run 34 //! use ar::Builder; 35 //! use std::collections::BTreeMap; 36 //! use std::fs::File; 37 //! // Create a new archive that will be written to foo.a: 38 //! let mut builder = Builder::new(File::create("foo.a").unwrap(), BTreeMap::new()).unwrap(); 39 //! // Add foo/bar.txt to the archive, under the name "bar.txt": 40 //! builder.append_path("foo/bar.txt").unwrap(); 41 //! // Add foo/baz.txt to the archive, under the name "hello.txt": 42 //! let mut file = File::open("foo/baz.txt").unwrap(); 43 //! builder.append_file(b"hello.txt", &mut file).unwrap(); 44 //! ``` 45 //! 46 //! Reading an archive: 47 //! 48 //! ```no_run 49 //! use ar::Archive; 50 //! use std::fs::File; 51 //! use std::io; 52 //! use std::str; 53 //! // Read an archive from the file foo.a: 54 //! let mut archive = Archive::new(File::open("foo.a").unwrap()); 55 //! // Iterate over all entries in the archive: 56 //! while let Some(entry_result) = archive.next_entry() { 57 //! let mut entry = entry_result.unwrap(); 58 //! // Create a new file with the same name as the archive entry: 59 //! let mut file = File::create( 60 //! str::from_utf8(entry.header().identifier()).unwrap(), 61 //! ).unwrap(); 62 //! // The Entry object also acts as an io::Read, so we can easily copy the 63 //! // contents of the archive entry into the file: 64 //! io::copy(&mut entry, &mut file).unwrap(); 65 //! } 66 //! ``` 67 68 #![warn(missing_docs)] 69 70 mod read; 71 mod write; 72 73 pub use read::{Archive, Entry, SymbolTableEntry, Symbols}; 74 pub use write::{Builder, GnuBuilder, GnuSymbolTableFormat}; 75 76 use std::fs::Metadata; 77 78 #[cfg(unix)] 79 use std::os::unix::fs::MetadataExt; 80 81 // ========================================================================= // 82 83 const GLOBAL_HEADER_LEN: usize = 8; 84 const GLOBAL_HEADER: &'static [u8; GLOBAL_HEADER_LEN] = b"!<arch>\n"; 85 86 const ENTRY_HEADER_LEN: usize = 60; 87 88 const BSD_SYMBOL_LOOKUP_TABLE_ID: &str = "__.SYMDEF"; 89 const BSD_SORTED_SYMBOL_LOOKUP_TABLE_ID: &str = "__.SYMDEF SORTED"; 90 91 const GNU_NAME_TABLE_ID: &str = "//"; 92 const GNU_SYMBOL_LOOKUP_TABLE_ID: &str = "/"; 93 const GNU_SYMBOL_LOOKUP_TABLE_64BIT_ID: &str = "/SYM64"; 94 95 // ========================================================================= // 96 97 /// Variants of the Unix archive format. 98 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 99 pub enum Variant { 100 /// Used by Debian package files; allows only short filenames. 101 Common, 102 /// Used by BSD `ar` (and OS X); backwards-compatible with common variant. 103 BSD, 104 /// Used by GNU `ar` (and Windows); incompatible with common variant. 105 GNU, 106 } 107 108 /// Variants of the symbol table if present 109 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 110 pub enum SymbolTableVariant { 111 /// Used by BSD Archives the more "classical" unix symbol table 112 BSD, 113 /// Used in GNU, SVR4 and others 114 GNU, 115 /// The extended format used when an archive becomes larger than 4gb 116 GNU64BIT, 117 } 118 119 // ========================================================================= // 120 121 /// Representation of an archive entry header. 122 #[derive(Clone, Debug, Eq, PartialEq)] 123 pub struct Header { 124 identifier: Vec<u8>, 125 mtime: u64, 126 uid: u32, 127 gid: u32, 128 mode: u32, 129 size: u64, 130 } 131 132 impl Header { 133 /// Creates a header with the given file identifier and size, and all 134 /// other fields set to zero. new(identifier: Vec<u8>, size: u64) -> Header135 pub fn new(identifier: Vec<u8>, size: u64) -> Header { 136 Header { 137 identifier, 138 mtime: 0, 139 uid: 0, 140 gid: 0, 141 mode: 0, 142 size, 143 } 144 } 145 146 /// Creates a header with the given file identifier and all other fields 147 /// set from the given filesystem metadata. 148 #[cfg(unix)] from_metadata(identifier: Vec<u8>, meta: &Metadata) -> Header149 pub fn from_metadata(identifier: Vec<u8>, meta: &Metadata) -> Header { 150 Header { 151 identifier, 152 mtime: meta.mtime() as u64, 153 uid: meta.uid(), 154 gid: meta.gid(), 155 mode: meta.mode(), 156 size: meta.len(), 157 } 158 } 159 160 #[cfg(not(unix))] from_metadata(identifier: Vec<u8>, meta: &Metadata) -> Header161 pub fn from_metadata(identifier: Vec<u8>, meta: &Metadata) -> Header { 162 Header::new(identifier, meta.len()) 163 } 164 165 /// Returns the file identifier. identifier(&self) -> &[u8]166 pub fn identifier(&self) -> &[u8] { &self.identifier } 167 168 /// Sets the file identifier. set_identifier(&mut self, identifier: Vec<u8>)169 pub fn set_identifier(&mut self, identifier: Vec<u8>) { 170 self.identifier = identifier; 171 } 172 173 /// Returns the last modification time in Unix time format. mtime(&self) -> u64174 pub fn mtime(&self) -> u64 { self.mtime } 175 176 /// Sets the last modification time in Unix time format. set_mtime(&mut self, mtime: u64)177 pub fn set_mtime(&mut self, mtime: u64) { self.mtime = mtime; } 178 179 /// Returns the value of the owner's user ID field. uid(&self) -> u32180 pub fn uid(&self) -> u32 { self.uid } 181 182 /// Sets the value of the owner's user ID field. set_uid(&mut self, uid: u32)183 pub fn set_uid(&mut self, uid: u32) { self.uid = uid; } 184 185 /// Returns the value of the group's user ID field. gid(&self) -> u32186 pub fn gid(&self) -> u32 { self.gid } 187 188 /// Returns the value of the group's user ID field. set_gid(&mut self, gid: u32)189 pub fn set_gid(&mut self, gid: u32) { self.gid = gid; } 190 191 /// Returns the mode bits for this file. mode(&self) -> u32192 pub fn mode(&self) -> u32 { self.mode } 193 194 /// Sets the mode bits for this file. set_mode(&mut self, mode: u32)195 pub fn set_mode(&mut self, mode: u32) { self.mode = mode; } 196 197 /// Returns the length of the file, in bytes. size(&self) -> u64198 pub fn size(&self) -> u64 { self.size } 199 200 /// Sets the length of the file, in bytes. set_size(&mut self, size: u64)201 pub fn set_size(&mut self, size: u64) { self.size = size; } 202 } 203 // ========================================================================= // 204