sftp_protocol/common/
file_attributes.rs

1use std::fmt;
2
3use nom::IResult;
4use nom::number::streaming::be_u32;
5use nom::number::streaming::be_u64;
6use nom::number::streaming::le_u32;
7use nom::number::streaming::le_u64;
8
9use serde::ser::Serialize;
10use serde::ser::Serializer;
11use serde::ser::SerializeStruct;
12
13#[cfg(test)]
14mod arbitrary;
15
16bitflags::bitflags! {
17	#[derive(Default)]
18	pub struct FileAttrFlags: u32 {
19		const Size = 0x00000001;
20		const UidGid = 0x00000002;
21		const Permissions = 0x00000004;
22		const ACModTime = 0x00000008;
23		const Extended = 0x80000000;
24	}
25}
26
27impl<I: nom_derive::InputSlice> nom_derive::Parse<I> for FileAttrFlags {
28	fn parse_be(i: I) -> IResult<I, Self> {
29		let (i, flags) = be_u32(i)?;
30		let this = Self::from_bits_truncate(flags);
31		Ok((i, this))
32	}
33
34	fn parse_le(i: I) -> IResult<I, Self> {
35		let (i, flags) = le_u32(i)?;
36		let this = Self::from_bits_truncate(flags);
37		Ok((i, this))
38	}
39
40	fn parse(i: I) -> IResult<I, Self> {
41		Self::parse_be(i)
42	}
43}
44
45#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
46#[repr(transparent)]
47pub struct Permissions(u32);
48impl fmt::Display for Permissions {
49	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
50		write!(f, "{}{}{}{}{}{}{}{}{}{}", self.dir(), self.owner_r(), self.owner_w(), self.owner_x(), self.group_r(), self.group_w(), self.group_x(), self.other_r(), self.other_w(), self.other_x())
51	}
52}
53
54impl Permissions {
55	fn dir(self) -> char /* {{{ */ {
56		match self.0 & 0o40000 {
57			0 => '-',
58			_ => 'd'
59		}
60	} // }}}
61
62	fn owner_r(self) -> char /* {{{ */ {
63		match self.0 & 0o400 {
64			0 => '-',
65			_ => 'r'
66		}
67	} // }}}
68
69	fn owner_w(self) -> char /* {{{ */ {
70		match self.0 & 0o200 {
71			0 => '-',
72			_ => 'w'
73		}
74	} // }}}
75
76	fn owner_x(self) -> char /* {{{ */ {
77		match self.0 & 0o100 {
78			0 => '-',
79			_ => 'x'
80		}
81	} // }}}
82
83	fn group_r(self) -> char /* {{{ */ {
84		match self.0 & 0o040 {
85			0 => '-',
86			_ => 'r'
87		}
88	} // }}}
89
90	fn group_w(self) -> char /* {{{ */ {
91		match self.0 & 0o020 {
92			0 => '-',
93			_ => 'w'
94		}
95	} // }}}
96
97	fn group_x(self) -> char /* {{{ */ {
98		match self.0 & 0o010 {
99			0 => '-',
100			_ => 'x'
101		}
102	} // }}}
103
104	fn other_r(self) -> char /* {{{ */ {
105		match self.0 & 0o004 {
106			0 => '-',
107			_ => 'r'
108		}
109	} // }}}
110
111	fn other_w(self) -> char /* {{{ */ {
112		match self.0 & 0o002 {
113			0 => '-',
114			_ => 'w'
115		}
116	} // }}}
117
118	fn other_x(self) -> char /* {{{ */ {
119		match self.0 & 0o001 {
120			0 => '-',
121			_ => 'x'
122		}
123	} // }}}
124}
125
126impl From<u32> for Permissions {
127	fn from(raw: u32) -> Self {
128		Self(raw)
129	}
130}
131
132#[derive(Clone, Debug, Default, Eq, PartialEq)]
133pub struct FileAttributes {
134	pub flags: FileAttrFlags,
135	pub size: Option<u64>,
136	pub uid: Option<u32>,
137	pub gid: Option<u32>,
138	pub permissions: Option<Permissions>,
139	pub atime: Option<u32>,
140	pub mtime: Option<u32>,
141	// TODO:  Extended count, extended strings
142}
143
144fn format_month(ts: &time::OffsetDateTime) -> &'static str {
145	match ts.month() {
146		time::Month::January => "Jan",
147		time::Month::February => "Feb",
148		time::Month::March => "Mar",
149		time::Month::April => "Apr",
150		time::Month::May => "May",
151		time::Month::June => "Jun",
152		time::Month::July => "Jul",
153		time::Month::August => "Aug",
154		time::Month::September => "Sep",
155		time::Month::October => "Oct",
156		time::Month::November => "Nov",
157		time::Month::December => "Dec"
158	}
159}
160
161impl fmt::Display for FileAttributes {
162	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
163		match self.permissions {
164			Some(v) => write!(f, "{} ", v)?,
165			None => write!(f, "           ")?
166			//                 1234567890
167		};
168		// TODO:  Actually check link count
169		write!(f, "  1 ")?;
170		//         123
171		match self.uid {
172			Some(v) => write!(f, "{: <8} ", v)?,
173			None => write!(f, "         ")?
174			//                 12345678
175		};
176		match self.gid {
177			Some(v) => write!(f, "{: <8} ", v)?,
178			None => write!(f, "         ")?
179			//                 12345678
180		};
181		match self.size {
182			Some(v) => write!(f, "{: >8} ", v)?,
183			None => write!(f, "         ")?
184			//                 12345678
185		};
186		match self.mtime {
187			Some(v) => {
188				// unwrapping is safe here because all possible u32 values are valid Unix timestamps
189				let time = time::OffsetDateTime::from_unix_timestamp(v.into()).unwrap();
190				write!(f, "{} {: >2} {:0>2}:{:0>2} ", format_month(&time), time.day(), time.hour(), time.minute())?
191			},
192			None => write!(f, "             ")?
193			//                 123456789012
194		};
195		Ok(())
196	}
197}
198
199impl Serialize for FileAttributes {
200	fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> /* {{{ */ {
201		let mut field_count = 1;
202		if(self.flags.contains(FileAttrFlags::Size)) {
203			field_count += 1;
204		}
205		if(self.flags.contains(FileAttrFlags::UidGid)) {
206			field_count += 2;
207		}
208		if(self.flags.contains(FileAttrFlags::Permissions)) {
209			field_count += 1;
210		}
211		if(self.flags.contains(FileAttrFlags::ACModTime)) {
212			field_count += 2;
213		}
214		// TODO:  Extended count and strings
215		let mut state = serializer.serialize_struct("FileAttributes", field_count)?;
216		state.serialize_field("flags", &self.flags.bits())?;
217		if(self.flags.contains(FileAttrFlags::Size)) {
218			state.serialize_field("size", &self.size.unwrap_or(0))?;
219		}
220		if(self.flags.contains(FileAttrFlags::UidGid)) {
221			state.serialize_field("uid", &self.uid.unwrap_or(0))?;
222			state.serialize_field("gid", &self.gid.unwrap_or(0))?;
223		}
224		if(self.flags.contains(FileAttrFlags::Permissions)) {
225			state.serialize_field("permissions", &self.permissions.unwrap_or_default())?;
226		}
227		if(self.flags.contains(FileAttrFlags::ACModTime)) {
228			state.serialize_field("atime", &self.atime.unwrap_or(0))?;
229			state.serialize_field("mtime", &self.mtime.unwrap_or(0))?;
230		}
231		state.end()
232	} // }}}
233}
234
235impl<I: nom_derive::InputSlice> nom_derive::Parse<I> for FileAttributes {
236	fn parse_be(i: I) -> IResult<I, Self> /* {{{ */ {
237		let (mut i, flags) = FileAttrFlags::parse(i)?;
238		let mut attrs = Self{
239			flags,
240			..Default::default()
241		};
242		if(attrs.flags.contains(FileAttrFlags::Size)) {
243			let (i_inner, size) = be_u64(i)?;
244			i = i_inner;
245			attrs.size = Some(size);
246		}
247		if(attrs.flags.contains(FileAttrFlags::UidGid)) {
248			let (i_inner, uid) = be_u32(i)?;
249			let (i_inner, gid) = be_u32(i_inner)?;
250			i = i_inner;
251			attrs.uid = Some(uid);
252			attrs.gid = Some(gid);
253		}
254		if(attrs.flags.contains(FileAttrFlags::Permissions)) {
255			let (i_inner, permissions) = be_u32(i)?;
256			i = i_inner;
257			attrs.permissions = Some(permissions.into());
258		}
259		if(attrs.flags.contains(FileAttrFlags::ACModTime)) {
260			let (i_inner, atime) = be_u32(i)?;
261			let (i_inner, mtime) = be_u32(i_inner)?;
262			i = i_inner;
263			attrs.atime = Some(atime);
264			attrs.mtime = Some(mtime);
265		}
266		Ok((i, attrs))
267	} // }}}
268
269	fn parse_le(i: I) -> IResult<I, Self> /* {{{ */ {
270		let mut attrs = FileAttributes::default();
271		let (mut i, flags) = FileAttrFlags::parse(i)?;
272		attrs.flags = flags;
273		if(attrs.flags.contains(FileAttrFlags::Size)) {
274			let (i_inner, size) = le_u64(i)?;
275			i = i_inner;
276			attrs.size = Some(size);
277		}
278		if(attrs.flags.contains(FileAttrFlags::UidGid)) {
279			let (i_inner, uid) = le_u32(i)?;
280			let (i_inner, gid) = le_u32(i_inner)?;
281			i = i_inner;
282			attrs.uid = Some(uid);
283			attrs.gid = Some(gid);
284		}
285		if(attrs.flags.contains(FileAttrFlags::Permissions)) {
286			let (i_inner, permissions) = le_u32(i)?;
287			i = i_inner;
288			attrs.permissions = Some(permissions.into());
289		}
290		if(attrs.flags.contains(FileAttrFlags::ACModTime)) {
291			let (i_inner, atime) = le_u32(i)?;
292			let (i_inner, mtime) = le_u32(i_inner)?;
293			i = i_inner;
294			attrs.atime = Some(atime);
295			attrs.mtime = Some(mtime);
296		}
297		Ok((i, attrs))
298	} // }}}
299
300	fn parse(i: I) -> IResult<I, Self> {
301		Self::parse_be(i)
302	}
303}
304
305impl FileAttributes {
306	pub fn new() -> Self /* {{{ */ {
307		Self{
308			flags: FileAttrFlags::from_bits_truncate(0),
309			..Default::default()
310		}
311	} // }}}
312
313	pub fn set_size(&mut self, size: u64) /* {{{ */ {
314		self.flags.set(FileAttrFlags::Size, true);
315		self.size = Some(size);
316	} // }}}
317
318	pub fn get_uid_gid(&self) -> Option<(u32, u32)> /* {{{ */ {
319		match self.flags.contains(FileAttrFlags::UidGid) {
320			true => Some((self.uid.unwrap(), self.gid.unwrap())),
321			false => None
322		}
323	} // }}}
324
325	pub fn set_uid_gid(&mut self, uid: u32, gid: u32) /* {{{ */ {
326		self.flags.set(FileAttrFlags::UidGid, true);
327		self.uid = Some(uid);
328		self.gid = Some(gid);
329	} // }}}
330
331	pub fn get_permissions(&self) -> Option<u32> /* {{{ */ {
332		self.permissions.map(|p| p.0)
333	} // }}}
334
335	pub fn set_permissions(&mut self, permissions: Permissions) /* {{{ */ {
336		self.flags.set(FileAttrFlags::Permissions, true);
337		self.permissions = Some(permissions);
338	} // }}}
339
340	pub fn get_atime_mtime(&self) -> Option<(u32, u32)> /* {{{ */ {
341		match self.flags.contains(FileAttrFlags::ACModTime) {
342			true => Some((self.atime.unwrap(), self.mtime.unwrap())),
343			false => None
344		}
345	} // }}}
346
347	pub fn set_atime_mtime(&mut self, atime: u32, mtime: u32) /* {{{ */ {
348		self.flags.set(FileAttrFlags::ACModTime, true);
349		self.atime = Some(atime);
350		self.mtime = Some(mtime);
351	} // }}}
352
353	pub fn binsize(&self) -> u32 /* {{{ */ {
354		let mut size = 4;
355		if(self.flags.contains(FileAttrFlags::Size)) {
356			size += 8;
357		}
358		if(self.flags.contains(FileAttrFlags::UidGid)) {
359			size += 8;
360		}
361		if(self.flags.contains(FileAttrFlags::Permissions)) {
362			size += 4;
363		}
364		if(self.flags.contains(FileAttrFlags::ACModTime)) {
365			size += 8;
366		}
367		size
368	} // }}}
369}
370
371impl From<&super::Metadata> for FileAttributes {
372	fn from(metadata: &super::Metadata) -> Self {
373		let mut this = Self::new();
374		this.set_size(metadata.size);
375		this.set_uid_gid(metadata.uid, metadata.gid);
376		this.set_permissions({
377			let mut permissions = metadata.permissions;
378			if(metadata.is_dir) {
379				permissions |= 0o40000;
380			}
381			permissions.into()
382		});
383		this.set_atime_mtime(metadata.atime.unix_timestamp() as u32, metadata.mtime.unix_timestamp() as u32);
384		this
385	}
386}
387
388impl From<super::Metadata> for FileAttributes {
389	fn from(metadata: super::Metadata) -> Self {
390		Self::from(&metadata)
391	}
392}
393