file_integrity/src/file_io.rs
Lines
97.30 %
Functions
80.00 %
Regions
92.86 %
Line | Count | Source (jump to first uncovered line) |
---|---|---|
1 |
//! Low-level file I/O operations that don't involve xattrs. |
|
2 | ||
3 |
// Copyright 2023 Zack Weinberg |
|
4 |
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 |
// you may not use this file except in compliance with the License. |
|
6 |
// You may obtain a copy of the License at |
|
7 |
// https://www.apache.org/licenses/LICENSE-2.0 |
|
8 | ||
9 |
#[cfg()] |
|
10 |
#[path = "test/file_io.rs"] |
|
11 |
mod test; |
|
12 | ||
13 |
use ::::File; |
|
14 |
use ::io; |
|
15 |
use ::::::AsRawFd; |
|
16 |
use ::::SystemTime; |
|
17 | ||
18 |
use ::{,}; |
|
19 | ||
20 |
/// Succeeds if the file F was opened with O_RDONLY rather than |
|
21 |
/// O_WRONLY or O_RDWR, fails otherwise. |
|
22 | 6 |
pub(crate) fn require_read_only_fd(f: &) -> ::<()> { |
23 | 6 |
// SAFETY: trusts you to supply a valid fd, manual error checking |
24 | 6 |
// required. Various other safety issues are not relevant when |
25 | 6 |
// the second argument is F_GETFL. |
26 | 6 |
let= unsafe { ::(as_raw_fd(), ::F_GETFL) }; |
27 | 6 |
if== -1 { |
28 | 2 |
return Err(::::()); |
29 | 4 |
}
|
30 | 4 |
if (& ::O_ACCMODE) != ::O_RDONLY { |
31 | 2 |
return Err(::::(::EINVAL)); |
32 | 2 |
}
|
33 | 2 |
Ok(()) |
34 | 6 |
}
|
35 | ||
36 |
/// Take an advisory read lock on a file, using flock(). The lock |
|
37 |
/// will be released when the file is closed (most precisely when the |
|
38 |
/// OS-level "open file description" is closed). |
|
39 |
///
|
|
40 |
/// This function waits for the lock to be granted and therefore may |
|
41 |
/// take a very long time to return. |
|
42 |
///
|
|
43 |
/// # Bugs |
|
44 |
///
|
|
45 |
/// Depending on the OS and the file system, this lock may or may not |
|
46 |
/// block programs that use fcntl() instead of flock() to lock files. |
|
47 | 3588 |
fn read_lock(f: &) -> ::<()> { |
48 | 3588 |
// SAFETY: trusts you to supply a valid fd, manual error checking required |
49 | 3588 |
let= unsafe { ::(as_raw_fd(), ::LOCK_SH) }; |
50 | 3588 |
if!= 0 { |
51 | 3 |
Err(::::()) |
52 |
} else { |
|
53 | 3585 |
Ok(()) |
54 |
}
|
|
55 | 3588 |
}
|
56 | ||
57 |
/// Subroutine of checked_file::wrap: Given an open file, look up its |
|
58 |
/// last modification time, lock it for reading, and create an mmap |
|
59 |
/// region covering the entire file. |
|
60 | 3585 |
pub(crate) fn mtime_and_contents( |
61 | 3585 |
file: & |
62 | 3585 |
) -> ::<()> { |
63 | 1 |
let=metadata()?; |
64 | ||
65 |
// Rust does not allow [u8] to have more than isize::MAX elements. |
|
66 |
// It's possible (particularly if usize is only 32 bits) for |
|
67 |
// md.len() to be larger than that. Reject such files now, |
|
68 |
// because the OS might allow mappings that are too large for |
|
69 |
// isize (but still fit in usize). |
|
70 | 3584 |
let: isize = |
71 | 3584 |
len() |
72 | 3584 |
try_into() |
73 | 3584 |
map_err(|| ::::(::EFBIG))?; |
74 | ||
75 | 1 |
read_lock()?; |
76 | ||
77 |
// SAFETY: File mappings are considered unsafe because the data |
|
78 |
// in the file _could_ be changed by another process during the |
|
79 |
// lifetime of the map, and the kernel _could_ expose that change |
|
80 |
// through the map (even for a MAP_PRIVATE mapping), violating |
|
81 |
// Rust's expectations that a &[u8] slice is immutable. |
|
82 |
//
|
|
83 |
// We presume that CheckedFiles are intended to be immutable on |
|
84 |
// disk (else what's the point of checking them?) so it should be |
|
85 |
// OK to trap the unsafety here and present a safe interface to |
|
86 |
// users of the crate. We further mitigate the risk by applying |
|
87 |
// an advisory read lock to the file and, at a higher level, |
|
88 |
// requiring the File object to be open exclusively for reading. |
|
89 |
// We don't, however, attempt to enforce that the file is read- |
|
90 |
// only on disk, because that would interfere with use of the |
|
91 |
// write_checksum() method, and because the answer to "is the file |
|
92 |
// read-only on disk" can, for most files, change at any moment. |
|
93 | 3582 |
let= unsafe { |
94 | 3583 |
::() |
95 | 3583 |
len(as usize) |
96 | 1 |
map_copy_read_only()? |
97 |
}; |
|
98 | ||
99 |
// As far as we can tell, it is impossible to make Metadata::modified() |
|
100 |
// fail on Unix. By using map() here, instead of ?, we avoid |
|
101 |
// having that show up as a lacuna in branch coverage. |
|
102 |
//
|
|
103 |
// In case we ever get fancier coverage testing that can catch the |
|
104 |
// lacuna, this function has been placed at the very bottom of |
|
105 |
// this file. That keeps the lacuna out of the the way of the |
|
106 |
// "jump to first uncovered line" feature in llvm-cov output. |
|
107 | 3582 |
modified()map(|actual_mtime| (,)) |
108 | 3585 |
}
|