]> git.proxmox.com Git - proxmox-backup.git/blame - src/tools/acl.rs
switch to external pxar and fuse crates
[proxmox-backup.git] / src / tools / acl.rs
CommitLineData
d22096ef
CE
1//! Implementation of the calls to handle POSIX access control lists
2
3// see C header file <sys/acl.h> for reference
4extern crate libc;
5
6use std::ffi::CString;
7use std::marker::PhantomData;
8use std::os::unix::ffi::OsStrExt;
9use std::os::unix::io::RawFd;
10use std::path::Path;
11use std::ptr;
12
13use libc::{c_char, c_int, c_uint, c_void};
14use nix::errno::Errno;
15
0502ce6d
CE
16// from: acl/include/acl.h
17pub const ACL_UNDEFINED_ID: u32 = 0xffffffff;
d22096ef
CE
18// acl_perm_t values
19pub type ACLPerm = c_uint;
dec1ff18
CE
20pub const ACL_READ: ACLPerm = 0x04;
21pub const ACL_WRITE: ACLPerm = 0x02;
d22096ef
CE
22pub const ACL_EXECUTE: ACLPerm = 0x01;
23
24// acl_tag_t values
25pub type ACLTag = c_int;
26pub const ACL_UNDEFINED_TAG: ACLTag = 0x00;
27pub const ACL_USER_OBJ: ACLTag = 0x01;
28pub const ACL_USER: ACLTag = 0x02;
29pub const ACL_GROUP_OBJ: ACLTag = 0x04;
30pub const ACL_GROUP: ACLTag = 0x08;
31pub const ACL_MASK: ACLTag = 0x10;
32pub const ACL_OTHER: ACLTag = 0x20;
33
34// acl_type_t values
35pub type ACLType = c_uint;
36pub const ACL_TYPE_ACCESS: ACLType = 0x8000;
37pub const ACL_TYPE_DEFAULT: ACLType = 0x4000;
38
39// acl entry constants
40pub const ACL_FIRST_ENTRY: c_int = 0;
41pub const ACL_NEXT_ENTRY: c_int = 1;
42
0502ce6d
CE
43// acl to extended attribute names constants
44// from: acl/include/acl_ea.h
45pub const ACL_EA_ACCESS: &'static str = "system.posix_acl_access";
46pub const ACL_EA_DEFAULT: &'static str = "system.posix_acl_default";
47pub const ACL_EA_VERSION: u32 = 0x0002;
48
d22096ef
CE
49#[link(name = "acl")]
50extern "C" {
51 fn acl_get_file(path: *const c_char, acl_type: ACLType) -> *mut c_void;
c443f58b
WB
52 // FIXME: remove 'pub' after the cleanup
53 pub(crate) fn acl_set_file(path: *const c_char, acl_type: ACLType, acl: *mut c_void) -> c_int;
d22096ef
CE
54 fn acl_get_fd(fd: RawFd) -> *mut c_void;
55 fn acl_get_entry(acl: *const c_void, entry_id: c_int, entry: *mut *mut c_void) -> c_int;
56 fn acl_create_entry(acl: *mut *mut c_void, entry: *mut *mut c_void) -> c_int;
57 fn acl_get_tag_type(entry: *mut c_void, tag_type: *mut ACLTag) -> c_int;
58 fn acl_set_tag_type(entry: *mut c_void, tag_type: ACLTag) -> c_int;
59 fn acl_get_permset(entry: *mut c_void, permset: *mut *mut c_void) -> c_int;
dec1ff18 60 fn acl_clear_perms(permset: *mut c_void) -> c_int;
d22096ef 61 fn acl_get_perm(permset: *mut c_void, perm: ACLPerm) -> c_int;
dec1ff18 62 fn acl_add_perm(permset: *mut c_void, perm: ACLPerm) -> c_int;
d22096ef
CE
63 fn acl_get_qualifier(entry: *mut c_void) -> *mut c_void;
64 fn acl_set_qualifier(entry: *mut c_void, qualifier: *const c_void) -> c_int;
65 fn acl_init(count: c_int) -> *mut c_void;
66 fn acl_valid(ptr: *const c_void) -> c_int;
67 fn acl_free(ptr: *mut c_void) -> c_int;
68}
69
70#[derive(Debug)]
71pub struct ACL {
c443f58b
WB
72 // FIXME: remove 'pub' after the cleanup
73 pub(crate) ptr: *mut c_void,
d22096ef
CE
74}
75
76impl Drop for ACL {
77 fn drop(&mut self) {
78 let ret = unsafe { acl_free(self.ptr) };
79 if ret != 0 {
80 panic!("invalid pointer encountered while dropping ACL - {}", Errno::last());
81 }
82 }
83}
84
85impl ACL {
86 pub fn init(count: usize) -> Result<ACL, nix::errno::Errno> {
87 let ptr = unsafe { acl_init(count as i32 as c_int) };
88 if ptr.is_null() {
89 return Err(Errno::last());
90 }
91
834a2f95 92 Ok(ACL { ptr })
d22096ef
CE
93 }
94
95 pub fn get_file<P: AsRef<Path>>(path: P, acl_type: ACLType) -> Result<ACL, nix::errno::Errno> {
96 let path_cstr = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
97 let ptr = unsafe { acl_get_file(path_cstr.as_ptr(), acl_type) };
98 if ptr.is_null() {
99 return Err(Errno::last());
100 }
101
834a2f95 102 Ok(ACL { ptr })
d22096ef
CE
103 }
104
105 pub fn set_file<P: AsRef<Path>>(&self, path: P, acl_type: ACLType) -> Result<(), nix::errno::Errno> {
106 let path_cstr = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
107 let res = unsafe { acl_set_file(path_cstr.as_ptr(), acl_type, self.ptr) };
108 if res < 0 {
109 return Err(Errno::last());
110 }
111
112 Ok(())
113 }
114
115 pub fn get_fd(fd: RawFd) -> Result<ACL, nix::errno::Errno> {
116 let ptr = unsafe { acl_get_fd(fd) };
117 if ptr.is_null() {
118 return Err(Errno::last());
119 }
120
834a2f95 121 Ok(ACL { ptr })
d22096ef
CE
122 }
123
62ee2eb4 124 pub fn create_entry(&mut self) -> Result<ACLEntry, nix::errno::Errno> {
d22096ef
CE
125 let mut ptr = ptr::null_mut() as *mut c_void;
126 let res = unsafe { acl_create_entry(&mut self.ptr, &mut ptr) };
127 if res < 0 {
128 return Err(Errno::last());
129 }
130
131 Ok(ACLEntry {
834a2f95 132 ptr,
d22096ef
CE
133 _phantom: PhantomData,
134 })
135 }
136
137 pub fn is_valid(&self) -> bool {
138 let res = unsafe { acl_valid(self.ptr) };
139 if res == 0 {
140 return true;
141 }
142
143 false
144 }
145
146 pub fn entries(self) -> ACLEntriesIterator {
147 ACLEntriesIterator {
148 acl: self,
149 current: ACL_FIRST_ENTRY,
150 }
151 }
dec1ff18
CE
152
153 pub fn add_entry_full(&mut self, tag: ACLTag, qualifier: Option<u64>, permissions: u64)
154 -> Result<(), nix::errno::Errno>
155 {
156 let mut entry = self.create_entry()?;
157 entry.set_tag_type(tag)?;
158 if let Some(qualifier) = qualifier {
159 entry.set_qualifier(qualifier)?;
160 }
161 entry.set_permissions(permissions)?;
162
163 Ok(())
164 }
d22096ef
CE
165}
166
167#[derive(Debug)]
168pub struct ACLEntry<'a> {
169 ptr: *mut c_void,
170 _phantom: PhantomData<&'a mut ()>,
171}
172
173impl<'a> ACLEntry<'a> {
174 pub fn get_tag_type(&self) -> Result<ACLTag, nix::errno::Errno> {
175 let mut tag = ACL_UNDEFINED_TAG;
176 let res = unsafe { acl_get_tag_type(self.ptr, &mut tag as *mut ACLTag) };
177 if res < 0 {
178 return Err(Errno::last());
179 }
180
181 Ok(tag)
182 }
183
184 pub fn set_tag_type(&mut self, tag: ACLTag) -> Result<(), nix::errno::Errno> {
185 let res = unsafe { acl_set_tag_type(self.ptr, tag) };
186 if res < 0 {
187 return Err(Errno::last());
188 }
189
190 Ok(())
191 }
192
193 pub fn get_permissions(&self) -> Result<u64, nix::errno::Errno> {
194 let mut permissions = 0;
195 let mut permset = ptr::null_mut() as *mut c_void;
196 let mut res = unsafe { acl_get_permset(self.ptr, &mut permset) };
197 if res < 0 {
198 return Err(Errno::last());
199 }
200
201 for &perm in &[ACL_READ, ACL_WRITE, ACL_EXECUTE] {
202 res = unsafe { acl_get_perm(permset, perm) };
203 if res < 0 {
204 return Err(Errno::last());
205 } else if res > 0 {
206 permissions |= perm as u64;
207 }
208 }
209
210 Ok(permissions)
211 }
212
dec1ff18
CE
213 pub fn set_permissions(&mut self, permissions: u64) -> Result<u64, nix::errno::Errno> {
214 let mut permset = ptr::null_mut() as *mut c_void;
215 let mut res = unsafe { acl_get_permset(self.ptr, &mut permset) };
216 if res < 0 {
217 return Err(Errno::last());
218 }
219
220 res = unsafe { acl_clear_perms(permset) };
221 if res < 0 {
222 return Err(Errno::last());
223 }
224
225 for &perm in &[ACL_READ, ACL_WRITE, ACL_EXECUTE] {
226 if permissions & perm as u64 == perm as u64 {
227 res = unsafe { acl_add_perm(permset, perm) };
228 if res < 0 {
229 return Err(Errno::last());
230 }
231 }
232 }
233
234 Ok(permissions)
235 }
236
d22096ef
CE
237 pub fn get_qualifier(&self) -> Result<u64, nix::errno::Errno> {
238 let qualifier = unsafe { acl_get_qualifier(self.ptr) };
239 if qualifier.is_null() {
240 return Err(Errno::last());
241 }
242 let result = unsafe { *(qualifier as *const u32) as u64 };
243 let ret = unsafe { acl_free(qualifier) };
244 if ret != 0 {
245 panic!("invalid pointer encountered while dropping ACL qualifier - {}", Errno::last());
246 }
247
248 Ok(result)
249 }
250
251 pub fn set_qualifier(&mut self, qualifier: u64) -> Result<(), nix::errno::Errno> {
252 let val = qualifier as u32;
253 let val_ptr: *const u32 = &val;
254 let res = unsafe { acl_set_qualifier(self.ptr, val_ptr as *const c_void) };
255 if res < 0 {
256 return Err(Errno::last());
257 }
258
259 Ok(())
260 }
261}
262
263#[derive(Debug)]
264pub struct ACLEntriesIterator {
265 acl: ACL,
266 current: c_int,
267}
268
269impl<'a> Iterator for &'a mut ACLEntriesIterator {
270 type Item = ACLEntry<'a>;
271
272 fn next(&mut self) -> Option<Self::Item> {
273 let mut entry_ptr = ptr::null_mut();
274 let res = unsafe { acl_get_entry(self.acl.ptr, self.current, &mut entry_ptr) };
275 self.current = ACL_NEXT_ENTRY;
276 if res == 1 {
277 return Some(ACLEntry { ptr: entry_ptr, _phantom: PhantomData });
278 }
279
280 None
281 }
282}
bcf0d452
CE
283
284/// Helper to transform `PxarEntry`s user mode to acl permissions.
285pub fn mode_user_to_acl_permissions(mode: u64) -> u64 {
286 (mode >> 6) & 7
287}
288
289/// Helper to transform `PxarEntry`s group mode to acl permissions.
290pub fn mode_group_to_acl_permissions(mode: u64) -> u64 {
291 (mode >> 3) & 7
292}
293
294/// Helper to transform `PxarEntry`s other mode to acl permissions.
295pub fn mode_other_to_acl_permissions(mode: u64) -> u64 {
296 mode & 7
297}
0502ce6d
CE
298
299/// Buffer to compose ACLs as extended attribute.
300pub struct ACLXAttrBuffer {
301 buffer: Vec<u8>,
302}
303
304impl ACLXAttrBuffer {
305 /// Create a new buffer to write ACLs as extended attribute.
306 ///
307 /// `version` defines the ACL_EA_VERSION found in acl/include/acl_ea.h
308 pub fn new(version: u32) -> Self {
309 let mut buffer = Vec::new();
310 buffer.extend_from_slice(&version.to_le_bytes());
311 Self { buffer }
312 }
313
314 /// Add ACL entry to buffer.
315 pub fn add_entry(&mut self, tag: ACLTag, qualifier: Option<u64>, permissions: u64) {
316 self.buffer.extend_from_slice(&(tag as u16).to_le_bytes());
317 self.buffer.extend_from_slice(&(permissions as u16).to_le_bytes());
318 match qualifier {
319 Some(qualifier) => self.buffer.extend_from_slice(&(qualifier as u32).to_le_bytes()),
320 None => self.buffer.extend_from_slice(&ACL_UNDEFINED_ID.to_le_bytes()),
321 }
322 }
323
324 /// Length of the buffer in bytes.
325 pub fn len(&self) -> usize {
326 self.buffer.len()
327 }
328
329 /// Borrow raw buffer as mut slice.
330 pub fn as_mut_slice(&mut self) -> &mut [u8] {
331 self.buffer.as_mut_slice()
332 }
333}