@@ -43,14 +43,92 @@ fn type_bits(mode: u32) -> u32 {
43
43
( mode >> 12 ) & 0o17
44
44
}
45
45
46
+ /// The different types of files known to this library
47
+ ///
48
+ /// Can be constructed `From<u32>`.
49
+ /// ```
50
+ /// assert_eq!(unix_mode::Type::from(0o0100640), unix_mode::Type::File);
51
+ /// ```
52
+ #[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
53
+ #[ non_exhaustive]
54
+ pub enum Type {
55
+ File ,
56
+ Dir ,
57
+ Symlink ,
58
+ Socket ,
59
+ Fifo ,
60
+ BlockDevice ,
61
+ CharDevice ,
62
+ /// Removed file in union filesystems
63
+ Whiteout ,
64
+ /// File type not recognized by this version of this library
65
+ ///
66
+ /// More types might be added in the future, so the semantics of this variant may change.
67
+ Unknown ,
68
+ }
69
+
70
+ impl From < u32 > for Type {
71
+ /// Parse type from mode
72
+ ///
73
+ fn from ( mode : u32 ) -> Type {
74
+ use Type :: * ;
75
+ match type_bits ( mode) {
76
+ 0o001 => Fifo ,
77
+ 0o002 => CharDevice ,
78
+ 0o004 => Dir ,
79
+ 0o006 => BlockDevice ,
80
+ 0o010 => File ,
81
+ 0o012 => Symlink ,
82
+ 0o014 => Socket ,
83
+ 0o016 => Whiteout ,
84
+ _ => Unknown ,
85
+ }
86
+ }
87
+ }
88
+
89
+ /// Enum for specifying the context / "who" accesses in [is_allowed]
90
+ #[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
91
+ pub enum Accessor {
92
+ Other ,
93
+ Group ,
94
+ User ,
95
+ }
96
+
97
+ /// Enum for specifying the type of access in [is_allowed]
98
+ #[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
99
+ pub enum Access {
100
+ /// (Beware: execute has various meanings depending on the type of file)
101
+ Execute ,
102
+ Write ,
103
+ Read ,
104
+ }
105
+
106
+ /// Check whether `mode` represents an allowed (`true`) or denied (`false`) access
107
+ pub fn is_allowed ( by : Accessor , ty : Access , mode : u32 ) -> bool {
108
+ use Access :: * ;
109
+ use Accessor :: * ;
110
+ let by = match by {
111
+ User => 2 ,
112
+ Group => 1 ,
113
+ Other => 0 ,
114
+ } ;
115
+ let bits = ( mode >> ( 3 * by) ) & 0o7 ;
116
+ let ty = match ty {
117
+ Read => 2 ,
118
+ Write => 1 ,
119
+ Execute => 0 ,
120
+ } ;
121
+ bits & ( 1 << ty) != 0
122
+ }
123
+
46
124
/// Returns true if this mode represents a regular file.
47
125
///
48
126
/// ```
49
127
/// assert_eq!(unix_mode::is_file(0o0041777), false);
50
128
/// assert_eq!(unix_mode::is_file(0o0100640), true);
51
129
/// ```
52
130
pub fn is_file ( mode : u32 ) -> bool {
53
- type_bits ( mode) == 0o010
131
+ Type :: from ( mode) == Type :: File
54
132
}
55
133
56
134
/// Returns true if this mode represents a directory.
@@ -60,7 +138,7 @@ pub fn is_file(mode: u32) -> bool {
60
138
/// assert_eq!(unix_mode::is_dir(0o0100640), false);
61
139
/// ```
62
140
pub fn is_dir ( mode : u32 ) -> bool {
63
- type_bits ( mode) == 0o004
141
+ Type :: from ( mode) == Type :: Dir
64
142
}
65
143
66
144
/// Returns true if this mode represents a symlink.
@@ -70,27 +148,27 @@ pub fn is_dir(mode: u32) -> bool {
70
148
/// assert_eq!(unix_mode::is_symlink(0o0120755), true);
71
149
/// ```
72
150
pub fn is_symlink ( mode : u32 ) -> bool {
73
- type_bits ( mode) == 0o012
151
+ Type :: from ( mode) == Type :: Symlink
74
152
}
75
153
76
154
/// Returns true if this mode represents a fifo, also known as a named pipe.
77
155
pub fn is_fifo ( mode : u32 ) -> bool {
78
- type_bits ( mode) == 0o001
156
+ Type :: from ( mode) == Type :: Fifo
79
157
}
80
158
81
159
/// Returns true if this mode represents a character device.
82
160
pub fn is_char_device ( mode : u32 ) -> bool {
83
- type_bits ( mode) == 0o002
161
+ Type :: from ( mode) == Type :: CharDevice
84
162
}
85
163
86
164
/// Returns true if this mode represents a block device.
87
165
pub fn is_block_device ( mode : u32 ) -> bool {
88
- type_bits ( mode) == 0o006
166
+ Type :: from ( mode) == Type :: BlockDevice
89
167
}
90
168
91
169
/// Returns true if this mode represents a Unix-domain socket.
92
170
pub fn is_socket ( mode : u32 ) -> bool {
93
- type_bits ( mode) == 0o014
171
+ Type :: from ( mode) == Type :: Socket
94
172
}
95
173
96
174
/// Returns true if the set-user-ID bit is set
@@ -130,71 +208,43 @@ pub fn is_sticky(mode: u32) -> bool {
130
208
pub fn to_string ( mode : u32 ) -> String {
131
209
// This is decoded "by hand" here so that it'll work
132
210
// on non-Unix platforms.
211
+ use Access :: * ;
212
+ use Accessor :: * ;
213
+ use Type :: * ;
133
214
134
- fn bitset ( a : u32 , b : u32 ) -> bool {
135
- a & b != 0
136
- }
137
-
138
- fn permch ( mode : u32 , b : u32 , ch : char ) -> char {
139
- if bitset ( mode, b) {
140
- ch
141
- } else {
142
- '-'
143
- }
144
- }
145
-
146
- let mut s = String :: with_capacity ( 10 ) ;
147
- s. push ( match ( mode >> 12 ) & 0o17 {
148
- 0o001 => 'p' , // pipe/fifo
149
- 0o002 => 'c' , // character dev
150
- 0o004 => 'd' , // directory
151
- 0o006 => 'b' , // block dev
152
- 0o010 => '-' , // regular file
153
- 0o012 => 'l' , // link
154
- 0o014 => 's' , // socket
155
- 0o016 => 'w' , // whiteout
156
- _ => '?' , // unknown
157
- } ) ;
158
215
let setuid = is_setuid ( mode) ;
159
216
let setgid = is_setgid ( mode) ;
160
217
let sticky = is_sticky ( mode) ;
161
- s. push ( permch ( mode, 0o400 , 'r' ) ) ;
162
- s. push ( permch ( mode, 0o200 , 'w' ) ) ;
163
- let usrx = bitset ( mode, 0o100 ) ;
164
- if setuid && usrx {
165
- s. push ( 's' )
166
- } else if setuid && !usrx {
167
- s. push ( 'S' )
168
- } else if usrx {
169
- s. push ( 'x' )
170
- } else {
171
- s. push ( '-' )
172
- }
173
- // group
174
- s. push ( permch ( mode, 0o40 , 'r' ) ) ;
175
- s. push ( permch ( mode, 0o20 , 'w' ) ) ;
176
- let grpx = bitset ( mode, 0o10 ) ;
177
- if setgid && grpx {
178
- s. push ( 's' )
179
- } else if setgid && !grpx {
180
- s. push ( 'S' )
181
- } else if grpx {
182
- s. push ( 'x' )
183
- } else {
184
- s. push ( '-' )
185
- }
186
- // other
187
- s. push ( permch ( mode, 0o4 , 'r' ) ) ;
188
- s. push ( permch ( mode, 0o2 , 'w' ) ) ;
189
- let otherx = bitset ( mode, 0o1 ) ;
190
- if sticky && otherx {
191
- s. push ( 't' )
192
- } else if sticky && !otherx {
193
- s. push ( 'T' )
194
- } else if otherx {
195
- s. push ( 'x' )
196
- } else {
197
- s. push ( '-' )
218
+
219
+ let mut s = String :: with_capacity ( 10 ) ;
220
+ s. push ( match Type :: from ( mode) {
221
+ Fifo => 'p' ,
222
+ CharDevice => 'c' ,
223
+ Dir => 'd' ,
224
+ BlockDevice => 'b' ,
225
+ File => '-' ,
226
+ Symlink => 'l' ,
227
+ Socket => 's' ,
228
+ Whiteout => 'w' ,
229
+ Unknown => '?' ,
230
+ } ) ;
231
+ for accessor in [ User , Group , Other ] {
232
+ for access in [ Read , Write , Execute ] {
233
+ s. push (
234
+ match ( access, accessor, is_allowed ( accessor, access, mode) ) {
235
+ ( Execute , User , true ) if setuid => 's' ,
236
+ ( Execute , User , false ) if setuid => 'S' ,
237
+ ( Execute , Group , true ) if setgid => 's' ,
238
+ ( Execute , Group , false ) if setgid => 'S' ,
239
+ ( Execute , Other , true ) if sticky => 't' ,
240
+ ( Execute , Other , false ) if sticky => 'T' ,
241
+ ( Execute , _, true ) => 'x' ,
242
+ ( Write , _, true ) => 'w' ,
243
+ ( Read , _, true ) => 'r' ,
244
+ ( _, _, false ) => '-' ,
245
+ } ,
246
+ ) ;
247
+ }
198
248
}
199
249
s
200
250
}
@@ -229,6 +279,20 @@ mod unix_tests {
229
279
// we can't make one (without root.)
230
280
}
231
281
282
+ /// Test [is_allowed] against files likely to already exist on a Unix system.
283
+ #[ test]
284
+ fn existing_file_perms ( ) {
285
+ use Access :: * ;
286
+ use Accessor :: * ;
287
+ for by in [ User , Group , Other ] {
288
+ assert ! ( is_allowed( by, Read , file_mode( "/" ) ) ) ;
289
+ assert ! ( is_allowed( by, Execute , file_mode( "/" ) ) ) ;
290
+ assert ! ( is_allowed( by, Write , file_mode( "/dev/null" ) ) ) ;
291
+ }
292
+ assert ! ( !is_allowed( Other , Write , file_mode( "/dev/" ) ) ) ;
293
+ assert ! ( !is_allowed( Other , Execute , file_mode( "/dev/null" ) ) ) ;
294
+ }
295
+
232
296
#[ test]
233
297
fn stat_created_symlink ( ) {
234
298
let tmp_dir = tempdir ( ) . unwrap ( ) ;
0 commit comments