cap_primitives/fs/
hard_link.rs

1//! This defines `hard_link`, the primary entrypoint to sandboxed hard-link
2//! creation.
3
4use crate::fs::hard_link_impl;
5#[cfg(racy_asserts)]
6use crate::fs::{
7    canonicalize, hard_link_unchecked, map_result, stat_unchecked, FollowSymlinks, Metadata,
8};
9use std::path::Path;
10use std::{fs, io};
11
12/// Perform a `linkat`-like operation, ensuring that the resolution of the path
13/// never escapes the directory tree rooted at `start`.
14#[cfg_attr(not(racy_asserts), allow(clippy::let_and_return))]
15#[inline]
16pub fn hard_link(
17    old_start: &fs::File,
18    old_path: &Path,
19    new_start: &fs::File,
20    new_path: &Path,
21) -> io::Result<()> {
22    #[cfg(racy_asserts)]
23    let (old_metadata_before, new_metadata_before) = (
24        stat_unchecked(old_start, old_path, FollowSymlinks::No),
25        stat_unchecked(new_start, new_path, FollowSymlinks::No),
26    );
27
28    // Call the underlying implementation.
29    let result = hard_link_impl(old_start, old_path, new_start, new_path);
30
31    #[cfg(racy_asserts)]
32    let (old_metadata_after, new_metadata_after) = (
33        stat_unchecked(old_start, old_path, FollowSymlinks::No),
34        stat_unchecked(new_start, new_path, FollowSymlinks::No),
35    );
36
37    #[cfg(racy_asserts)]
38    check_hard_link(
39        old_start,
40        old_path,
41        new_start,
42        new_path,
43        &old_metadata_before,
44        &new_metadata_before,
45        &result,
46        &old_metadata_after,
47        &new_metadata_after,
48    );
49
50    result
51}
52
53#[cfg(racy_asserts)]
54#[allow(clippy::too_many_arguments)]
55#[allow(clippy::enum_glob_use)]
56fn check_hard_link(
57    old_start: &fs::File,
58    old_path: &Path,
59    new_start: &fs::File,
60    new_path: &Path,
61    old_metadata_before: &io::Result<Metadata>,
62    new_metadata_before: &io::Result<Metadata>,
63    result: &io::Result<()>,
64    old_metadata_after: &io::Result<Metadata>,
65    new_metadata_after: &io::Result<Metadata>,
66) {
67    use io::ErrorKind::*;
68
69    match (
70        map_result(old_metadata_before),
71        map_result(new_metadata_before),
72        map_result(result),
73        map_result(old_metadata_after),
74        map_result(new_metadata_after),
75    ) {
76        (
77            Ok(old_metadata_before),
78            Err((NotFound, _)),
79            Ok(()),
80            Ok(old_metadata_after),
81            Ok(new_metadata_after),
82        ) => {
83            assert_same_file_metadata!(old_metadata_before, old_metadata_after);
84            assert_same_file_metadata!(old_metadata_before, new_metadata_after);
85        }
86
87        (_, Ok(new_metadata_before), Err((AlreadyExists, _)), _, Ok(new_metadata_after)) => {
88            assert_same_file_metadata!(&new_metadata_before, &new_metadata_after);
89        }
90
91        (_, _, Err((_kind, _message)), _, _) => match (
92            map_result(&canonicalize(old_start, old_path)),
93            map_result(&canonicalize(new_start, new_path)),
94        ) {
95            (Ok(old_canon), Ok(new_canon)) => match map_result(&hard_link_unchecked(
96                old_start, &old_canon, new_start, &new_canon,
97            )) {
98                Err((_unchecked_kind, _unchecked_message)) => {
99                    /* TODO: Check error messages.
100                    assert_eq!(kind, unchecked_kind);
101                    assert_eq!(message, unchecked_message);
102                    */
103                }
104                _ => panic!("unsandboxed link success"),
105            },
106            (Err((_old_canon_kind, _old_canon_message)), _) => {
107                /* TODO: Check error messages.
108                assert_eq!(kind, old_canon_kind);
109                assert_eq!(message, old_canon_message);
110                */
111            }
112            (_, Err((_new_canon_kind, _new_canon_message))) => {
113                /* TODO: Check error messages.
114                assert_eq!(kind, new_canon_kind);
115                assert_eq!(message, new_canon_message);
116                */
117            }
118        },
119
120        other => panic!(
121            "inconsistent link checks: old_start='{:?}', old_path='{}', new_start='{:?}', \
122             new_path='{}':\n{:#?}",
123            old_start,
124            old_path.display(),
125            new_start,
126            new_path.display(),
127            other
128        ),
129    }
130}