1 use std::fs::{self, File};
2 use std::io::{self, Write};
3 use std::path::Path;
4 use std::{iter, mem, thread, time};
5 
6 use tempfile::Builder;
7 
8 use tar::{GnuHeader, Header, HeaderMode};
9 
10 #[test]
default_gnu()11 fn default_gnu() {
12     let mut h = Header::new_gnu();
13     assert!(h.as_gnu().is_some());
14     assert!(h.as_gnu_mut().is_some());
15     assert!(h.as_ustar().is_none());
16     assert!(h.as_ustar_mut().is_none());
17 }
18 
19 #[test]
goto_old()20 fn goto_old() {
21     let mut h = Header::new_old();
22     assert!(h.as_gnu().is_none());
23     assert!(h.as_gnu_mut().is_none());
24     assert!(h.as_ustar().is_none());
25     assert!(h.as_ustar_mut().is_none());
26 }
27 
28 #[test]
goto_ustar()29 fn goto_ustar() {
30     let mut h = Header::new_ustar();
31     assert!(h.as_gnu().is_none());
32     assert!(h.as_gnu_mut().is_none());
33     assert!(h.as_ustar().is_some());
34     assert!(h.as_ustar_mut().is_some());
35 }
36 
37 #[test]
link_name()38 fn link_name() {
39     let mut h = Header::new_gnu();
40     t!(h.set_link_name("foo"));
41     assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo"));
42     t!(h.set_link_name("../foo"));
43     assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("../foo"));
44     t!(h.set_link_name("foo/bar"));
45     assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo/bar"));
46     t!(h.set_link_name("foo\\ba"));
47     if cfg!(windows) {
48         assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo/ba"));
49     } else {
50         assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo\\ba"));
51     }
52 
53     let name = "foo\\bar\0";
54     for (slot, val) in h.as_old_mut().linkname.iter_mut().zip(name.as_bytes()) {
55         *slot = *val;
56     }
57     assert_eq!(t!(h.link_name()).unwrap().to_str(), Some("foo\\bar"));
58 
59     assert!(h.set_link_name("\0").is_err());
60 }
61 
62 #[test]
mtime()63 fn mtime() {
64     let h = Header::new_gnu();
65     assert_eq!(t!(h.mtime()), 0);
66 
67     let h = Header::new_ustar();
68     assert_eq!(t!(h.mtime()), 0);
69 
70     let h = Header::new_old();
71     assert_eq!(t!(h.mtime()), 0);
72 }
73 
74 #[test]
user_and_group_name()75 fn user_and_group_name() {
76     let mut h = Header::new_gnu();
77     t!(h.set_username("foo"));
78     t!(h.set_groupname("bar"));
79     assert_eq!(t!(h.username()), Some("foo"));
80     assert_eq!(t!(h.groupname()), Some("bar"));
81 
82     h = Header::new_ustar();
83     t!(h.set_username("foo"));
84     t!(h.set_groupname("bar"));
85     assert_eq!(t!(h.username()), Some("foo"));
86     assert_eq!(t!(h.groupname()), Some("bar"));
87 
88     h = Header::new_old();
89     assert_eq!(t!(h.username()), None);
90     assert_eq!(t!(h.groupname()), None);
91     assert!(h.set_username("foo").is_err());
92     assert!(h.set_groupname("foo").is_err());
93 }
94 
95 #[test]
dev_major_minor()96 fn dev_major_minor() {
97     let mut h = Header::new_gnu();
98     t!(h.set_device_major(1));
99     t!(h.set_device_minor(2));
100     assert_eq!(t!(h.device_major()), Some(1));
101     assert_eq!(t!(h.device_minor()), Some(2));
102 
103     h = Header::new_ustar();
104     t!(h.set_device_major(1));
105     t!(h.set_device_minor(2));
106     assert_eq!(t!(h.device_major()), Some(1));
107     assert_eq!(t!(h.device_minor()), Some(2));
108 
109     h.as_ustar_mut().unwrap().dev_minor[0] = 0x7f;
110     h.as_ustar_mut().unwrap().dev_major[0] = 0x7f;
111     assert!(h.device_major().is_err());
112     assert!(h.device_minor().is_err());
113 
114     h.as_ustar_mut().unwrap().dev_minor[0] = b'g';
115     h.as_ustar_mut().unwrap().dev_major[0] = b'h';
116     assert!(h.device_major().is_err());
117     assert!(h.device_minor().is_err());
118 
119     h = Header::new_old();
120     assert_eq!(t!(h.device_major()), None);
121     assert_eq!(t!(h.device_minor()), None);
122     assert!(h.set_device_major(1).is_err());
123     assert!(h.set_device_minor(1).is_err());
124 }
125 
126 #[test]
set_path()127 fn set_path() {
128     let mut h = Header::new_gnu();
129     t!(h.set_path("foo"));
130     assert_eq!(t!(h.path()).to_str(), Some("foo"));
131     t!(h.set_path("foo/"));
132     assert_eq!(t!(h.path()).to_str(), Some("foo/"));
133     t!(h.set_path("foo/bar"));
134     assert_eq!(t!(h.path()).to_str(), Some("foo/bar"));
135     t!(h.set_path("foo\\bar"));
136     if cfg!(windows) {
137         assert_eq!(t!(h.path()).to_str(), Some("foo/bar"));
138     } else {
139         assert_eq!(t!(h.path()).to_str(), Some("foo\\bar"));
140     }
141 
142     // set_path documentation explictly states it removes any ".", signfying the
143     // current directory, from the path. This test ensures that documented
144     // beavhior occurs
145     t!(h.set_path("./control"));
146     assert_eq!(t!(h.path()).to_str(), Some("control"));
147 
148     let long_name = iter::repeat("foo").take(100).collect::<String>();
149     let medium1 = iter::repeat("foo").take(52).collect::<String>();
150     let medium2 = iter::repeat("fo/").take(52).collect::<String>();
151 
152     assert!(h.set_path(&long_name).is_err());
153     assert!(h.set_path(&medium1).is_err());
154     assert!(h.set_path(&medium2).is_err());
155     assert!(h.set_path("\0").is_err());
156 
157     assert!(h.set_path("..").is_err());
158     assert!(h.set_path("foo/..").is_err());
159     assert!(h.set_path("foo/../bar").is_err());
160 
161     h = Header::new_ustar();
162     t!(h.set_path("foo"));
163     assert_eq!(t!(h.path()).to_str(), Some("foo"));
164 
165     assert!(h.set_path(&long_name).is_err());
166     assert!(h.set_path(&medium1).is_err());
167     t!(h.set_path(&medium2));
168     assert_eq!(t!(h.path()).to_str(), Some(&medium2[..]));
169 }
170 
171 #[test]
set_ustar_path_hard()172 fn set_ustar_path_hard() {
173     let mut h = Header::new_ustar();
174     let p = Path::new("a").join(&vec!["a"; 100].join(""));
175     t!(h.set_path(&p));
176     assert_eq!(t!(h.path()), p);
177 }
178 
179 #[test]
set_metadata_deterministic()180 fn set_metadata_deterministic() {
181     let td = t!(Builder::new().prefix("tar-rs").tempdir());
182     let tmppath = td.path().join("tmpfile");
183 
184     fn mk_header(path: &Path, readonly: bool) -> Result<Header, io::Error> {
185         let mut file = t!(File::create(path));
186         t!(file.write_all(b"c"));
187         let mut perms = t!(file.metadata()).permissions();
188         perms.set_readonly(readonly);
189         t!(fs::set_permissions(path, perms));
190         let mut h = Header::new_ustar();
191         h.set_metadata_in_mode(&t!(path.metadata()), HeaderMode::Deterministic);
192         Ok(h)
193     }
194 
195     // Create "the same" File twice in a row, one second apart, with differing readonly values.
196     let one = t!(mk_header(tmppath.as_path(), false));
197     thread::sleep(time::Duration::from_millis(1050));
198     let two = t!(mk_header(tmppath.as_path(), true));
199 
200     // Always expected to match.
201     assert_eq!(t!(one.size()), t!(two.size()));
202     assert_eq!(t!(one.path()), t!(two.path()));
203     assert_eq!(t!(one.mode()), t!(two.mode()));
204 
205     // Would not match without `Deterministic`.
206     assert_eq!(t!(one.mtime()), t!(two.mtime()));
207     // TODO: No great way to validate that these would not be filled, but
208     // check them anyway.
209     assert_eq!(t!(one.uid()), t!(two.uid()));
210     assert_eq!(t!(one.gid()), t!(two.gid()));
211 }
212 
213 #[test]
extended_numeric_format()214 fn extended_numeric_format() {
215     let mut h: GnuHeader = unsafe { mem::zeroed() };
216     h.as_header_mut().set_size(42);
217     assert_eq!(h.size, [48, 48, 48, 48, 48, 48, 48, 48, 48, 53, 50, 0]);
218     h.as_header_mut().set_size(8589934593);
219     assert_eq!(h.size, [0x80, 0, 0, 0, 0, 0, 0, 0x02, 0, 0, 0, 1]);
220     h.size = [0x80, 0, 0, 0, 0, 0, 0, 0x02, 0, 0, 0, 0];
221     assert_eq!(h.as_header().entry_size().unwrap(), 0x0200000000);
222     h.size = [48, 48, 48, 48, 48, 48, 48, 48, 48, 53, 51, 0];
223     assert_eq!(h.as_header().entry_size().unwrap(), 43);
224 
225     h.as_header_mut().set_gid(42);
226     assert_eq!(h.gid, [48, 48, 48, 48, 48, 53, 50, 0]);
227     assert_eq!(h.as_header().gid().unwrap(), 42);
228     h.as_header_mut().set_gid(0x7fffffffffffffff);
229     assert_eq!(h.gid, [0xff; 8]);
230     assert_eq!(h.as_header().gid().unwrap(), 0x7fffffffffffffff);
231     h.uid = [0x80, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78];
232     assert_eq!(h.as_header().uid().unwrap(), 0x12345678);
233 
234     h.mtime = [
235         0x80, 0, 0, 0, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
236     ];
237     assert_eq!(h.as_header().mtime().unwrap(), 0x0123456789abcdef);
238 }
239 
240 #[test]
byte_slice_conversion()241 fn byte_slice_conversion() {
242     let h = Header::new_gnu();
243     let b: &[u8] = h.as_bytes();
244     let b_conv: &[u8] = Header::from_byte_slice(h.as_bytes()).as_bytes();
245     assert_eq!(b, b_conv);
246 }
247