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