path_permission/
unix.rs

1use std::{
2    io,
3    path::{Path, PathBuf},
4    os::{
5        raw::c_int,
6        unix::{
7            ffi::OsStrExt,
8            fs::MetadataExt,
9        }
10    },
11};
12
13pub trait PathPermission {
14    /// 检查对路径的权限,通过1(x)、2(w)、4(r)
15    fn access(&self, amode: c_int) -> io::Result<bool>;
16
17    /// 判断路径是否可读
18    fn is_readable(&self) -> io::Result<bool>;
19
20    /// 判断路径是否可写
21    fn is_writable(&self) -> io::Result<bool>;
22
23    /// 判断路径是否可执行
24    fn is_excutable(&self) -> io::Result<bool>;
25
26    /// 判断路径可否被创建(当前无此路径)
27    fn is_creatable(&self) -> io::Result<bool>;
28
29    /// 判断路径能否被删除
30    fn is_removable(&self) -> io::Result<bool>;
31
32    /// 检查文件的权限
33    /// mode 可习惯上使用8进制数字,如:0o0644
34    /// The file type and mode: The stat.st_mode contains the file type and mode.
35    /// 帮助手册[inode(7)](https://man7.org/linux/man-pages/man7/inode.7.html)
36    fn check_access(&self, mode: u16) -> io::Result<bool>;
37
38    /// 返回路径的权限,以 stat 的形式:0o0644
39    /// 注意:已经格式化为字符串!
40    fn get_access(&self) -> io::Result<String>;
41
42    /// 变更文件的权限
43    /// mode 可习惯上使用8进制数字,如:0o0644
44    fn chmod(&self, mode: u16) -> io::Result<bool>;
45}
46
47impl PathPermission for Path {
48    fn access(&self, amode: c_int) -> io::Result<bool> {
49        access(self, amode)
50    }
51
52    fn is_readable(&self) -> io::Result<bool> {
53        self.access(libc::R_OK)
54    }
55
56    fn is_writable(&self) -> io::Result<bool> {
57        self.access(libc::W_OK)
58    }
59
60    fn is_excutable(&self) -> io::Result<bool> {
61        self.access(libc::X_OK)
62    }
63
64    fn is_creatable(&self) -> io::Result<bool> {
65        let parent = match self.parent() {
66            // 此时已无父级目录,则此路径为相对路径,其起始位置为当前目录。
67            // 不建议使用相对路径
68            None => Path::new("./"),
69            Some(parent) => parent,
70        };
71        if ! parent.exists() {
72            parent.is_creatable()
73        } else {
74            // parent 一定存在,可直接使用unwrap()获取结果
75            // 需要对父级目录有写和读的权限(1 + 2 = 3)
76            parent.access(libc::X_OK + libc::W_OK)
77        }
78    }
79
80    fn is_removable(&self) -> io::Result<bool> {
81        // 文件不存在时,返回Ok(false)
82        if ! self.exists() {
83            return Ok(false)
84        }
85        let parent = match self.parent() {
86            None => Path::new("./"),
87            Some(parent) => parent,
88        };
89
90        // 如果父级目录没有设置 S_ISVTX
91        // 需要对父级目录有写和读的权限(1 + 2 = 3)
92        if ! parent.check_access(0o1000).unwrap() {
93            parent.access(libc::X_OK + libc::W_OK)
94        } else {
95            // 需进行是否为本用户所属文件判断
96            unsafe {
97                if libc::getuid() == self.metadata().unwrap().uid() {
98                    parent.access(libc::X_OK + libc::W_OK)
99                } else {
100                    Ok(false)
101                }
102            }
103        }
104    }
105
106    fn check_access(&self, mode: u16) -> io::Result<bool> {
107        if let Ok(metadata) = self.metadata() {
108            if metadata.mode() as u16 & mode == mode {
109                Ok(true)
110            } else {
111                Ok(false)
112            }
113        } else {
114            Err(io::Error::last_os_error())
115        }
116    }
117
118    fn get_access(&self) -> io::Result<String> {
119        if let Ok(metadata) = self.metadata() {
120            Ok(format!("{:o}{:o}",
121                       metadata.mode() as u16 & 0o7000,
122                       metadata.mode() as u16 & 0o777))
123        } else {
124            Err(io::Error::last_os_error())
125        }
126    }
127
128    fn chmod(&self, mode: u16) -> io::Result<bool> {
129        chmod(self, mode)
130    }
131}
132
133impl PathPermission for PathBuf {
134    fn access(&self, amode: c_int) -> io::Result<bool> {
135        self.as_path().access(amode)
136    }
137
138    fn is_readable(&self) -> io::Result<bool> {
139        self.as_path().is_readable()
140    }
141
142    fn is_writable(&self) -> io::Result<bool> {
143        self.as_path().is_writable()
144    }
145
146    fn is_excutable(&self) -> io::Result<bool> {
147        self.as_path().is_excutable()
148    }
149
150    fn is_creatable(&self) -> io::Result<bool> {
151        self.as_path().is_creatable()
152    }
153
154    fn is_removable(&self) -> io::Result<bool> {
155        self.as_path().is_removable()
156    }
157
158    fn check_access(&self, mode: u16) -> io::Result<bool> {
159        self.as_path().check_access(mode)
160    }
161
162    fn get_access(&self) -> io::Result<String> {
163        self.as_path().get_access()
164    }
165
166    fn chmod(&self, mode: u16) -> io::Result<bool> {
167        self.as_path().chmod(mode)
168    }
169}
170
171fn access(path: &Path, mod_mask: c_int) ->io::Result<bool> {
172    let mut buf = Vec::new();
173    let buf_ptr;
174
175    // 在C中,char的最后一位是'\0'或ASCII码值为0
176    buf.extend(path.as_os_str().as_bytes());
177    buf.push(0);
178
179    buf_ptr = buf.as_ptr() as *const libc::c_char;
180
181    let result = unsafe {
182        libc::access(buf_ptr, mod_mask)
183    };
184
185    match result {
186        0 => Ok(true),
187        _ => {
188            let err = io::Error::last_os_error();
189            if err.raw_os_error().unwrap() == libc::EACCES {
190                Ok(false)  // 无查看此路径的权限(无法确认路径是否存在)
191            } else {
192                Err(err)  // 其它错误,如路径不存在等
193            }
194        }
195    }
196}
197
198fn chmod(path: &Path, mode: u16) -> io::Result<bool> {
199    let mut buf = Vec::new();
200    let buf_ptr;
201
202    // 在C中,char的最后一位是'\0'或ASCII码值为0
203    buf.extend(path.as_os_str().as_bytes());
204    buf.push(0);
205
206    buf_ptr = buf.as_ptr() as *const libc::c_char;
207
208    let result = unsafe {
209        libc::chmod(buf_ptr, mode)
210    };
211
212    match result {
213        0 => Ok(true),
214        // 1: PermissionDenied, 2: No such file or directory
215        1 | 2 => Ok(false),
216        _ => Err(io::Error::last_os_error()),
217    }
218}