1use std::fmt::{self, Debug};
5use std::mem;
6use std::os::unix::io::AsRawFd;
7#[cfg(not(any(target_os = "linux", target_os = "android")))]
8use std::ffi::CStr;
9
10use crate::{NixPath, Result, errno::Errno};
11
12#[cfg(target_os = "android")]
14pub type fsid_t = libc::__fsid_t;
15#[cfg(not(target_os = "android"))]
17pub type fsid_t = libc::fsid_t;
18
19#[derive(Clone, Copy)]
21#[repr(transparent)]
22pub struct Statfs(libc::statfs);
23
24#[cfg(target_os = "freebsd")]
25type fs_type_t = u32;
26#[cfg(target_os = "android")]
27type fs_type_t = libc::c_ulong;
28#[cfg(all(target_os = "linux", target_arch = "s390x"))]
29type fs_type_t = libc::c_uint;
30#[cfg(all(target_os = "linux", target_env = "musl"))]
31type fs_type_t = libc::c_ulong;
32#[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
33type fs_type_t = libc::__fsword_t;
34
35#[cfg(any(
37 target_os = "freebsd",
38 target_os = "android",
39 all(target_os = "linux", target_arch = "s390x"),
40 all(target_os = "linux", target_env = "musl"),
41 all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))),
42))]
43#[derive(Eq, Copy, Clone, PartialEq, Debug)]
44pub struct FsType(pub fs_type_t);
45
46#[cfg(all(target_os = "linux", not(target_env = "musl")))]
49#[allow(missing_docs)]
50pub const ADFS_SUPER_MAGIC: FsType = FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
51#[cfg(all(target_os = "linux", not(target_env = "musl")))]
52#[allow(missing_docs)]
53pub const AFFS_SUPER_MAGIC: FsType = FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
54#[cfg(all(target_os = "linux", not(target_env = "musl")))]
55#[allow(missing_docs)]
56pub const CODA_SUPER_MAGIC: FsType = FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
57#[cfg(all(target_os = "linux", not(target_env = "musl")))]
58#[allow(missing_docs)]
59pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t);
60#[cfg(all(target_os = "linux", not(target_env = "musl")))]
61#[allow(missing_docs)]
62pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t);
63#[cfg(all(target_os = "linux", not(target_env = "musl")))]
64#[allow(missing_docs)]
65pub const EXT2_SUPER_MAGIC: FsType = FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
66#[cfg(all(target_os = "linux", not(target_env = "musl")))]
67#[allow(missing_docs)]
68pub const EXT3_SUPER_MAGIC: FsType = FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
69#[cfg(all(target_os = "linux", not(target_env = "musl")))]
70#[allow(missing_docs)]
71pub const EXT4_SUPER_MAGIC: FsType = FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
72#[cfg(all(target_os = "linux", not(target_env = "musl")))]
73#[allow(missing_docs)]
74pub const HPFS_SUPER_MAGIC: FsType = FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
75#[cfg(all(target_os = "linux", not(target_env = "musl")))]
76#[allow(missing_docs)]
77pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t);
78#[cfg(all(target_os = "linux", not(target_env = "musl")))]
79#[allow(missing_docs)]
80pub const ISOFS_SUPER_MAGIC: FsType = FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
81#[cfg(all(target_os = "linux", not(target_env = "musl")))]
82#[allow(missing_docs)]
83pub const JFFS2_SUPER_MAGIC: FsType = FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
84#[cfg(all(target_os = "linux", not(target_env = "musl")))]
85#[allow(missing_docs)]
86pub const MINIX_SUPER_MAGIC: FsType = FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
87#[cfg(all(target_os = "linux", not(target_env = "musl")))]
88#[allow(missing_docs)]
89pub const MINIX_SUPER_MAGIC2: FsType = FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
90#[cfg(all(target_os = "linux", not(target_env = "musl")))]
91#[allow(missing_docs)]
92pub const MINIX2_SUPER_MAGIC: FsType = FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
93#[cfg(all(target_os = "linux", not(target_env = "musl")))]
94#[allow(missing_docs)]
95pub const MINIX2_SUPER_MAGIC2: FsType = FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
96#[cfg(all(target_os = "linux", not(target_env = "musl")))]
97#[allow(missing_docs)]
98pub const MSDOS_SUPER_MAGIC: FsType = FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
99#[cfg(all(target_os = "linux", not(target_env = "musl")))]
100#[allow(missing_docs)]
101pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t);
102#[cfg(all(target_os = "linux", not(target_env = "musl")))]
103#[allow(missing_docs)]
104pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t);
105#[cfg(all(target_os = "linux", not(target_env = "musl")))]
106#[allow(missing_docs)]
107pub const OPENPROM_SUPER_MAGIC: FsType = FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
108#[cfg(all(target_os = "linux", not(target_env = "musl")))]
109#[allow(missing_docs)]
110pub const OVERLAYFS_SUPER_MAGIC: FsType = FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
111#[cfg(all(target_os = "linux", not(target_env = "musl")))]
112#[allow(missing_docs)]
113pub const PROC_SUPER_MAGIC: FsType = FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
114#[cfg(all(target_os = "linux", not(target_env = "musl")))]
115#[allow(missing_docs)]
116pub const QNX4_SUPER_MAGIC: FsType = FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
117#[cfg(all(target_os = "linux", not(target_env = "musl")))]
118#[allow(missing_docs)]
119pub const REISERFS_SUPER_MAGIC: FsType = FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
120#[cfg(all(target_os = "linux", not(target_env = "musl")))]
121#[allow(missing_docs)]
122pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t);
123#[cfg(all(target_os = "linux", not(target_env = "musl")))]
124#[allow(missing_docs)]
125pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t);
126#[cfg(all(target_os = "linux", not(target_env = "musl")))]
127#[allow(missing_docs)]
128pub const USBDEVICE_SUPER_MAGIC: FsType = FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
129#[cfg(all(target_os = "linux", not(target_env = "musl")))]
130#[allow(missing_docs)]
131pub const CGROUP_SUPER_MAGIC: FsType = FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
132#[cfg(all(target_os = "linux", not(target_env = "musl")))]
133#[allow(missing_docs)]
134pub const CGROUP2_SUPER_MAGIC: FsType = FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
135
136
137impl Statfs {
138 #[cfg(not(any(
140 target_os = "openbsd",
141 target_os = "dragonfly",
142 target_os = "ios",
143 target_os = "macos"
144 )))]
145 pub fn filesystem_type(&self) -> FsType {
146 FsType(self.0.f_type)
147 }
148
149 #[cfg(not(any(target_os = "linux", target_os = "android")))]
151 pub fn filesystem_type_name(&self) -> &str {
152 let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) };
153 c_str.to_str().unwrap()
154 }
155
156 #[cfg(any(target_os = "ios", target_os = "macos"))]
158 pub fn optimal_transfer_size(&self) -> i32 {
159 self.0.f_iosize
160 }
161
162 #[cfg(target_os = "openbsd")]
164 pub fn optimal_transfer_size(&self) -> u32 {
165 self.0.f_iosize
166 }
167
168 #[cfg(all(target_os = "linux", target_arch = "s390x"))]
170 pub fn optimal_transfer_size(&self) -> u32 {
171 self.0.f_bsize
172 }
173
174 #[cfg(any(
176 target_os = "android",
177 all(target_os = "linux", target_env = "musl")
178 ))]
179 pub fn optimal_transfer_size(&self) -> libc::c_ulong {
180 self.0.f_bsize
181 }
182
183 #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
185 pub fn optimal_transfer_size(&self) -> libc::__fsword_t {
186 self.0.f_bsize
187 }
188
189 #[cfg(target_os = "dragonfly")]
191 pub fn optimal_transfer_size(&self) -> libc::c_long {
192 self.0.f_iosize
193 }
194
195 #[cfg(target_os = "freebsd")]
197 pub fn optimal_transfer_size(&self) -> u64 {
198 self.0.f_iosize
199 }
200
201 #[cfg(any(target_os = "ios", target_os = "macos", target_os = "openbsd"))]
203 pub fn block_size(&self) -> u32 {
204 self.0.f_bsize
205 }
206
207 #[cfg(all(target_os = "linux", target_arch = "s390x"))]
210 pub fn block_size(&self) -> u32 {
211 self.0.f_bsize
212 }
213
214 #[cfg(all(target_os = "linux", target_env = "musl"))]
217 pub fn block_size(&self) -> libc::c_ulong {
218 self.0.f_bsize
219 }
220
221 #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
224 pub fn block_size(&self) -> libc::__fsword_t {
225 self.0.f_bsize
226 }
227
228 #[cfg(target_os = "freebsd")]
230 pub fn block_size(&self) -> u64 {
231 self.0.f_bsize
232 }
233
234 #[cfg(target_os = "android")]
236 pub fn block_size(&self) -> libc::c_ulong {
237 self.0.f_bsize
238 }
239
240 #[cfg(target_os = "dragonfly")]
242 pub fn block_size(&self) -> libc::c_long {
243 self.0.f_bsize
244 }
245
246 #[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
248 pub fn maximum_name_length(&self) -> u32 {
249 self.0.f_namemax
250 }
251
252 #[cfg(all(target_os = "linux", target_arch = "s390x"))]
254 pub fn maximum_name_length(&self) -> u32 {
255 self.0.f_namelen
256 }
257
258 #[cfg(all(target_os = "linux", target_env = "musl"))]
260 pub fn maximum_name_length(&self) -> libc::c_ulong {
261 self.0.f_namelen
262 }
263
264 #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
266 pub fn maximum_name_length(&self) -> libc::__fsword_t {
267 self.0.f_namelen
268 }
269
270 #[cfg(target_os = "android")]
272 pub fn maximum_name_length(&self) -> libc::c_ulong {
273 self.0.f_namelen
274 }
275
276 #[cfg(any(
278 target_os = "ios",
279 target_os = "macos",
280 target_os = "android",
281 target_os = "freebsd",
282 target_os = "openbsd",
283 ))]
284 pub fn blocks(&self) -> u64 {
285 self.0.f_blocks
286 }
287
288 #[cfg(target_os = "dragonfly")]
290 pub fn blocks(&self) -> libc::c_long {
291 self.0.f_blocks
292 }
293
294 #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
296 pub fn blocks(&self) -> u64 {
297 self.0.f_blocks
298 }
299
300 #[cfg(not(any(
302 target_os = "ios",
303 target_os = "macos",
304 target_os = "android",
305 target_os = "freebsd",
306 target_os = "openbsd",
307 target_os = "dragonfly",
308 all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
309 )))]
310 pub fn blocks(&self) -> libc::c_ulong {
311 self.0.f_blocks
312 }
313
314 #[cfg(any(
316 target_os = "ios",
317 target_os = "macos",
318 target_os = "android",
319 target_os = "freebsd",
320 target_os = "openbsd",
321 ))]
322 pub fn blocks_free(&self) -> u64 {
323 self.0.f_bfree
324 }
325
326 #[cfg(target_os = "dragonfly")]
328 pub fn blocks_free(&self) -> libc::c_long {
329 self.0.f_bfree
330 }
331
332 #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
334 pub fn blocks_free(&self) -> u64 {
335 self.0.f_bfree
336 }
337
338 #[cfg(not(any(
340 target_os = "ios",
341 target_os = "macos",
342 target_os = "android",
343 target_os = "freebsd",
344 target_os = "openbsd",
345 target_os = "dragonfly",
346 all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
347 )))]
348 pub fn blocks_free(&self) -> libc::c_ulong {
349 self.0.f_bfree
350 }
351
352 #[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))]
354 pub fn blocks_available(&self) -> u64 {
355 self.0.f_bavail
356 }
357
358 #[cfg(target_os = "dragonfly")]
360 pub fn blocks_available(&self) -> libc::c_long {
361 self.0.f_bavail
362 }
363
364 #[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
366 pub fn blocks_available(&self) -> i64 {
367 self.0.f_bavail
368 }
369
370 #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
372 pub fn blocks_available(&self) -> u64 {
373 self.0.f_bavail
374 }
375
376 #[cfg(not(any(
378 target_os = "ios",
379 target_os = "macos",
380 target_os = "android",
381 target_os = "freebsd",
382 target_os = "openbsd",
383 target_os = "dragonfly",
384 all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
385 )))]
386 pub fn blocks_available(&self) -> libc::c_ulong {
387 self.0.f_bavail
388 }
389
390 #[cfg(any(
392 target_os = "ios",
393 target_os = "macos",
394 target_os = "android",
395 target_os = "freebsd",
396 target_os = "openbsd",
397 ))]
398 pub fn files(&self) -> u64 {
399 self.0.f_files
400 }
401
402 #[cfg(target_os = "dragonfly")]
404 pub fn files(&self) -> libc::c_long {
405 self.0.f_files
406 }
407
408 #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
410 pub fn files(&self) -> libc::fsfilcnt_t {
411 self.0.f_files
412 }
413
414 #[cfg(not(any(
416 target_os = "ios",
417 target_os = "macos",
418 target_os = "android",
419 target_os = "freebsd",
420 target_os = "openbsd",
421 target_os = "dragonfly",
422 all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
423 )))]
424 pub fn files(&self) -> libc::c_ulong {
425 self.0.f_files
426 }
427
428 #[cfg(any(
430 target_os = "android",
431 target_os = "ios",
432 target_os = "macos",
433 target_os = "openbsd"
434 ))]
435 pub fn files_free(&self) -> u64 {
436 self.0.f_ffree
437 }
438
439 #[cfg(target_os = "dragonfly")]
441 pub fn files_free(&self) -> libc::c_long {
442 self.0.f_ffree
443 }
444
445 #[cfg(target_os = "freebsd")]
447 pub fn files_free(&self) -> i64 {
448 self.0.f_ffree
449 }
450
451 #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
453 pub fn files_free(&self) -> libc::fsfilcnt_t {
454 self.0.f_ffree
455 }
456
457 #[cfg(not(any(
459 target_os = "ios",
460 target_os = "macos",
461 target_os = "android",
462 target_os = "freebsd",
463 target_os = "openbsd",
464 target_os = "dragonfly",
465 all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
466 )))]
467 pub fn files_free(&self) -> libc::c_ulong {
468 self.0.f_ffree
469 }
470
471 pub fn filesystem_id(&self) -> fsid_t {
473 self.0.f_fsid
474 }
475}
476
477impl Debug for Statfs {
478 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
479 f.debug_struct("Statfs")
480 .field("optimal_transfer_size", &self.optimal_transfer_size())
481 .field("block_size", &self.block_size())
482 .field("blocks", &self.blocks())
483 .field("blocks_free", &self.blocks_free())
484 .field("blocks_available", &self.blocks_available())
485 .field("files", &self.files())
486 .field("files_free", &self.files_free())
487 .field("filesystem_id", &self.filesystem_id())
488 .finish()
489 }
490}
491
492pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> {
501 unsafe {
502 let mut stat = mem::MaybeUninit::<libc::statfs>::uninit();
503 let res = path.with_nix_path(|path| libc::statfs(path.as_ptr(), stat.as_mut_ptr()))?;
504 Errno::result(res).map(|_| Statfs(stat.assume_init()))
505 }
506}
507
508pub fn fstatfs<T: AsRawFd>(fd: &T) -> Result<Statfs> {
517 unsafe {
518 let mut stat = mem::MaybeUninit::<libc::statfs>::uninit();
519 Errno::result(libc::fstatfs(fd.as_raw_fd(), stat.as_mut_ptr()))
520 .map(|_| Statfs(stat.assume_init()))
521 }
522}
523
524#[cfg(test)]
525mod test {
526 use std::fs::File;
527
528 use crate::sys::statfs::*;
529 use crate::sys::statvfs::*;
530 use std::path::Path;
531
532 #[test]
533 fn statfs_call() {
534 check_statfs("/tmp");
535 check_statfs("/dev");
536 check_statfs("/run");
537 check_statfs("/");
538 }
539
540 #[test]
541 fn fstatfs_call() {
542 check_fstatfs("/tmp");
543 check_fstatfs("/dev");
544 check_fstatfs("/run");
545 check_fstatfs("/");
546 }
547
548 fn check_fstatfs(path: &str) {
549 if !Path::new(path).exists() {
550 return;
551 }
552 let vfs = statvfs(path.as_bytes()).unwrap();
553 let file = File::open(path).unwrap();
554 let fs = fstatfs(&file).unwrap();
555 assert_fs_equals(fs, vfs);
556 }
557
558 fn check_statfs(path: &str) {
559 if !Path::new(path).exists() {
560 return;
561 }
562 let vfs = statvfs(path.as_bytes()).unwrap();
563 let fs = statfs(path.as_bytes()).unwrap();
564 assert_fs_equals(fs, vfs);
565 }
566
567 #[allow(clippy::unnecessary_cast)]
569 fn assert_fs_equals(fs: Statfs, vfs: Statvfs) {
570 assert_eq!(fs.files() as u64, vfs.files() as u64);
571 assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
572 assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
573 }
574
575 #[test]
578 #[ignore]
579 fn statfs_call_strict() {
580 check_statfs_strict("/tmp");
581 check_statfs_strict("/dev");
582 check_statfs_strict("/run");
583 check_statfs_strict("/");
584 }
585
586 #[test]
589 #[ignore]
590 fn fstatfs_call_strict() {
591 check_fstatfs_strict("/tmp");
592 check_fstatfs_strict("/dev");
593 check_fstatfs_strict("/run");
594 check_fstatfs_strict("/");
595 }
596
597 fn check_fstatfs_strict(path: &str) {
598 if !Path::new(path).exists() {
599 return;
600 }
601 let vfs = statvfs(path.as_bytes());
602 let file = File::open(path).unwrap();
603 let fs = fstatfs(&file);
604 assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
605 }
606
607 fn check_statfs_strict(path: &str) {
608 if !Path::new(path).exists() {
609 return;
610 }
611 let vfs = statvfs(path.as_bytes());
612 let fs = statfs(path.as_bytes());
613 assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
614 }
615
616 #[allow(clippy::unnecessary_cast)]
618 fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) {
619 assert_eq!(fs.files_free() as u64, vfs.files_free() as u64);
620 assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64);
621 assert_eq!(fs.blocks_available() as u64, vfs.blocks_available() as u64);
622 assert_eq!(fs.files() as u64, vfs.files() as u64);
623 assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
624 assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
625 }
626}