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 quic_idle_timeout_sec: u64,
180    pub quic_keep_alive_interval_sec: u64,
181    pub progress: bool,
182    pub progress_delay: Option<String>,
183    pub remote_copy_conn_timeout_sec: u64,
184    /// SHA-256 fingerprint of the Master's TLS certificate (32 bytes)
185    /// Used for certificate pinning when rcpd connects to Master
186    pub master_cert_fingerprint: Vec<u8>,
187}
188
189impl RcpdConfig {
190    pub fn to_args(&self) -> Vec<String> {
191        let mut args = vec![
192            format!("--max-workers={}", self.max_workers),
193            format!("--max-blocking-threads={}", self.max_blocking_threads),
194            format!("--ops-throttle={}", self.ops_throttle),
195            format!("--iops-throttle={}", self.iops_throttle),
196            format!("--chunk-size={}", self.chunk_size),
197            format!("--overwrite-compare={}", self.overwrite_compare),
198        ];
199        if self.verbose > 0 {
200            args.push(format!("-{}", "v".repeat(self.verbose as usize)));
201        }
202        if self.fail_early {
203            args.push("--fail-early".to_string());
204        }
205        if let Some(v) = self.max_open_files {
206            args.push(format!("--max-open-files={v}"));
207        }
208        if self.dereference {
209            args.push("--dereference".to_string());
210        }
211        if self.overwrite {
212            args.push("--overwrite".to_string());
213        }
214        if let Some(ref prefix) = self.debug_log_prefix {
215            args.push(format!("--debug-log-prefix={prefix}"));
216        }
217        if let Some(ref ranges) = self.quic_port_ranges {
218            args.push(format!("--quic-port-ranges={ranges}"));
219        }
220        args.push(format!(
221            "--quic-idle-timeout-sec={}",
222            self.quic_idle_timeout_sec
223        ));
224        args.push(format!(
225            "--quic-keep-alive-interval-sec={}",
226            self.quic_keep_alive_interval_sec
227        ));
228        if self.progress {
229            args.push("--progress".to_string());
230        }
231        if let Some(ref delay) = self.progress_delay {
232            args.push(format!("--progress-delay={delay}"));
233        }
234        args.push(format!(
235            "--remote-copy-conn-timeout-sec={}",
236            self.remote_copy_conn_timeout_sec
237        ));
238        // pass master cert fingerprint as hex-encoded string
239        args.push(format!(
240            "--master-cert-fingerprint={}",
241            hex::encode(&self.master_cert_fingerprint)
242        ));
243        args
244    }
245}
246
247#[derive(Clone, Debug, Deserialize, Serialize)]
248pub struct TracingHello {}
249
250#[derive(Clone, Debug, Deserialize, Serialize)]
251pub enum MasterHello {
252    Source {
253        src: std::path::PathBuf,
254        dst: std::path::PathBuf,
255    },
256    Destination {
257        source_addr: std::net::SocketAddr,
258        server_name: String,
259        /// SHA-256 fingerprint of the source's TLS certificate (32 bytes)
260        /// Used for certificate pinning to prevent MITM attacks
261        source_cert_fingerprint: Vec<u8>,
262        preserve: common::preserve::Settings,
263    },
264}
265
266#[derive(Clone, Debug, Deserialize, Serialize)]
267pub struct SourceMasterHello {
268    pub source_addr: std::net::SocketAddr,
269    pub server_name: String,
270    /// SHA-256 fingerprint of this source's TLS certificate (32 bytes)
271    /// Used for certificate pinning to prevent MITM attacks
272    pub cert_fingerprint: Vec<u8>,
273}
274
275#[derive(Clone, Debug, Deserialize, Serialize)]
276pub enum RcpdResult {
277    Success {
278        message: String,
279        summary: common::copy::Summary,
280    },
281    Failure {
282        error: String,
283        summary: common::copy::Summary,
284    },
285}