1use std::io::{Read, Write};
9use std::os::unix::net::UnixStream;
10use std::time::Duration;
11
12use crate::client::{Client, encode_segment};
13use crate::error::{Error, Result};
14
15const INIT: u8 = 1;
16const VERSION: u8 = 2;
17const SETSTAT: u8 = 9;
18const STAT: u8 = 17;
19const STATUS: u8 = 101;
20const ATTRS: u8 = 105;
21const ATTR_SIZE: u32 = 0x1;
22const ATTR_UIDGID: u32 = 0x2;
23const ATTR_PERMISSIONS: u32 = 0x4;
24#[cfg(test)]
25const ATTR_ACMODTIME: u32 = 0x8;
26const NO_SUCH_FILE: u32 = 2;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct Stat {
31 pub uid: u32,
32 pub gid: u32,
33 pub mode: u32,
35}
36
37pub struct Sftp {
39 stream: UnixStream,
40 next_id: u32,
41 what: String,
42}
43
44impl Sftp {
45 pub fn open(client: &Client, instance: &str, timeout: Duration) -> Result<Self> {
47 let path = format!("/1.0/instances/{}/sftp", encode_segment(instance));
48 let stream = client.upgrade(&path, "sftp", timeout)?;
49 let mut s = Sftp {
50 stream,
51 next_id: 1,
52 what: format!("sftp to {instance}"),
53 };
54 s.send(INIT, &3u32.to_be_bytes())?;
55 let (t, _) = s.recv()?;
56 if t != VERSION {
57 return Err(s.protocol(format!("expected VERSION, got packet type {t}")));
58 }
59 Ok(s)
60 }
61
62 pub fn stat(&mut self, path: &str) -> Result<Option<Stat>> {
64 let id = self.request(STAT, path, &[])?;
65 let (t, body) = self.recv()?;
66 let mut r = Reader::new(&body);
67 if r.u32()? != id {
68 return Err(self.protocol("reply to another request".into()));
69 }
70 match t {
71 ATTRS => {
72 let flags = r.u32()?;
73 if flags & ATTR_SIZE != 0 {
74 r.u64()?;
75 }
76 if flags & ATTR_UIDGID == 0 || flags & ATTR_PERMISSIONS == 0 {
77 return Err(self.protocol(format!("stat {path}: no owner or mode")));
78 }
79 let (uid, gid) = (r.u32()?, r.u32()?);
80 let mode = r.u32()? & 0o7777;
81 Ok(Some(Stat { uid, gid, mode }))
82 }
83 STATUS => match r.u32()? {
84 NO_SUCH_FILE => Ok(None),
85 code => Err(self.failed(&format!("stat {path}"), code, &mut r)),
86 },
87 t => Err(self.protocol(format!("unexpected packet type {t}"))),
88 }
89 }
90
91 pub fn chown(&mut self, path: &str, uid: u32, gid: u32) -> Result<()> {
93 let mut a = ATTR_UIDGID.to_be_bytes().to_vec();
94 a.extend(uid.to_be_bytes());
95 a.extend(gid.to_be_bytes());
96 self.setstat(path, &a, &format!("chown {uid}:{gid} {path}"))
97 }
98
99 pub fn chmod(&mut self, path: &str, mode: u32) -> Result<()> {
101 let mut a = ATTR_PERMISSIONS.to_be_bytes().to_vec();
102 a.extend((mode & 0o7777).to_be_bytes());
103 self.setstat(path, &a, &format!("chmod {mode:04o} {path}"))
104 }
105
106 fn setstat(&mut self, path: &str, attrs: &[u8], what: &str) -> Result<()> {
107 let id = self.request(SETSTAT, path, attrs)?;
108 let (t, body) = self.recv()?;
109 let mut r = Reader::new(&body);
110 if t != STATUS || r.u32()? != id {
111 return Err(self.protocol(format!("{what}: unexpected reply type {t}")));
112 }
113 match r.u32()? {
114 0 => Ok(()),
115 code => Err(self.failed(what, code, &mut r)),
116 }
117 }
118
119 fn request(&mut self, kind: u8, path: &str, rest: &[u8]) -> Result<u32> {
120 let id = self.next_id;
121 self.next_id += 1;
122 let mut body = id.to_be_bytes().to_vec();
123 body.extend((path.len() as u32).to_be_bytes());
124 body.extend(path.as_bytes());
125 body.extend(rest);
126 self.send(kind, &body)?;
127 Ok(id)
128 }
129
130 fn send(&mut self, kind: u8, body: &[u8]) -> Result<()> {
131 let mut p = ((body.len() + 1) as u32).to_be_bytes().to_vec();
132 p.push(kind);
133 p.extend(body);
134 self.stream.write_all(&p).map_err(|e| self.io(e))
135 }
136
137 fn recv(&mut self) -> Result<(u8, Vec<u8>)> {
138 let mut len = [0u8; 4];
139 self.stream.read_exact(&mut len).map_err(|e| self.io(e))?;
140 let len = u32::from_be_bytes(len) as usize;
141 if len == 0 || len > 1 << 20 {
142 return Err(self.protocol(format!("packet of {len} bytes")));
143 }
144 let mut p = vec![0u8; len];
145 self.stream.read_exact(&mut p).map_err(|e| self.io(e))?;
146 Ok((p[0], p.split_off(1)))
147 }
148
149 fn failed(&self, what: &str, code: u32, r: &mut Reader) -> Error {
150 let msg = r.string().unwrap_or_default();
151 Error::OperationFailed {
152 step: format!("{} ({what})", self.what),
153 message: if msg.is_empty() {
154 format!("SFTP status {code}")
155 } else {
156 msg
157 },
158 }
159 }
160
161 fn protocol(&self, m: String) -> Error {
162 Error::Protocol(format!("{}: {m}", self.what))
163 }
164
165 fn io(&self, e: std::io::Error) -> Error {
166 Error::Protocol(format!("{}: {e}", self.what))
167 }
168}
169
170struct Reader<'a> {
171 b: &'a [u8],
172}
173
174impl<'a> Reader<'a> {
175 fn new(b: &'a [u8]) -> Self {
176 Reader { b }
177 }
178
179 fn take(&mut self, n: usize) -> Result<&'a [u8]> {
180 if self.b.len() < n {
181 return Err(Error::Protocol("sftp: short packet".into()));
182 }
183 let (h, t) = self.b.split_at(n);
184 self.b = t;
185 Ok(h)
186 }
187
188 fn u32(&mut self) -> Result<u32> {
189 let b = self.take(4)?;
190 Ok(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
191 }
192
193 fn u64(&mut self) -> Result<u64> {
194 Ok(((self.u32()? as u64) << 32) | self.u32()? as u64)
195 }
196
197 fn string(&mut self) -> Result<String> {
198 let n = self.u32()? as usize;
199 Ok(String::from_utf8_lossy(self.take(n)?).into_owned())
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn reads_attrs_as_incus_sends_them() {
209 let body = b"\x00\x00\x00\x01\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x10\x00\
211 \x00\x00\x03\xe8\x00\x00\x03\xe9\x00\x00\x41\xc9\x6a\xc3\x3f\x51\x6a\xc3\x3f\x51";
212 let mut r = Reader::new(body);
213 assert_eq!(r.u32().unwrap(), 1);
214 let flags = r.u32().unwrap();
215 assert_eq!(
216 flags,
217 ATTR_SIZE | ATTR_UIDGID | ATTR_PERMISSIONS | ATTR_ACMODTIME
218 );
219 assert_eq!(r.u64().unwrap(), 4096);
220 assert_eq!((r.u32().unwrap(), r.u32().unwrap()), (1000, 1001));
221 assert_eq!(r.u32().unwrap() & 0o7777, 0o711);
222 assert!(Reader::new(b"\x00\x00").u32().is_err());
223 }
224}