1use std::{
2 ffi::{OsStr, OsString},
3 fs::File,
4 io::{Cursor, Error as IoError, ErrorKind, Read, Result as IoResult, Seek, SeekFrom},
5 mem::size_of,
6 num::NonZeroU64,
7 os::unix::ffi::{OsStrExt, OsStringExt},
8 path::Path,
9};
10
11mod balloc;
12mod dir;
13mod ialloc;
14mod inode;
15mod symlink;
16mod xattr;
17
18use crate::{
19 blockreader::{Backend, BlockReader},
20 data::*,
21 decoder::{Config, Decoder},
22};
23
24#[macro_export]
26macro_rules! err {
27 ($name:ident) => {
28 IoError::from_raw_os_error(libc::$name)
29 };
30}
31
32macro_rules! iobail {
33 ($kind:expr, $($tk:tt)+) => {
34 return Err(IoError::new($kind, format!($($tk)+)))
35 };
36}
37
38#[derive(Debug, Clone)]
40#[doc(alias = "Statfs")]
41pub struct Info {
42 pub blocks: u64,
44
45 pub bfree: u64,
47
48 pub files: u64,
50
51 pub ffree: u64,
53
54 pub bsize: u32,
56
57 pub fsize: u32,
59}
60
61pub struct Ufs<R: Backend> {
63 file: Decoder<BlockReader<R>>,
64 superblock: Superblock,
65}
66
67impl Ufs<File> {
68 pub fn open(path: &Path, rw: bool) -> IoResult<Self> {
69 let file = BlockReader::open(path, rw)?;
70 Self::new(file)
71 }
72}
73
74impl<R: Backend> Ufs<R> {
75 pub fn new(mut file: BlockReader<R>) -> IoResult<Self> {
76 let pos = SBLOCK_UFS2 as u64 + MAGIC_OFFSET;
77 file.seek(SeekFrom::Start(pos))?;
78 let mut magic = [0u8; 4];
79 file.read_exact(&mut magic)?;
80
81 let config = match magic {
83 [0x19, 0x01, 0x54, 0x19] => Config::little(),
84 [0x19, 0x54, 0x01, 0x19] => Config::big(),
85 _ => {
86 iobail!(
87 ErrorKind::InvalidInput,
88 "invalid superblock magic number: {magic:?}"
89 )
90 }
91 };
92 let mut file = Decoder::new(file, config);
95
96 let superblock: Superblock = file.decode_at(SBLOCK_UFS2 as u64)?;
97 if superblock.magic != FS_UFS2_MAGIC {
98 iobail!(
99 ErrorKind::InvalidInput,
100 "invalid superblock magic number: {}",
101 superblock.magic
102 );
103 }
104 let mut s = Self { file, superblock };
105 s.check()?;
106 Ok(s)
107 }
108
109 pub fn write_enabled(&self) -> bool {
110 self.file.inner().write_enabled()
111 }
112
113 fn assert_rw(&self) -> IoResult<()> {
114 if self.write_enabled() {
115 Ok(())
116 } else {
117 Err(err!(EROFS))
118 }
119 }
120
121 #[doc(alias("statfs", "statvfs"))]
123 pub fn info(&self) -> Info {
124 let sb = &self.superblock;
125 let cst = &sb.cstotal;
126 Info {
127 blocks: sb.dsize as u64,
128 bfree: (cst.nbfree * sb.frag as i64 + cst.nffree) as u64,
129 files: (sb.ipg * sb.ncg) as u64,
130 ffree: cst.nifree as u64,
131 bsize: sb.bsize as u32,
132 fsize: sb.fsize as u32,
133 }
134 }
135
136 fn check(&mut self) -> IoResult<()> {
137 let sb = &self.superblock;
138 log::debug!("Superblock: {sb:#?}");
139
140 log::info!("Summary:");
141 log::info!("Block Size: {}", sb.bsize);
142 log::info!("# Blocks: {}", sb.size);
143 log::info!("# Data Blocks: {}", sb.dsize);
144 log::info!("Fragment Size: {}", sb.fsize);
145 log::info!("Fragments per Block: {}", sb.frag);
146 log::info!("# Cylinder Groups: {}", sb.ncg);
147 log::info!("CG Size: {}MiB", sb.cgsize() / 1024 / 1024);
148
149 macro_rules! sbassert {
150 ($e:expr) => {
151 if !($e) {
152 log::error!("superblock corrupted: {}", stringify!($e));
153 return Err(IoError::from_raw_os_error(libc::EIO));
154 }
155 };
156 }
157
158 sbassert!(sb.ncg > 0);
159 sbassert!(sb.ipg > 0);
160 sbassert!(sb.fpg > 0);
161 sbassert!(sb.frag > 0 && sb.frag <= 8);
162 sbassert!(sb.fsize == (sb.bsize / sb.frag));
163 sbassert!(Some(sb.bsize) == 1i32.checked_shl(sb.bshift as u32));
165 sbassert!(Some(sb.fsize) == 1i32.checked_shl(sb.fshift as u32));
166 sbassert!(Some(sb.frag) == 1i32.checked_shl(sb.fragshift as u32));
167 sbassert!(sb.bsize == (!sb.bmask + 1));
168 sbassert!(sb.fsize == (!sb.fmask + 1));
169 sbassert!(sb.sbsize == sb.fsize);
170 sbassert!(sb.cgsize_struct() < sb.bsize as usize);
171
172 let fpg = sb.fpg as u64;
173 let sblkno = sb.sblkno as u64;
174 let fs = sb.fsize as u64;
175
176 for i in 0..sb.ncg {
178 let addr = (i as u64 * fpg + sblkno) * fs;
179 let csb: Superblock = self.file.decode_at(addr).unwrap();
180 if csb.magic != FS_UFS2_MAGIC {
181 log::error!("CG{i} has invalid superblock magic: {:x}", csb.magic);
182 return Err(err!(EIO));
183 }
184 }
185
186 for i in 0..self.superblock.ncg {
188 let addr = self.cg_addr(i as u64);
189 let cg: CylGroup = self.file.decode_at(addr).unwrap();
190 if cg.magic != CG_MAGIC {
191 log::error!("CG{i} has invalid cg magic: {:x}", cg.magic);
192 return Err(err!(EIO));
193 }
194 }
195 log::info!("OK");
196 Ok(())
197 }
198
199 fn cg_addr(&self, idx: u64) -> u64 {
200 let sb = &self.superblock;
201 let fpg = sb.fpg as u64;
202 let cblkno = sb.cblkno as u64;
203 let fs = sb.fsize as u64;
204
205 (idx * fpg + cblkno) * fs
206 }
207
208 fn update_sb(&mut self, f: impl FnOnce(&mut Superblock)) -> IoResult<()> {
209 f(&mut self.superblock);
211 self.file.encode_at(SBLOCK_UFS2 as u64, &self.superblock)?;
212 Ok(())
213 }
214}
215
216fn check_name_is_legal(name: &OsStr, allow_special: bool) -> IoResult<()> {
217 let b = name.as_encoded_bytes();
218
219 let x = b.contains(&b'/') ||
220 (name == "." && !allow_special) ||
221 (name == ".." && !allow_special) ||
222 b.contains(&b'\0');
223
224 if x {
225 Err(err!(EINVAL))
226 } else {
227 Ok(())
228 }
229}