1#![deny(warnings)]
26#![deny(deprecated_safe)]
27#![deny(future_incompatible)]
28#![deny(keyword_idents)]
29#![deny(let_underscore)]
30#![deny(nonstandard_style)]
31#![deny(refining_impl_trait)]
32#![deny(rust_2018_idioms)]
33#![deny(unused)]
34
35#[macro_use]
61extern crate bitflags;
62
63#[cfg(feature = "serde")]
64#[macro_use]
65extern crate serde;
66
67use e2p_sys::*;
68use std::ffi::CString;
69use std::fs::File;
70use std::io::{Error, ErrorKind};
71use std::os::unix::io::AsRawFd;
72use std::path::Path;
73
74bitflags! {
75 #[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
77 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
78 pub struct Flags: u32 {
79 const SECRM = EXT2_SECRM_FL;
80 const UNRM = EXT2_UNRM_FL;
81 const COMPR = EXT2_COMPR_FL;
82 const SYNC = EXT2_SYNC_FL;
83 const IMMUTABLE = EXT2_IMMUTABLE_FL;
84 const APPEND = EXT2_APPEND_FL;
85 const NODUMP = EXT2_NODUMP_FL;
86 const NOATIME = EXT2_NOATIME_FL;
87 const DIRTY = EXT2_DIRTY_FL;
88 const COMPRBLK = EXT2_COMPRBLK_FL;
89 const NOCOMPR = EXT2_NOCOMPR_FL;
90
91 #[cfg(ENCRYPT)]
92 const ENCRYPT = EXT4_ENCRYPT_FL;
93
94 const BTREE = EXT2_BTREE_FL;
95 const INDEX = EXT2_INDEX_FL;
96 const IMAGIC = EXT2_IMAGIC_FL;
97 const JOURNAL_DATA = EXT3_JOURNAL_DATA_FL;
98 const NOTAIL = EXT2_NOTAIL_FL;
99 const DIRSYNC = EXT2_DIRSYNC_FL;
100 const TOPDIR = EXT2_TOPDIR_FL;
101 const HUGE_FILE = EXT4_HUGE_FILE_FL;
102 const EXTENTS = EXT4_EXTENTS_FL;
103
104 #[cfg(VERITY)]
105 const VERITY = EXT4_VERITY_FL;
106
107 const EA_INODE = EXT4_EA_INODE_FL;
108 const NOCOW = FS_NOCOW_FL;
109 const SNAPFILE = EXT4_SNAPFILE_FL;
110
111 #[cfg(DAX)]
112 const DAX = FS_DAX_FL;
113
114 const SNAPFILE_DELETED = EXT4_SNAPFILE_DELETED_FL;
115 const SNAPFILE_SHRUNK = EXT4_SNAPFILE_SHRUNK_FL;
116
117 #[cfg(INLINE_DATA)]
118 const INLINE_DATA = EXT4_INLINE_DATA_FL;
119
120 #[cfg(PROJINHERIT)]
121 const PROJINHERIT = EXT4_PROJINHERIT_FL;
122
123 #[cfg(CASEFOLD)]
124 const CASEFOLD = EXT4_CASEFOLD_FL;
125
126 const RESERVED = EXT2_RESERVED_FL;
127 const USER_VISIBLE = EXT2_FL_USER_VISIBLE;
128 const USER_MODIFIABLE = EXT2_FL_USER_MODIFIABLE;
129 }
130}
131
132pub trait FileFlags {
134 fn flags(&self) -> Result<Flags, Error>;
136
137 fn set_flags(&self, f: Flags) -> Result<(), Error>;
140}
141
142impl FileFlags for Path {
143 fn flags(&self) -> Result<Flags, Error> {
144 let path_cstr = match self.to_str() {
145 Some(s) => CString::new(s)?,
146 None => {
147 return Err(Error::new(
148 ErrorKind::InvalidInput,
149 "Provided path is no valid Unicode",
150 ));
151 }
152 };
153 let ret: i32;
154 let mut retflags: u64 = 0;
155 let path_ptr = path_cstr.as_ptr();
156 let retflags_ptr: *mut u64 = &mut retflags;
157
158 unsafe {
159 ret = fgetflags(path_ptr, retflags_ptr);
160 }
161
162 match ret {
163 0 => match Flags::from_bits(retflags as u32) {
164 Some(f) => Ok(f),
165 None => Err(Error::new(
166 ErrorKind::InvalidData,
167 "Unexcpected flags encountered",
168 )),
169 },
170 _ => Err(Error::last_os_error()),
171 }
172 }
173
174 fn set_flags(&self, f: Flags) -> Result<(), Error> {
175 let path_cstr = match self.to_str() {
176 Some(s) => CString::new(s)?,
177 None => {
178 return Err(Error::new(
179 ErrorKind::InvalidInput,
180 "Provided path is no valid Unicode",
181 ));
182 }
183 };
184 let ret: i32;
185 let intflags: u64 = f.bits() as u64;
186 let path_ptr = path_cstr.as_ptr();
187
188 unsafe {
189 ret = fsetflags(path_ptr, intflags);
190 }
191
192 match ret {
193 0 => Ok(()),
194 _ => Err(Error::last_os_error()),
195 }
196 }
197}
198
199impl FileFlags for File {
200 fn flags(&self) -> Result<Flags, Error> {
201 let ret: i32;
202 let mut retflags: u64 = 0;
203 let retflags_ptr: *mut u64 = &mut retflags;
204
205 unsafe {
206 ret = getflags(self.as_raw_fd(), retflags_ptr);
207 }
208
209 match ret {
210 0 => match Flags::from_bits(retflags as u32) {
211 Some(f) => Ok(f),
212 None => Err(Error::new(
213 ErrorKind::InvalidData,
214 "Unexcpected flags encountered",
215 )),
216 },
217 _ => Err(Error::last_os_error()),
218 }
219 }
220
221 fn set_flags(&self, f: Flags) -> Result<(), Error> {
222 let ret: i32;
223 let intflags: u64 = f.bits() as u64;
224
225 unsafe {
226 ret = setflags(self.as_raw_fd(), intflags);
227 }
228
229 match ret {
230 0 => Ok(()),
231 _ => Err(Error::last_os_error()),
232 }
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use std::env;
240 use std::fs::{File, remove_file};
241
242 #[test]
243 fn unified() {
244 let mut p = env::current_dir().unwrap();
245 p.push("e2p-fileflags-testfile-voo4JooY");
246 let f = File::create(&p).unwrap();
247
248 let initial = p.flags().unwrap();
249 assert_eq!(initial, f.flags().unwrap());
250
251 p.set_flags(Flags::NOATIME | initial).unwrap();
252 assert_eq!(f.flags().unwrap(), Flags::NOATIME | initial);
253 p.set_flags(initial).unwrap();
254 assert_eq!(f.flags().unwrap(), initial);
255
256 f.set_flags(Flags::NOATIME | initial).unwrap();
257 assert_eq!(p.flags().unwrap(), Flags::NOATIME | initial);
258 f.set_flags(initial).unwrap();
259 assert_eq!(p.flags().unwrap(), initial);
260
261 drop(f);
262 drop(remove_file(p));
263 }
264}