xref: /aosp_15_r20/external/crosvm/ext2/tests/tests.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2024 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker #![cfg(target_os = "linux")]
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::BTreeMap;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::BTreeSet;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::fs;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::create_dir;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::read_link;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::symlink_metadata;
13*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File;
14*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::OpenOptions;
15*bb4ee6a4SAndroid Build Coastguard Worker use std::io::BufWriter;
16*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Seek;
17*bb4ee6a4SAndroid Build Coastguard Worker use std::io::SeekFrom;
18*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Write;
19*bb4ee6a4SAndroid Build Coastguard Worker use std::os::unix::fs::symlink;
20*bb4ee6a4SAndroid Build Coastguard Worker use std::path::Path;
21*bb4ee6a4SAndroid Build Coastguard Worker use std::path::PathBuf;
22*bb4ee6a4SAndroid Build Coastguard Worker use std::process::Command;
23*bb4ee6a4SAndroid Build Coastguard Worker 
24*bb4ee6a4SAndroid Build Coastguard Worker use base::MappedRegion;
25*bb4ee6a4SAndroid Build Coastguard Worker use ext2::Builder;
26*bb4ee6a4SAndroid Build Coastguard Worker use tempfile::tempdir;
27*bb4ee6a4SAndroid Build Coastguard Worker use tempfile::tempdir_in;
28*bb4ee6a4SAndroid Build Coastguard Worker use tempfile::TempDir;
29*bb4ee6a4SAndroid Build Coastguard Worker use walkdir::WalkDir;
30*bb4ee6a4SAndroid Build Coastguard Worker 
31*bb4ee6a4SAndroid Build Coastguard Worker const FSCK_PATH: &str = "/usr/sbin/e2fsck";
32*bb4ee6a4SAndroid Build Coastguard Worker const DEBUGFS_PATH: &str = "/usr/sbin/debugfs";
33*bb4ee6a4SAndroid Build Coastguard Worker 
34*bb4ee6a4SAndroid Build Coastguard Worker const BLOCK_SIZE: u32 = 4096;
35*bb4ee6a4SAndroid Build Coastguard Worker 
run_fsck(path: &PathBuf)36*bb4ee6a4SAndroid Build Coastguard Worker fn run_fsck(path: &PathBuf) {
37*bb4ee6a4SAndroid Build Coastguard Worker     // Run fsck and scheck its exit code is 0.
38*bb4ee6a4SAndroid Build Coastguard Worker     // Passing 'y' to stop attempting interactive repair.
39*bb4ee6a4SAndroid Build Coastguard Worker     let output = Command::new(FSCK_PATH)
40*bb4ee6a4SAndroid Build Coastguard Worker         .arg("-fvy")
41*bb4ee6a4SAndroid Build Coastguard Worker         .arg(path)
42*bb4ee6a4SAndroid Build Coastguard Worker         .output()
43*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
44*bb4ee6a4SAndroid Build Coastguard Worker     println!("status: {}", output.status);
45*bb4ee6a4SAndroid Build Coastguard Worker     println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
46*bb4ee6a4SAndroid Build Coastguard Worker     println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
47*bb4ee6a4SAndroid Build Coastguard Worker     assert!(output.status.success());
48*bb4ee6a4SAndroid Build Coastguard Worker }
49*bb4ee6a4SAndroid Build Coastguard Worker 
run_debugfs_cmd(args: &[&str], disk: &PathBuf) -> String50*bb4ee6a4SAndroid Build Coastguard Worker fn run_debugfs_cmd(args: &[&str], disk: &PathBuf) -> String {
51*bb4ee6a4SAndroid Build Coastguard Worker     let output = Command::new(DEBUGFS_PATH)
52*bb4ee6a4SAndroid Build Coastguard Worker         .arg("-R")
53*bb4ee6a4SAndroid Build Coastguard Worker         .args(args)
54*bb4ee6a4SAndroid Build Coastguard Worker         .arg(disk)
55*bb4ee6a4SAndroid Build Coastguard Worker         .output()
56*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
57*bb4ee6a4SAndroid Build Coastguard Worker 
58*bb4ee6a4SAndroid Build Coastguard Worker     let stdout = String::from_utf8_lossy(&output.stdout);
59*bb4ee6a4SAndroid Build Coastguard Worker     let stderr = String::from_utf8_lossy(&output.stderr);
60*bb4ee6a4SAndroid Build Coastguard Worker     println!("status: {}", output.status);
61*bb4ee6a4SAndroid Build Coastguard Worker     println!("stdout: {stdout}");
62*bb4ee6a4SAndroid Build Coastguard Worker     println!("stderr: {stderr}");
63*bb4ee6a4SAndroid Build Coastguard Worker     assert!(output.status.success());
64*bb4ee6a4SAndroid Build Coastguard Worker 
65*bb4ee6a4SAndroid Build Coastguard Worker     stdout.trim_start().trim_end().to_string()
66*bb4ee6a4SAndroid Build Coastguard Worker }
67*bb4ee6a4SAndroid Build Coastguard Worker 
mkfs(td: &TempDir, builder: Builder) -> PathBuf68*bb4ee6a4SAndroid Build Coastguard Worker fn mkfs(td: &TempDir, builder: Builder) -> PathBuf {
69*bb4ee6a4SAndroid Build Coastguard Worker     let path = td.path().join("empty.ext2");
70*bb4ee6a4SAndroid Build Coastguard Worker     let mem = builder
71*bb4ee6a4SAndroid Build Coastguard Worker         .allocate_memory()
72*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap()
73*bb4ee6a4SAndroid Build Coastguard Worker         .build_mmap_info()
74*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap()
75*bb4ee6a4SAndroid Build Coastguard Worker         .do_mmap()
76*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
77*bb4ee6a4SAndroid Build Coastguard Worker     // SAFETY: `mem` has a valid pointer and its size.
78*bb4ee6a4SAndroid Build Coastguard Worker     let buf = unsafe { std::slice::from_raw_parts(mem.as_ptr(), mem.size()) };
79*bb4ee6a4SAndroid Build Coastguard Worker     let mut file = OpenOptions::new()
80*bb4ee6a4SAndroid Build Coastguard Worker         .write(true)
81*bb4ee6a4SAndroid Build Coastguard Worker         .create(true)
82*bb4ee6a4SAndroid Build Coastguard Worker         .truncate(true)
83*bb4ee6a4SAndroid Build Coastguard Worker         .open(&path)
84*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
85*bb4ee6a4SAndroid Build Coastguard Worker     file.write_all(buf).unwrap();
86*bb4ee6a4SAndroid Build Coastguard Worker 
87*bb4ee6a4SAndroid Build Coastguard Worker     run_fsck(&path);
88*bb4ee6a4SAndroid Build Coastguard Worker 
89*bb4ee6a4SAndroid Build Coastguard Worker     path
90*bb4ee6a4SAndroid Build Coastguard Worker }
91*bb4ee6a4SAndroid Build Coastguard Worker 
92*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_mkfs_empty()93*bb4ee6a4SAndroid Build Coastguard Worker fn test_mkfs_empty() {
94*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
95*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
96*bb4ee6a4SAndroid Build Coastguard Worker         &td,
97*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
98*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 1024,
99*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 1024,
100*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
101*bb4ee6a4SAndroid Build Coastguard Worker         },
102*bb4ee6a4SAndroid Build Coastguard Worker     );
103*bb4ee6a4SAndroid Build Coastguard Worker 
104*bb4ee6a4SAndroid Build Coastguard Worker     // Ensure the content of the generated disk image with `debugfs`.
105*bb4ee6a4SAndroid Build Coastguard Worker     // It contains the following entries:
106*bb4ee6a4SAndroid Build Coastguard Worker     // - `.`: the rootdir whose inode is 2 and rec_len is 12.
107*bb4ee6a4SAndroid Build Coastguard Worker     // - `..`: this is also the rootdir with same inode and the same rec_len.
108*bb4ee6a4SAndroid Build Coastguard Worker     // - `lost+found`: inode is 11 and rec_len is 4072 (= block_size - 2*12).
109*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq!(
110*bb4ee6a4SAndroid Build Coastguard Worker         run_debugfs_cmd(&["ls"], &disk),
111*bb4ee6a4SAndroid Build Coastguard Worker         "2  (12) .    2  (12) ..    11  (4072) lost+found"
112*bb4ee6a4SAndroid Build Coastguard Worker     );
113*bb4ee6a4SAndroid Build Coastguard Worker }
114*bb4ee6a4SAndroid Build Coastguard Worker 
115*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_mkfs_empty_multi_block_groups()116*bb4ee6a4SAndroid Build Coastguard Worker fn test_mkfs_empty_multi_block_groups() {
117*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
118*bb4ee6a4SAndroid Build Coastguard Worker     let blocks_per_group = 2048;
119*bb4ee6a4SAndroid Build Coastguard Worker     let num_groups = 2;
120*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
121*bb4ee6a4SAndroid Build Coastguard Worker         &td,
122*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
123*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group,
124*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
125*bb4ee6a4SAndroid Build Coastguard Worker             size: 4096 * blocks_per_group * num_groups,
126*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
127*bb4ee6a4SAndroid Build Coastguard Worker         },
128*bb4ee6a4SAndroid Build Coastguard Worker     );
129*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq!(
130*bb4ee6a4SAndroid Build Coastguard Worker         run_debugfs_cmd(&["ls"], &disk),
131*bb4ee6a4SAndroid Build Coastguard Worker         "2  (12) .    2  (12) ..    11  (4072) lost+found"
132*bb4ee6a4SAndroid Build Coastguard Worker     );
133*bb4ee6a4SAndroid Build Coastguard Worker }
134*bb4ee6a4SAndroid Build Coastguard Worker 
collect_paths(dir: &Path, skip_lost_found: bool) -> BTreeSet<(String, PathBuf)>135*bb4ee6a4SAndroid Build Coastguard Worker fn collect_paths(dir: &Path, skip_lost_found: bool) -> BTreeSet<(String, PathBuf)> {
136*bb4ee6a4SAndroid Build Coastguard Worker     WalkDir::new(dir)
137*bb4ee6a4SAndroid Build Coastguard Worker         .into_iter()
138*bb4ee6a4SAndroid Build Coastguard Worker         .filter_map(|entry| {
139*bb4ee6a4SAndroid Build Coastguard Worker             entry.ok().and_then(|e| {
140*bb4ee6a4SAndroid Build Coastguard Worker                 let name = e
141*bb4ee6a4SAndroid Build Coastguard Worker                     .path()
142*bb4ee6a4SAndroid Build Coastguard Worker                     .strip_prefix(dir)
143*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap()
144*bb4ee6a4SAndroid Build Coastguard Worker                     .to_string_lossy()
145*bb4ee6a4SAndroid Build Coastguard Worker                     .into_owned();
146*bb4ee6a4SAndroid Build Coastguard Worker                 let path = e.path().to_path_buf();
147*bb4ee6a4SAndroid Build Coastguard Worker                 if name.is_empty() {
148*bb4ee6a4SAndroid Build Coastguard Worker                     return None;
149*bb4ee6a4SAndroid Build Coastguard Worker                 }
150*bb4ee6a4SAndroid Build Coastguard Worker                 if skip_lost_found && name == "lost+found" {
151*bb4ee6a4SAndroid Build Coastguard Worker                     return None;
152*bb4ee6a4SAndroid Build Coastguard Worker                 }
153*bb4ee6a4SAndroid Build Coastguard Worker 
154*bb4ee6a4SAndroid Build Coastguard Worker                 Some((name, path))
155*bb4ee6a4SAndroid Build Coastguard Worker             })
156*bb4ee6a4SAndroid Build Coastguard Worker         })
157*bb4ee6a4SAndroid Build Coastguard Worker         .collect()
158*bb4ee6a4SAndroid Build Coastguard Worker }
159*bb4ee6a4SAndroid Build Coastguard Worker 
assert_eq_dirs( td: &TempDir, dir: &Path, disk: &PathBuf, xattr_map: Option<BTreeMap<String, Vec<(&str, &str)>>>, )160*bb4ee6a4SAndroid Build Coastguard Worker fn assert_eq_dirs(
161*bb4ee6a4SAndroid Build Coastguard Worker     td: &TempDir,
162*bb4ee6a4SAndroid Build Coastguard Worker     dir: &Path,
163*bb4ee6a4SAndroid Build Coastguard Worker     disk: &PathBuf,
164*bb4ee6a4SAndroid Build Coastguard Worker     // Check the correct xattr is set and any unexpected one isn't set.
165*bb4ee6a4SAndroid Build Coastguard Worker     // Pass None to skip this check for test cases where many files are created.
166*bb4ee6a4SAndroid Build Coastguard Worker     xattr_map: Option<BTreeMap<String, Vec<(&str, &str)>>>,
167*bb4ee6a4SAndroid Build Coastguard Worker ) {
168*bb4ee6a4SAndroid Build Coastguard Worker     // dump the disk contents to `dump_dir`.
169*bb4ee6a4SAndroid Build Coastguard Worker     let dump_dir = td.path().join("dump");
170*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dump_dir).unwrap();
171*bb4ee6a4SAndroid Build Coastguard Worker     run_debugfs_cmd(
172*bb4ee6a4SAndroid Build Coastguard Worker         &[&format!(
173*bb4ee6a4SAndroid Build Coastguard Worker             "rdump / {}",
174*bb4ee6a4SAndroid Build Coastguard Worker             dump_dir.as_os_str().to_str().unwrap()
175*bb4ee6a4SAndroid Build Coastguard Worker         )],
176*bb4ee6a4SAndroid Build Coastguard Worker         disk,
177*bb4ee6a4SAndroid Build Coastguard Worker     );
178*bb4ee6a4SAndroid Build Coastguard Worker 
179*bb4ee6a4SAndroid Build Coastguard Worker     let paths1 = collect_paths(dir, true);
180*bb4ee6a4SAndroid Build Coastguard Worker     let paths2 = collect_paths(&dump_dir, true);
181*bb4ee6a4SAndroid Build Coastguard Worker     if paths1.len() != paths2.len() {
182*bb4ee6a4SAndroid Build Coastguard Worker         panic!(
183*bb4ee6a4SAndroid Build Coastguard Worker             "number of entries mismatch: {:?}={:?}, {:?}={:?}",
184*bb4ee6a4SAndroid Build Coastguard Worker             dir,
185*bb4ee6a4SAndroid Build Coastguard Worker             paths1.len(),
186*bb4ee6a4SAndroid Build Coastguard Worker             dump_dir,
187*bb4ee6a4SAndroid Build Coastguard Worker             paths2.len()
188*bb4ee6a4SAndroid Build Coastguard Worker         );
189*bb4ee6a4SAndroid Build Coastguard Worker     }
190*bb4ee6a4SAndroid Build Coastguard Worker 
191*bb4ee6a4SAndroid Build Coastguard Worker     for ((name1, path1), (name2, path2)) in paths1.iter().zip(paths2.iter()) {
192*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(name1, name2);
193*bb4ee6a4SAndroid Build Coastguard Worker         let m1 = symlink_metadata(path1).unwrap();
194*bb4ee6a4SAndroid Build Coastguard Worker         let m2 = symlink_metadata(path2).unwrap();
195*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
196*bb4ee6a4SAndroid Build Coastguard Worker             m1.file_type(),
197*bb4ee6a4SAndroid Build Coastguard Worker             m2.file_type(),
198*bb4ee6a4SAndroid Build Coastguard Worker             "file type mismatch ({name1})"
199*bb4ee6a4SAndroid Build Coastguard Worker         );
200*bb4ee6a4SAndroid Build Coastguard Worker 
201*bb4ee6a4SAndroid Build Coastguard Worker         if m1.file_type().is_symlink() {
202*bb4ee6a4SAndroid Build Coastguard Worker             let dst1 = read_link(path1).unwrap();
203*bb4ee6a4SAndroid Build Coastguard Worker             let dst2 = read_link(path2).unwrap();
204*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(
205*bb4ee6a4SAndroid Build Coastguard Worker                 dst1, dst2,
206*bb4ee6a4SAndroid Build Coastguard Worker                 "symlink mismatch ({name1}): {:?}->{:?} vs {:?}->{:?}",
207*bb4ee6a4SAndroid Build Coastguard Worker                 path1, dst1, path2, dst2
208*bb4ee6a4SAndroid Build Coastguard Worker             );
209*bb4ee6a4SAndroid Build Coastguard Worker         } else {
210*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(m1.len(), m2.len(), "length mismatch ({name1})");
211*bb4ee6a4SAndroid Build Coastguard Worker         }
212*bb4ee6a4SAndroid Build Coastguard Worker 
213*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
214*bb4ee6a4SAndroid Build Coastguard Worker             m1.permissions(),
215*bb4ee6a4SAndroid Build Coastguard Worker             m2.permissions(),
216*bb4ee6a4SAndroid Build Coastguard Worker             "permissions mismatch ({name1})"
217*bb4ee6a4SAndroid Build Coastguard Worker         );
218*bb4ee6a4SAndroid Build Coastguard Worker 
219*bb4ee6a4SAndroid Build Coastguard Worker         if m1.file_type().is_file() {
220*bb4ee6a4SAndroid Build Coastguard Worker             // Check contents
221*bb4ee6a4SAndroid Build Coastguard Worker             let c1 = std::fs::read_to_string(path1).unwrap();
222*bb4ee6a4SAndroid Build Coastguard Worker             let c2 = std::fs::read_to_string(path2).unwrap();
223*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(c1, c2, "content mismatch: ({name1})");
224*bb4ee6a4SAndroid Build Coastguard Worker         }
225*bb4ee6a4SAndroid Build Coastguard Worker 
226*bb4ee6a4SAndroid Build Coastguard Worker         // Check xattr
227*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(mp) = &xattr_map {
228*bb4ee6a4SAndroid Build Coastguard Worker             match mp.get(name1) {
229*bb4ee6a4SAndroid Build Coastguard Worker                 Some(expected_xattrs) if !expected_xattrs.is_empty() => {
230*bb4ee6a4SAndroid Build Coastguard Worker                     for (key, value) in expected_xattrs {
231*bb4ee6a4SAndroid Build Coastguard Worker                         let s = run_debugfs_cmd(&[&format!("ea_get -V {name1} {key}",)], disk);
232*bb4ee6a4SAndroid Build Coastguard Worker                         assert_eq!(&s, value);
233*bb4ee6a4SAndroid Build Coastguard Worker                     }
234*bb4ee6a4SAndroid Build Coastguard Worker                 }
235*bb4ee6a4SAndroid Build Coastguard Worker                 // If no xattr is specified, any value must not be set.
236*bb4ee6a4SAndroid Build Coastguard Worker                 _ => {
237*bb4ee6a4SAndroid Build Coastguard Worker                     let s = run_debugfs_cmd(&[&format!("ea_list {}", name1,)], disk);
238*bb4ee6a4SAndroid Build Coastguard Worker                     assert_eq!(s, "");
239*bb4ee6a4SAndroid Build Coastguard Worker                 }
240*bb4ee6a4SAndroid Build Coastguard Worker             }
241*bb4ee6a4SAndroid Build Coastguard Worker         }
242*bb4ee6a4SAndroid Build Coastguard Worker     }
243*bb4ee6a4SAndroid Build Coastguard Worker }
244*bb4ee6a4SAndroid Build Coastguard Worker 
245*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_simple_dir()246*bb4ee6a4SAndroid Build Coastguard Worker fn test_simple_dir() {
247*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
248*bb4ee6a4SAndroid Build Coastguard Worker     // ├── a.txt
249*bb4ee6a4SAndroid Build Coastguard Worker     // ├── b.txt
250*bb4ee6a4SAndroid Build Coastguard Worker     // └── dir
251*bb4ee6a4SAndroid Build Coastguard Worker     //     └── c.txt
252*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
253*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
254*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(&dir).unwrap();
255*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir.join("a.txt")).unwrap();
256*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir.join("b.txt")).unwrap();
257*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(dir.join("dir")).unwrap();
258*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir.join("dir/c.txt")).unwrap();
259*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
260*bb4ee6a4SAndroid Build Coastguard Worker         &td,
261*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
262*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
263*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
264*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
265*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
266*bb4ee6a4SAndroid Build Coastguard Worker         },
267*bb4ee6a4SAndroid Build Coastguard Worker     );
268*bb4ee6a4SAndroid Build Coastguard Worker 
269*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
270*bb4ee6a4SAndroid Build Coastguard Worker 
271*bb4ee6a4SAndroid Build Coastguard Worker     td.close().unwrap(); // make sure that tempdir is properly deleted.
272*bb4ee6a4SAndroid Build Coastguard Worker }
273*bb4ee6a4SAndroid Build Coastguard Worker 
274*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_nested_dirs()275*bb4ee6a4SAndroid Build Coastguard Worker fn test_nested_dirs() {
276*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
277*bb4ee6a4SAndroid Build Coastguard Worker     // └── dir1
278*bb4ee6a4SAndroid Build Coastguard Worker     //     ├── a.txt
279*bb4ee6a4SAndroid Build Coastguard Worker     //     └── dir2
280*bb4ee6a4SAndroid Build Coastguard Worker     //         ├── b.txt
281*bb4ee6a4SAndroid Build Coastguard Worker     //         └── dir3
282*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
283*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
284*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(&dir).unwrap();
285*bb4ee6a4SAndroid Build Coastguard Worker     let dir1 = &dir.join("dir1");
286*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(dir1).unwrap();
287*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir1.join("a.txt")).unwrap();
288*bb4ee6a4SAndroid Build Coastguard Worker     let dir2 = dir1.join("dir2");
289*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(&dir2).unwrap();
290*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir2.join("b.txt")).unwrap();
291*bb4ee6a4SAndroid Build Coastguard Worker     let dir3 = dir2.join("dir3");
292*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(dir3).unwrap();
293*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
294*bb4ee6a4SAndroid Build Coastguard Worker         &td,
295*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
296*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
297*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
298*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
299*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
300*bb4ee6a4SAndroid Build Coastguard Worker         },
301*bb4ee6a4SAndroid Build Coastguard Worker     );
302*bb4ee6a4SAndroid Build Coastguard Worker 
303*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
304*bb4ee6a4SAndroid Build Coastguard Worker }
305*bb4ee6a4SAndroid Build Coastguard Worker 
306*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_file_contents()307*bb4ee6a4SAndroid Build Coastguard Worker fn test_file_contents() {
308*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
309*bb4ee6a4SAndroid Build Coastguard Worker     // ├── hello.txt (content: "Hello!\n")
310*bb4ee6a4SAndroid Build Coastguard Worker     // └── big.txt (content: 10KB of data, which doesn't fit in one block)
311*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
312*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
313*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(&dir).unwrap();
314*bb4ee6a4SAndroid Build Coastguard Worker     let mut hello = File::create(dir.join("hello.txt")).unwrap();
315*bb4ee6a4SAndroid Build Coastguard Worker     hello.write_all(b"Hello!\n").unwrap();
316*bb4ee6a4SAndroid Build Coastguard Worker     let mut big = BufWriter::new(File::create(dir.join("big.txt")).unwrap());
317*bb4ee6a4SAndroid Build Coastguard Worker     let data = b"123456789\n";
318*bb4ee6a4SAndroid Build Coastguard Worker     for _ in 0..1024 {
319*bb4ee6a4SAndroid Build Coastguard Worker         big.write_all(data).unwrap();
320*bb4ee6a4SAndroid Build Coastguard Worker     }
321*bb4ee6a4SAndroid Build Coastguard Worker 
322*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
323*bb4ee6a4SAndroid Build Coastguard Worker         &td,
324*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
325*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
326*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
327*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
328*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
329*bb4ee6a4SAndroid Build Coastguard Worker         },
330*bb4ee6a4SAndroid Build Coastguard Worker     );
331*bb4ee6a4SAndroid Build Coastguard Worker 
332*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
333*bb4ee6a4SAndroid Build Coastguard Worker }
334*bb4ee6a4SAndroid Build Coastguard Worker 
335*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_max_file_name()336*bb4ee6a4SAndroid Build Coastguard Worker fn test_max_file_name() {
337*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
338*bb4ee6a4SAndroid Build Coastguard Worker     // └── aa..aa (whose file name length is 255, which is the ext2/3/4's maximum file name length)
339*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
340*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
341*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(&dir).unwrap();
342*bb4ee6a4SAndroid Build Coastguard Worker     let long_name = "a".repeat(255);
343*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir.join(long_name)).unwrap();
344*bb4ee6a4SAndroid Build Coastguard Worker 
345*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
346*bb4ee6a4SAndroid Build Coastguard Worker         &td,
347*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
348*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
349*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
350*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
351*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
352*bb4ee6a4SAndroid Build Coastguard Worker         },
353*bb4ee6a4SAndroid Build Coastguard Worker     );
354*bb4ee6a4SAndroid Build Coastguard Worker 
355*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
356*bb4ee6a4SAndroid Build Coastguard Worker }
357*bb4ee6a4SAndroid Build Coastguard Worker 
358*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_mkfs_indirect_block()359*bb4ee6a4SAndroid Build Coastguard Worker fn test_mkfs_indirect_block() {
360*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
361*bb4ee6a4SAndroid Build Coastguard Worker     // ├── big.txt (80KiB), which requires indirect blocks
362*bb4ee6a4SAndroid Build Coastguard Worker     // └── huge.txt (8MiB), which requires doubly indirect blocks
363*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
364*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
365*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dir).unwrap();
366*bb4ee6a4SAndroid Build Coastguard Worker     let mut big = std::fs::File::create(dir.join("big.txt")).unwrap();
367*bb4ee6a4SAndroid Build Coastguard Worker     big.seek(SeekFrom::Start(80 * 1024)).unwrap();
368*bb4ee6a4SAndroid Build Coastguard Worker     big.write_all(&[0]).unwrap();
369*bb4ee6a4SAndroid Build Coastguard Worker 
370*bb4ee6a4SAndroid Build Coastguard Worker     let mut huge = std::fs::File::create(dir.join("huge.txt")).unwrap();
371*bb4ee6a4SAndroid Build Coastguard Worker     huge.seek(SeekFrom::Start(8 * 1024 * 1024)).unwrap();
372*bb4ee6a4SAndroid Build Coastguard Worker     huge.write_all(&[0]).unwrap();
373*bb4ee6a4SAndroid Build Coastguard Worker 
374*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
375*bb4ee6a4SAndroid Build Coastguard Worker         &td,
376*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
377*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 4096,
378*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
379*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
380*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
381*bb4ee6a4SAndroid Build Coastguard Worker         },
382*bb4ee6a4SAndroid Build Coastguard Worker     );
383*bb4ee6a4SAndroid Build Coastguard Worker 
384*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
385*bb4ee6a4SAndroid Build Coastguard Worker }
386*bb4ee6a4SAndroid Build Coastguard Worker 
387*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_mkfs_symlink()388*bb4ee6a4SAndroid Build Coastguard Worker fn test_mkfs_symlink() {
389*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
390*bb4ee6a4SAndroid Build Coastguard Worker     // ├── a.txt
391*bb4ee6a4SAndroid Build Coastguard Worker     // ├── self -> ./self
392*bb4ee6a4SAndroid Build Coastguard Worker     // ├── symlink0 -> ./a.txt
393*bb4ee6a4SAndroid Build Coastguard Worker     // ├── symlink1 -> ./symlink0
394*bb4ee6a4SAndroid Build Coastguard Worker     // └── dir
395*bb4ee6a4SAndroid Build Coastguard Worker     //     └── upper-a -> ../a.txt
396*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
397*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
398*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(&dir).unwrap();
399*bb4ee6a4SAndroid Build Coastguard Worker 
400*bb4ee6a4SAndroid Build Coastguard Worker     let mut f = File::create(dir.join("a.txt")).unwrap();
401*bb4ee6a4SAndroid Build Coastguard Worker     f.write_all("Hello".as_bytes()).unwrap();
402*bb4ee6a4SAndroid Build Coastguard Worker 
403*bb4ee6a4SAndroid Build Coastguard Worker     symlink("./self", dir.join("self")).unwrap();
404*bb4ee6a4SAndroid Build Coastguard Worker 
405*bb4ee6a4SAndroid Build Coastguard Worker     symlink("./a.txt", dir.join("symlink0")).unwrap();
406*bb4ee6a4SAndroid Build Coastguard Worker     symlink("./symlink0", dir.join("symlink1")).unwrap();
407*bb4ee6a4SAndroid Build Coastguard Worker 
408*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(dir.join("dir")).unwrap();
409*bb4ee6a4SAndroid Build Coastguard Worker     symlink("../a.txt", dir.join("dir/upper-a")).unwrap();
410*bb4ee6a4SAndroid Build Coastguard Worker 
411*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
412*bb4ee6a4SAndroid Build Coastguard Worker         &td,
413*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
414*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
415*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
416*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
417*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
418*bb4ee6a4SAndroid Build Coastguard Worker         },
419*bb4ee6a4SAndroid Build Coastguard Worker     );
420*bb4ee6a4SAndroid Build Coastguard Worker 
421*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
422*bb4ee6a4SAndroid Build Coastguard Worker }
423*bb4ee6a4SAndroid Build Coastguard Worker 
424*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_mkfs_abs_symlink()425*bb4ee6a4SAndroid Build Coastguard Worker fn test_mkfs_abs_symlink() {
426*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
427*bb4ee6a4SAndroid Build Coastguard Worker     // ├── a.txt
428*bb4ee6a4SAndroid Build Coastguard Worker     // ├── a -> /testdata/a
429*bb4ee6a4SAndroid Build Coastguard Worker     // ├── self -> /testdata/self
430*bb4ee6a4SAndroid Build Coastguard Worker     // ├── tmp -> /tmp
431*bb4ee6a4SAndroid Build Coastguard Worker     // └── abc -> /a/b/c
432*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
433*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
434*bb4ee6a4SAndroid Build Coastguard Worker 
435*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dir).unwrap();
436*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir.join("a.txt")).unwrap();
437*bb4ee6a4SAndroid Build Coastguard Worker     symlink(dir.join("a.txt"), dir.join("a")).unwrap();
438*bb4ee6a4SAndroid Build Coastguard Worker     symlink(dir.join("self"), dir.join("self")).unwrap();
439*bb4ee6a4SAndroid Build Coastguard Worker     symlink("/tmp/", dir.join("tmp")).unwrap();
440*bb4ee6a4SAndroid Build Coastguard Worker     symlink("/a/b/c", dir.join("abc")).unwrap();
441*bb4ee6a4SAndroid Build Coastguard Worker 
442*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
443*bb4ee6a4SAndroid Build Coastguard Worker         &td,
444*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
445*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
446*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
447*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
448*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
449*bb4ee6a4SAndroid Build Coastguard Worker         },
450*bb4ee6a4SAndroid Build Coastguard Worker     );
451*bb4ee6a4SAndroid Build Coastguard Worker 
452*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
453*bb4ee6a4SAndroid Build Coastguard Worker }
454*bb4ee6a4SAndroid Build Coastguard Worker 
455*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_mkfs_symlink_to_deleted()456*bb4ee6a4SAndroid Build Coastguard Worker fn test_mkfs_symlink_to_deleted() {
457*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
458*bb4ee6a4SAndroid Build Coastguard Worker     // ├── (deleted)
459*bb4ee6a4SAndroid Build Coastguard Worker     // └── symlink_to_deleted -> (deleted)
460*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
461*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
462*bb4ee6a4SAndroid Build Coastguard Worker 
463*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dir).unwrap();
464*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir.join("deleted")).unwrap();
465*bb4ee6a4SAndroid Build Coastguard Worker     symlink("./deleted", dir.join("symlink_to_deleted")).unwrap();
466*bb4ee6a4SAndroid Build Coastguard Worker     fs::remove_file(dir.join("deleted")).unwrap();
467*bb4ee6a4SAndroid Build Coastguard Worker 
468*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
469*bb4ee6a4SAndroid Build Coastguard Worker         &td,
470*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
471*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
472*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
473*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
474*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
475*bb4ee6a4SAndroid Build Coastguard Worker         },
476*bb4ee6a4SAndroid Build Coastguard Worker     );
477*bb4ee6a4SAndroid Build Coastguard Worker 
478*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
479*bb4ee6a4SAndroid Build Coastguard Worker }
480*bb4ee6a4SAndroid Build Coastguard Worker 
481*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_mkfs_long_symlink()482*bb4ee6a4SAndroid Build Coastguard Worker fn test_mkfs_long_symlink() {
483*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
484*bb4ee6a4SAndroid Build Coastguard Worker     // ├── /(long name directory)/a.txt
485*bb4ee6a4SAndroid Build Coastguard Worker     // └── symlink -> /(long name directory)/a.txt
486*bb4ee6a4SAndroid Build Coastguard Worker     // ├── (60-byte filename)
487*bb4ee6a4SAndroid Build Coastguard Worker     // └── symlink60 -> (60-byte filename)
488*bb4ee6a4SAndroid Build Coastguard Worker 
489*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
490*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
491*bb4ee6a4SAndroid Build Coastguard Worker 
492*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(&dir).unwrap();
493*bb4ee6a4SAndroid Build Coastguard Worker 
494*bb4ee6a4SAndroid Build Coastguard Worker     const LONG_DIR_NAME: &str =
495*bb4ee6a4SAndroid Build Coastguard Worker         "this_is_a_very_long_directory_name_so_that_name_cannoot_fit_in_60_characters_in_inode";
496*bb4ee6a4SAndroid Build Coastguard Worker     assert!(LONG_DIR_NAME.len() > 60);
497*bb4ee6a4SAndroid Build Coastguard Worker 
498*bb4ee6a4SAndroid Build Coastguard Worker     let long_dir = dir.join(LONG_DIR_NAME);
499*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(&long_dir).unwrap();
500*bb4ee6a4SAndroid Build Coastguard Worker     File::create(long_dir.join("a.txt")).unwrap();
501*bb4ee6a4SAndroid Build Coastguard Worker     symlink(long_dir.join("a.txt"), dir.join("symlink")).unwrap();
502*bb4ee6a4SAndroid Build Coastguard Worker 
503*bb4ee6a4SAndroid Build Coastguard Worker     const SIXTY_CHAR_DIR_NAME: &str =
504*bb4ee6a4SAndroid Build Coastguard Worker         "./this_is_just_60_byte_long_so_it_can_work_as_a_corner_case.";
505*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq!(SIXTY_CHAR_DIR_NAME.len(), 60);
506*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir.join(SIXTY_CHAR_DIR_NAME)).unwrap();
507*bb4ee6a4SAndroid Build Coastguard Worker     symlink(SIXTY_CHAR_DIR_NAME, dir.join("symlink60")).unwrap();
508*bb4ee6a4SAndroid Build Coastguard Worker 
509*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
510*bb4ee6a4SAndroid Build Coastguard Worker         &td,
511*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
512*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
513*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
514*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
515*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
516*bb4ee6a4SAndroid Build Coastguard Worker         },
517*bb4ee6a4SAndroid Build Coastguard Worker     );
518*bb4ee6a4SAndroid Build Coastguard Worker 
519*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
520*bb4ee6a4SAndroid Build Coastguard Worker }
521*bb4ee6a4SAndroid Build Coastguard Worker 
522*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_ignore_lost_found()523*bb4ee6a4SAndroid Build Coastguard Worker fn test_ignore_lost_found() {
524*bb4ee6a4SAndroid Build Coastguard Worker     // Ignore /lost+found/ directory in source to avoid conflict.
525*bb4ee6a4SAndroid Build Coastguard Worker     //
526*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
527*bb4ee6a4SAndroid Build Coastguard Worker     // ├── lost+found (ignored and recreated as an empty dir)
528*bb4ee6a4SAndroid Build Coastguard Worker     // │   └── should_be_ignored.txt
529*bb4ee6a4SAndroid Build Coastguard Worker     // └── sub
530*bb4ee6a4SAndroid Build Coastguard Worker     //     └── lost+found (not ignored)
531*bb4ee6a4SAndroid Build Coastguard Worker     //         └── a.txt
532*bb4ee6a4SAndroid Build Coastguard Worker 
533*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
534*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
535*bb4ee6a4SAndroid Build Coastguard Worker 
536*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(&dir).unwrap();
537*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(dir.join("lost+found")).unwrap();
538*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir.join("lost+found").join("should_be_ignored.txt")).unwrap();
539*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(dir.join("sub")).unwrap();
540*bb4ee6a4SAndroid Build Coastguard Worker     create_dir(dir.join("sub").join("lost+found")).unwrap();
541*bb4ee6a4SAndroid Build Coastguard Worker     File::create(dir.join("sub").join("lost+found").join("a.txt")).unwrap();
542*bb4ee6a4SAndroid Build Coastguard Worker 
543*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
544*bb4ee6a4SAndroid Build Coastguard Worker         &td,
545*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
546*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
547*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
548*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
549*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
550*bb4ee6a4SAndroid Build Coastguard Worker         },
551*bb4ee6a4SAndroid Build Coastguard Worker     );
552*bb4ee6a4SAndroid Build Coastguard Worker 
553*bb4ee6a4SAndroid Build Coastguard Worker     // dump the disk contents to `dump_dir`.
554*bb4ee6a4SAndroid Build Coastguard Worker     let dump_dir = td.path().join("dump");
555*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dump_dir).unwrap();
556*bb4ee6a4SAndroid Build Coastguard Worker     run_debugfs_cmd(
557*bb4ee6a4SAndroid Build Coastguard Worker         &[&format!(
558*bb4ee6a4SAndroid Build Coastguard Worker             "rdump / {}",
559*bb4ee6a4SAndroid Build Coastguard Worker             dump_dir.as_os_str().to_str().unwrap()
560*bb4ee6a4SAndroid Build Coastguard Worker         )],
561*bb4ee6a4SAndroid Build Coastguard Worker         &disk,
562*bb4ee6a4SAndroid Build Coastguard Worker     );
563*bb4ee6a4SAndroid Build Coastguard Worker 
564*bb4ee6a4SAndroid Build Coastguard Worker     let paths = collect_paths(&dump_dir, false /* skip_lost_found */)
565*bb4ee6a4SAndroid Build Coastguard Worker         .into_iter()
566*bb4ee6a4SAndroid Build Coastguard Worker         .map(|(path, _)| path)
567*bb4ee6a4SAndroid Build Coastguard Worker         .collect::<BTreeSet<_>>();
568*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq!(
569*bb4ee6a4SAndroid Build Coastguard Worker         paths,
570*bb4ee6a4SAndroid Build Coastguard Worker         BTreeSet::from([
571*bb4ee6a4SAndroid Build Coastguard Worker             "lost+found".to_string(),
572*bb4ee6a4SAndroid Build Coastguard Worker             // 'lost+found/should_be_ignored.txt' must not in the result.
573*bb4ee6a4SAndroid Build Coastguard Worker             "sub".to_string(),
574*bb4ee6a4SAndroid Build Coastguard Worker             "sub/lost+found".to_string(),
575*bb4ee6a4SAndroid Build Coastguard Worker             "sub/lost+found/a.txt".to_string()
576*bb4ee6a4SAndroid Build Coastguard Worker         ])
577*bb4ee6a4SAndroid Build Coastguard Worker     );
578*bb4ee6a4SAndroid Build Coastguard Worker }
579*bb4ee6a4SAndroid Build Coastguard Worker 
580*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_multiple_block_directory_entry()581*bb4ee6a4SAndroid Build Coastguard Worker fn test_multiple_block_directory_entry() {
582*bb4ee6a4SAndroid Build Coastguard Worker     // Creates a many files in a directory.
583*bb4ee6a4SAndroid Build Coastguard Worker     // So the sum of the sizes of directory entries exceeds 4KB and they need to be stored in
584*bb4ee6a4SAndroid Build Coastguard Worker     // multiple blocks.
585*bb4ee6a4SAndroid Build Coastguard Worker     //
586*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
587*bb4ee6a4SAndroid Build Coastguard Worker     // ├─  0.txt
588*bb4ee6a4SAndroid Build Coastguard Worker     // ├─  1.txt
589*bb4ee6a4SAndroid Build Coastguard Worker     // ...
590*bb4ee6a4SAndroid Build Coastguard Worker     // └── 999.txt
591*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
592*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
593*bb4ee6a4SAndroid Build Coastguard Worker 
594*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dir).unwrap();
595*bb4ee6a4SAndroid Build Coastguard Worker 
596*bb4ee6a4SAndroid Build Coastguard Worker     for i in 0..1000 {
597*bb4ee6a4SAndroid Build Coastguard Worker         let path = dir.join(format!("{i}.txt"));
598*bb4ee6a4SAndroid Build Coastguard Worker         File::create(&path).unwrap();
599*bb4ee6a4SAndroid Build Coastguard Worker     }
600*bb4ee6a4SAndroid Build Coastguard Worker 
601*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
602*bb4ee6a4SAndroid Build Coastguard Worker         &td,
603*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
604*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group: 2048,
605*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 4096,
606*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
607*bb4ee6a4SAndroid Build Coastguard Worker             ..Default::default()
608*bb4ee6a4SAndroid Build Coastguard Worker         },
609*bb4ee6a4SAndroid Build Coastguard Worker     );
610*bb4ee6a4SAndroid Build Coastguard Worker 
611*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, None); // skip xattr check
612*bb4ee6a4SAndroid Build Coastguard Worker }
613*bb4ee6a4SAndroid Build Coastguard Worker 
614*bb4ee6a4SAndroid Build Coastguard Worker // Test a case where the inode tables spans multiple block groups.
615*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_multiple_bg_multi_inode_bitmap()616*bb4ee6a4SAndroid Build Coastguard Worker fn test_multiple_bg_multi_inode_bitmap() {
617*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
618*bb4ee6a4SAndroid Build Coastguard Worker     // ├─  0.txt
619*bb4ee6a4SAndroid Build Coastguard Worker     // ├─  1.txt
620*bb4ee6a4SAndroid Build Coastguard Worker     // ...
621*bb4ee6a4SAndroid Build Coastguard Worker     // └── 999.txt
622*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
623*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
624*bb4ee6a4SAndroid Build Coastguard Worker 
625*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dir).unwrap();
626*bb4ee6a4SAndroid Build Coastguard Worker 
627*bb4ee6a4SAndroid Build Coastguard Worker     for i in 0..1000 {
628*bb4ee6a4SAndroid Build Coastguard Worker         let fname = format!("{i}.txt");
629*bb4ee6a4SAndroid Build Coastguard Worker         let path = dir.join(&fname);
630*bb4ee6a4SAndroid Build Coastguard Worker         let mut f = File::create(&path).unwrap();
631*bb4ee6a4SAndroid Build Coastguard Worker         // Write a file name to the file.
632*bb4ee6a4SAndroid Build Coastguard Worker         f.write_all(fname.as_bytes()).unwrap();
633*bb4ee6a4SAndroid Build Coastguard Worker     }
634*bb4ee6a4SAndroid Build Coastguard Worker 
635*bb4ee6a4SAndroid Build Coastguard Worker     let blocks_per_group = 1024;
636*bb4ee6a4SAndroid Build Coastguard Worker     // Set `inodes_per_group` to a smaller value than the number of files.
637*bb4ee6a4SAndroid Build Coastguard Worker     // So, the inode table in the 2nd block group will be used.
638*bb4ee6a4SAndroid Build Coastguard Worker     let inodes_per_group = 512;
639*bb4ee6a4SAndroid Build Coastguard Worker     let num_groups = 2;
640*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
641*bb4ee6a4SAndroid Build Coastguard Worker         &td,
642*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
643*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group,
644*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group,
645*bb4ee6a4SAndroid Build Coastguard Worker             size: BLOCK_SIZE * blocks_per_group * num_groups,
646*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
647*bb4ee6a4SAndroid Build Coastguard Worker         },
648*bb4ee6a4SAndroid Build Coastguard Worker     );
649*bb4ee6a4SAndroid Build Coastguard Worker 
650*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, None);
651*bb4ee6a4SAndroid Build Coastguard Worker }
652*bb4ee6a4SAndroid Build Coastguard Worker 
653*bb4ee6a4SAndroid Build Coastguard Worker /// Test a case where the block tables spans multiple block groups.
654*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_multiple_bg_multi_block_bitmap()655*bb4ee6a4SAndroid Build Coastguard Worker fn test_multiple_bg_multi_block_bitmap() {
656*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
657*bb4ee6a4SAndroid Build Coastguard Worker     // ├─  0.txt
658*bb4ee6a4SAndroid Build Coastguard Worker     // ├─  1.txt
659*bb4ee6a4SAndroid Build Coastguard Worker     // ...
660*bb4ee6a4SAndroid Build Coastguard Worker     // └── 999.txt
661*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
662*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
663*bb4ee6a4SAndroid Build Coastguard Worker 
664*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dir).unwrap();
665*bb4ee6a4SAndroid Build Coastguard Worker 
666*bb4ee6a4SAndroid Build Coastguard Worker     for i in 0..1000 {
667*bb4ee6a4SAndroid Build Coastguard Worker         let fname = format!("{i}.txt");
668*bb4ee6a4SAndroid Build Coastguard Worker         let path = dir.join(&fname);
669*bb4ee6a4SAndroid Build Coastguard Worker         let mut f = File::create(&path).unwrap();
670*bb4ee6a4SAndroid Build Coastguard Worker         // Write a file name to the file.
671*bb4ee6a4SAndroid Build Coastguard Worker         f.write_all(fname.as_bytes()).unwrap();
672*bb4ee6a4SAndroid Build Coastguard Worker     }
673*bb4ee6a4SAndroid Build Coastguard Worker 
674*bb4ee6a4SAndroid Build Coastguard Worker     // Set `blocks_per_group` to a smaller value than the number of files.
675*bb4ee6a4SAndroid Build Coastguard Worker     // So, the block table in the 2nd block group will be used.
676*bb4ee6a4SAndroid Build Coastguard Worker     let blocks_per_group = 512;
677*bb4ee6a4SAndroid Build Coastguard Worker     let inodes_per_group = 2048;
678*bb4ee6a4SAndroid Build Coastguard Worker     let num_groups = 4;
679*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
680*bb4ee6a4SAndroid Build Coastguard Worker         &td,
681*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
682*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group,
683*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group,
684*bb4ee6a4SAndroid Build Coastguard Worker             size: BLOCK_SIZE * blocks_per_group * num_groups,
685*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
686*bb4ee6a4SAndroid Build Coastguard Worker         },
687*bb4ee6a4SAndroid Build Coastguard Worker     );
688*bb4ee6a4SAndroid Build Coastguard Worker 
689*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, None);
690*bb4ee6a4SAndroid Build Coastguard Worker }
691*bb4ee6a4SAndroid Build Coastguard Worker 
692*bb4ee6a4SAndroid Build Coastguard Worker // Test a case where a file spans multiple block groups.
693*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_multiple_bg_big_files()694*bb4ee6a4SAndroid Build Coastguard Worker fn test_multiple_bg_big_files() {
695*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
696*bb4ee6a4SAndroid Build Coastguard Worker     // ├─  0.txt (200 * 5000 bytes)
697*bb4ee6a4SAndroid Build Coastguard Worker     // ├─  1.txt (200 * 5000 bytes)
698*bb4ee6a4SAndroid Build Coastguard Worker     // ...
699*bb4ee6a4SAndroid Build Coastguard Worker     // └── 9.txt (200 * 5000 bytes)
700*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir().unwrap();
701*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
702*bb4ee6a4SAndroid Build Coastguard Worker 
703*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dir).unwrap();
704*bb4ee6a4SAndroid Build Coastguard Worker 
705*bb4ee6a4SAndroid Build Coastguard Worker     // Prepare a large data.
706*bb4ee6a4SAndroid Build Coastguard Worker     let data = vec!["0123456789"; 5000 * 20].concat();
707*bb4ee6a4SAndroid Build Coastguard Worker     for i in 0..10 {
708*bb4ee6a4SAndroid Build Coastguard Worker         let path = dir.join(format!("{i}.txt"));
709*bb4ee6a4SAndroid Build Coastguard Worker         let mut f = File::create(&path).unwrap();
710*bb4ee6a4SAndroid Build Coastguard Worker         f.write_all(data.as_bytes()).unwrap();
711*bb4ee6a4SAndroid Build Coastguard Worker     }
712*bb4ee6a4SAndroid Build Coastguard Worker 
713*bb4ee6a4SAndroid Build Coastguard Worker     // Set `blocks_per_group` to a value smaller than |size of a file| / 4K.
714*bb4ee6a4SAndroid Build Coastguard Worker     // So, each file spans multiple block groups.
715*bb4ee6a4SAndroid Build Coastguard Worker     let blocks_per_group = 128;
716*bb4ee6a4SAndroid Build Coastguard Worker     let num_groups = 50;
717*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(
718*bb4ee6a4SAndroid Build Coastguard Worker         &td,
719*bb4ee6a4SAndroid Build Coastguard Worker         Builder {
720*bb4ee6a4SAndroid Build Coastguard Worker             blocks_per_group,
721*bb4ee6a4SAndroid Build Coastguard Worker             inodes_per_group: 1024,
722*bb4ee6a4SAndroid Build Coastguard Worker             size: BLOCK_SIZE * blocks_per_group * num_groups,
723*bb4ee6a4SAndroid Build Coastguard Worker             root_dir: Some(dir.clone()),
724*bb4ee6a4SAndroid Build Coastguard Worker         },
725*bb4ee6a4SAndroid Build Coastguard Worker     );
726*bb4ee6a4SAndroid Build Coastguard Worker 
727*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(Default::default()));
728*bb4ee6a4SAndroid Build Coastguard Worker }
729*bb4ee6a4SAndroid Build Coastguard Worker 
730*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_mkfs_xattr()731*bb4ee6a4SAndroid Build Coastguard Worker fn test_mkfs_xattr() {
732*bb4ee6a4SAndroid Build Coastguard Worker     // Since tmpfs doesn't support xattr, use the current directory.
733*bb4ee6a4SAndroid Build Coastguard Worker     let td = tempdir_in(".").unwrap();
734*bb4ee6a4SAndroid Build Coastguard Worker     let dir = td.path().join("testdata");
735*bb4ee6a4SAndroid Build Coastguard Worker     // testdata
736*bb4ee6a4SAndroid Build Coastguard Worker     // ├── a.txt ("user.foo"="a", "user.bar"="0123456789")
737*bb4ee6a4SAndroid Build Coastguard Worker     // ├── b.txt ("security.selinux"="unconfined_u:object_r:user_home_t:s0")
738*bb4ee6a4SAndroid Build Coastguard Worker     // ├── c.txt (no xattr)
739*bb4ee6a4SAndroid Build Coastguard Worker     // └── dir/ ("user.foo"="directory")
740*bb4ee6a4SAndroid Build Coastguard Worker     //     └─ d.txt ("user.foo"="in_directory")
741*bb4ee6a4SAndroid Build Coastguard Worker     std::fs::create_dir(&dir).unwrap();
742*bb4ee6a4SAndroid Build Coastguard Worker 
743*bb4ee6a4SAndroid Build Coastguard Worker     let dir_xattrs = vec![("dir".to_string(), vec![("user.foo", "directory")])];
744*bb4ee6a4SAndroid Build Coastguard Worker     let file_xattrs = vec![
745*bb4ee6a4SAndroid Build Coastguard Worker         (
746*bb4ee6a4SAndroid Build Coastguard Worker             "a.txt".to_string(),
747*bb4ee6a4SAndroid Build Coastguard Worker             vec![("user.foo", "a"), ("user.number", "0123456789")],
748*bb4ee6a4SAndroid Build Coastguard Worker         ),
749*bb4ee6a4SAndroid Build Coastguard Worker         (
750*bb4ee6a4SAndroid Build Coastguard Worker             "b.txt".to_string(),
751*bb4ee6a4SAndroid Build Coastguard Worker             vec![("security.selinux", "unconfined_u:object_r:user_home_t:s0")],
752*bb4ee6a4SAndroid Build Coastguard Worker         ),
753*bb4ee6a4SAndroid Build Coastguard Worker         ("c.txt".to_string(), vec![]),
754*bb4ee6a4SAndroid Build Coastguard Worker         ("dir/d.txt".to_string(), vec![("user.foo", "in_directory")]),
755*bb4ee6a4SAndroid Build Coastguard Worker     ];
756*bb4ee6a4SAndroid Build Coastguard Worker 
757*bb4ee6a4SAndroid Build Coastguard Worker     // Create dirs
758*bb4ee6a4SAndroid Build Coastguard Worker     for (fname, xattrs) in &dir_xattrs {
759*bb4ee6a4SAndroid Build Coastguard Worker         let f_path = dir.join(fname);
760*bb4ee6a4SAndroid Build Coastguard Worker         std::fs::create_dir(&f_path).unwrap();
761*bb4ee6a4SAndroid Build Coastguard Worker         for (key, value) in xattrs {
762*bb4ee6a4SAndroid Build Coastguard Worker             ext2::set_xattr(&f_path, key, value).unwrap();
763*bb4ee6a4SAndroid Build Coastguard Worker         }
764*bb4ee6a4SAndroid Build Coastguard Worker     }
765*bb4ee6a4SAndroid Build Coastguard Worker     // Create files
766*bb4ee6a4SAndroid Build Coastguard Worker     for (fname, xattrs) in &file_xattrs {
767*bb4ee6a4SAndroid Build Coastguard Worker         let f_path = dir.join(fname);
768*bb4ee6a4SAndroid Build Coastguard Worker         File::create(&f_path).unwrap();
769*bb4ee6a4SAndroid Build Coastguard Worker         for (key, value) in xattrs {
770*bb4ee6a4SAndroid Build Coastguard Worker             ext2::set_xattr(&f_path, key, value).unwrap();
771*bb4ee6a4SAndroid Build Coastguard Worker         }
772*bb4ee6a4SAndroid Build Coastguard Worker     }
773*bb4ee6a4SAndroid Build Coastguard Worker 
774*bb4ee6a4SAndroid Build Coastguard Worker     let xattr_map: BTreeMap<String, Vec<(&str, &str)>> =
775*bb4ee6a4SAndroid Build Coastguard Worker         file_xattrs.into_iter().chain(dir_xattrs).collect();
776*bb4ee6a4SAndroid Build Coastguard Worker 
777*bb4ee6a4SAndroid Build Coastguard Worker     let builder = Builder {
778*bb4ee6a4SAndroid Build Coastguard Worker         root_dir: Some(dir.clone()),
779*bb4ee6a4SAndroid Build Coastguard Worker         ..Default::default()
780*bb4ee6a4SAndroid Build Coastguard Worker     };
781*bb4ee6a4SAndroid Build Coastguard Worker     let disk = mkfs(&td, builder);
782*bb4ee6a4SAndroid Build Coastguard Worker 
783*bb4ee6a4SAndroid Build Coastguard Worker     assert_eq_dirs(&td, &dir, &disk, Some(xattr_map));
784*bb4ee6a4SAndroid Build Coastguard Worker }
785