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