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