remote/protocol/
mod.rs

1use serde::{Deserialize, Serialize};
2use std::os::unix::fs::MetadataExt;
3use std::os::unix::prelude::PermissionsExt;
4
5#[derive(Debug, Deserialize, Serialize)]
6pub struct Metadata {
7    pub mode: u32,
8    pub uid: u32,
9    pub gid: u32,
10    pub atime: i64,
11    pub mtime: i64,
12    pub atime_nsec: i64,
13    pub mtime_nsec: i64,
14}
15
16impl common::preserve::Metadata for Metadata {
17    fn uid(&self) -> u32 {
18        self.uid
19    }
20    fn gid(&self) -> u32 {
21        self.gid
22    }
23    fn atime(&self) -> i64 {
24        self.atime
25    }
26    fn atime_nsec(&self) -> i64 {
27        self.atime_nsec
28    }
29    fn mtime(&self) -> i64 {
30        self.mtime
31    }
32    fn mtime_nsec(&self) -> i64 {
33        self.mtime_nsec
34    }
35    fn permissions(&self) -> std::fs::Permissions {
36        std::fs::Permissions::from_mode(self.mode)
37    }
38}
39
40impl common::preserve::Metadata for &Metadata {
41    fn uid(&self) -> u32 {
42        (*self).uid()
43    }
44    fn gid(&self) -> u32 {
45        (*self).gid()
46    }
47    fn atime(&self) -> i64 {
48        (*self).atime()
49    }
50    fn atime_nsec(&self) -> i64 {
51        (*self).atime_nsec()
52    }
53    fn mtime(&self) -> i64 {
54        (*self).mtime()
55    }
56    fn mtime_nsec(&self) -> i64 {
57        (*self).mtime_nsec()
58    }
59    fn permissions(&self) -> std::fs::Permissions {
60        (*self).permissions()
61    }
62}
63
64impl From<&std::fs::Metadata> for Metadata {
65    fn from(metadata: &std::fs::Metadata) -> Self {
66        Metadata {
67            mode: metadata.mode(),
68            uid: metadata.uid(),
69            gid: metadata.gid(),
70            atime: metadata.atime(),
71            mtime: metadata.mtime(),
72            atime_nsec: metadata.atime_nsec(),
73            mtime_nsec: metadata.mtime_nsec(),
74        }
75    }
76}
77
78// implies files contents will be sent immediately after receiving this object
79#[derive(Debug, Deserialize, Serialize)]
80pub struct File {
81    pub src: std::path::PathBuf,
82    pub dst: std::path::PathBuf,
83    pub size: u64,
84    pub metadata: Metadata,
85    pub is_root: bool,
86}
87
88// wrapper that includes size for comparison purposes
89#[derive(Debug)]
90pub struct FileMetadata<'a> {
91    pub metadata: &'a Metadata,
92    pub size: u64,
93}
94
95impl<'a> common::preserve::Metadata for FileMetadata<'a> {
96    fn uid(&self) -> u32 {
97        self.metadata.uid()
98    }
99    fn gid(&self) -> u32 {
100        self.metadata.gid()
101    }
102    fn atime(&self) -> i64 {
103        self.metadata.atime()
104    }
105    fn atime_nsec(&self) -> i64 {
106        self.metadata.atime_nsec()
107    }
108    fn mtime(&self) -> i64 {
109        self.metadata.mtime()
110    }
111    fn mtime_nsec(&self) -> i64 {
112        self.metadata.mtime_nsec()
113    }
114    fn permissions(&self) -> std::fs::Permissions {
115        self.metadata.permissions()
116    }
117    fn size(&self) -> u64 {
118        self.size
119    }
120}
121
122#[derive(Debug, Deserialize, Serialize)]
123pub enum SourceMessage {
124    DirStub {
125        src: std::path::PathBuf,
126        dst: std::path::PathBuf,
127        num_entries: usize,
128    },
129    Directory {
130        src: std::path::PathBuf,
131        dst: std::path::PathBuf,
132        metadata: Metadata,
133        is_root: bool,
134    },
135    // this message is useful to open the control stream when there are no directories
136    DirStructureComplete,
137    Symlink {
138        src: std::path::PathBuf,
139        dst: std::path::PathBuf,
140        target: std::path::PathBuf,
141        metadata: Metadata,
142        is_root: bool,
143    },
144    FileSkipped(SrcDst),    // file failed to send, decrement directory counter
145    SymlinkSkipped(SrcDst), // symlink failed to send, decrement directory counter
146    SourceDone,             // must be the last message sent by the source
147}
148
149#[derive(Clone, Debug, Deserialize, Serialize)]
150pub struct SrcDst {
151    pub src: std::path::PathBuf,
152    pub dst: std::path::PathBuf,
153}
154
155#[derive(Clone, Debug, Deserialize, Serialize)]
156pub enum DestinationMessage {
157    DirectoryCreated(SrcDst),
158    DirectoryComplete(SrcDst),
159    DirectoryFailed(SrcDst), // directory creation failed, source should skip its contents
160    DestinationDone,         // must be the last message sent by the destination
161}
162
163#[derive(Clone, Debug, Deserialize, Serialize)]
164pub struct RcpdConfig {
165    pub verbose: u8,
166    pub fail_early: bool,
167    pub max_workers: usize,
168    pub max_blocking_threads: usize,
169    pub max_open_files: Option<usize>,
170    pub ops_throttle: usize,
171    pub iops_throttle: usize,
172    pub chunk_size: usize,
173    // common::copy::Settings
174    pub dereference: bool,
175    pub overwrite: bool,
176    pub overwrite_compare: String,
177    pub debug_log_prefix: Option<String>,
178    pub quic_port_ranges: Option<String>,
179    pub progress: bool,
180    pub progress_delay: Option<String>,
181    pub remote_copy_conn_timeout_sec: u64,
182    /// SHA-256 fingerprint of the Master's TLS certificate (32 bytes)
183    /// Used for certificate pinning when rcpd connects to Master
184    pub master_cert_fingerprint: Vec<u8>,
185}
186
187impl RcpdConfig {
188    pub fn to_args(&self) -> Vec<String> {
189        let mut args = vec![
190            format!("--max-workers={}", self.max_workers),
191            format!("--max-blocking-threads={}", self.max_blocking_threads),
192            format!("--ops-throttle={}", self.ops_throttle),
193            format!("--iops-throttle={}", self.iops_throttle),
194            format!("--chunk-size={}", self.chunk_size),
195            format!("--overwrite-compare={}", self.overwrite_compare),
196        ];
197        if self.verbose > 0 {
198            args.push(format!("-{}", "v".repeat(self.verbose as usize)));
199        }
200        if self.fail_early {
201            args.push("--fail-early".to_string());
202        }
203        if let Some(v) = self.max_open_files {
204            args.push(format!("--max-open-files={v}"));
205        }
206        if self.dereference {
207            args.push("--dereference".to_string());
208        }
209        if self.overwrite {
210            args.push("--overwrite".to_string());
211        }
212        if let Some(ref prefix) = self.debug_log_prefix {
213            args.push(format!("--debug-log-prefix={prefix}"));
214        }
215        if let Some(ref ranges) = self.quic_port_ranges {
216            args.push(format!("--quic-port-ranges={ranges}"));
217        }
218        if self.progress {
219            args.push("--progress".to_string());
220        }
221        if let Some(ref delay) = self.progress_delay {
222            args.push(format!("--progress-delay={delay}"));
223        }
224        args.push(format!(
225            "--remote-copy-conn-timeout-sec={}",
226            self.remote_copy_conn_timeout_sec
227        ));
228        // pass master cert fingerprint as hex-encoded string
229        args.push(format!(
230            "--master-cert-fingerprint={}",
231            hex::encode(&self.master_cert_fingerprint)
232        ));
233        args
234    }
235}
236
237#[derive(Clone, Debug, Deserialize, Serialize)]
238pub struct TracingHello {}
239
240#[derive(Clone, Debug, Deserialize, Serialize)]
241pub enum MasterHello {
242    Source {
243        src: std::path::PathBuf,
244        dst: std::path::PathBuf,
245    },
246    Destination {
247        source_addr: std::net::SocketAddr,
248        server_name: String,
249        /// SHA-256 fingerprint of the source's TLS certificate (32 bytes)
250        /// Used for certificate pinning to prevent MITM attacks
251        source_cert_fingerprint: Vec<u8>,
252        preserve: common::preserve::Settings,
253    },
254}
255
256#[derive(Clone, Debug, Deserialize, Serialize)]
257pub struct SourceMasterHello {
258    pub source_addr: std::net::SocketAddr,
259    pub server_name: String,
260    /// SHA-256 fingerprint of this source's TLS certificate (32 bytes)
261    /// Used for certificate pinning to prevent MITM attacks
262    pub cert_fingerprint: Vec<u8>,
263}
264
265#[derive(Clone, Debug, Deserialize, Serialize)]
266pub enum RcpdResult {
267    Success {
268        message: String,
269        summary: common::copy::Summary,
270    },
271    Failure {
272        error: String,
273        summary: common::copy::Summary,
274    },
275}