file_mode/
lib.rs

1/*!
2Decode Unix file mode bits, change them and apply them to files.
3
4All file type, special and protection bits described in `sys/stat.h` are represented.
5
6The [Mode] can represent a file mode partially by the use of a bitmask. Only modified bits will be changed in the target file.
7Modifications specific only to directories (search) are handled correctly.
8
9# Usage examples
10
11## Decoding mode bits
12
13```
14use std::path::Path;
15use file_mode::{ModePath, User};
16
17let mode = Path::new("LICENSE").mode().unwrap();
18
19assert!(mode.file_type().unwrap().is_regular_file());
20assert!(mode.user_protection(User::Owner).is_read_set());
21assert!(mode.user_protection(User::Group).is_write_set());
22assert!(!mode.user_protection(User::Other).is_execute_set());
23```
24
25## Applying chmod strings
26
27```
28use std::path::Path;
29use file_mode::ModePath;
30
31Path::new("LICENSE").set_mode("u+r,g+u").unwrap();
32```
33
34## Applying octal modes
35
36```
37use std::path::Path;
38use file_mode::ModePath;
39
40Path::new("LICENSE").set_mode(0o664).unwrap();
41```
42
43## Printing standard mode strings
44
45```
46use std::path::Path;
47use file_mode::ModePath;
48
49let mode = Path::new("LICENSE").mode().unwrap();
50
51println!("{}", mode); // -rw-rw-r--
52assert_eq!(&mode.to_string(), "-rw-rw-r--");
53```
54
55## Constructing Mode programmatically
56
57```
58use file_mode::{Mode, Protection, ProtectionBit, User};
59
60let mut mode = Mode::empty();
61let mut rw = Protection::empty();
62
63rw.set(ProtectionBit::Read);
64rw.set(ProtectionBit::Write);
65
66mode.set_protection(User::Owner, &rw);
67mode.set_protection(User::Group, &rw);
68mode.set_protection(User::Other, &ProtectionBit::Read.into());
69
70assert_eq!(mode.mode(), 0o664);
71
72mode.set_mode_path("LICENSE").unwrap();
73```
74
75## For non-Unix systems mode can still be used but not set to files
76
77```rust
78use file_mode::{Mode, Protection, ProtectionBit, User};
79
80let mut mode = Mode::empty();
81let mut rw = Protection::empty();
82
83rw.set(ProtectionBit::Read);
84rw.set(ProtectionBit::Write);
85
86mode.set_protection(User::Owner, &rw);
87mode.set_protection(User::Group, &rw);
88mode.set_protection(User::Other, &ProtectionBit::Read.into());
89
90assert_eq!(mode.apply_to(0o600), 0o664);
91```
92
93Use `Mode::from` to construct mode as if it was read from a file:
94
95```rust
96use file_mode::Mode;
97
98let mut mode = Mode::from(0o600);
99
100mode.set_str("g+rw,o+r").unwrap();
101
102assert_eq!(mode.mode(), 0o664);
103```
104
105# Features and platform support
106
107This carte should compile on all platforms.
108On non-Unix (not `target_family = "unix"`) systems, functions related to setting mode to files will not be available.
109
110## The calling process's umask
111
112On non-Unix systems, the calling process's umask is emulated via global variable and defaults to `0o002`.
113
114## Serde
115
116Implementations of `Serialize` and `Deserialize` for [Mode] can be enabled with `serde` feature flag.
117
118*/
119
120#[cfg(target_family = "unix")]
121use std::io;
122use std::fmt::{self, Debug, Display};
123#[cfg(target_family = "unix")]
124use std::path::Path;
125#[cfg(target_family = "unix")]
126use std::fs::{metadata, symlink_metadata, set_permissions, File};
127use std::iter::FromIterator;
128
129#[cfg(target_family = "unix")]
130use std::os::unix::fs::PermissionsExt;
131
132#[cfg(feature = "serde")]
133use serde::{Serialize, Deserialize};
134
135mod parser;
136pub use parser::ModeParseError;
137
138/* Encoding of the file mode.  */
139const S_IFMT: u32 =		0o170000;	/* These bits determine file type.  */
140
141/* File types.  */
142const S_IFDIR: u32 =	0o040000;	/* Directory.  */
143const S_IFCHR: u32 =	0o020000;	/* Character device.  */
144const S_IFBLK: u32 =	0o060000;	/* Block device.  */
145const S_IFREG: u32 =	0o100000;	/* Regular file.  */
146const S_IFIFO: u32 =	0o010000;	/* FIFO.  */
147const S_IFLNK: u32 =	0o120000;	/* Symbolic link.  */
148const S_IFSOCK: u32 =	0o140000;	/* Socket.  */
149
150
151/* Protection bits.  */
152const S_ISUID: u32 =	0o4000;		/* Set user ID on execution.  */
153const S_ISGID: u32 =	0o2000;		/* Set group ID on execution.  */
154const S_ISVTX: u32 =	0o1000;		/* Save swapped text after use (sticky).  */
155const S_IREAD: u32 =	0o400;		/* Read by owner.  */
156const S_IWRITE: u32 =	0o200;		/* Write by owner.  */
157const S_IEXEC: u32 =	0o100;		/* Execute by owner.  */
158
159/// Represents which user's access to the file will be changed.
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161pub enum User {
162    /// Represents 'u' flag
163    Owner,
164    /// Represents 'g' flag
165    Group,
166    /// Represents 'o' flag
167    Other,
168}
169
170/// Type of the file as encoded in the mode value.
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
172pub enum FileType {
173    Directory,
174    CharacterDevice,
175    BlockDevice,
176    RegularFile,
177    FIFO,
178    SymbolicLink,
179    Socket,
180}
181
182impl FileType {
183    /// Gets [FileType] from mode value.
184    pub fn from_mode(mode: u32) -> Option<FileType> {
185        use FileType::*;
186        match mode & S_IFMT {
187            S_IFDIR => Some(Directory),
188            S_IFCHR => Some(CharacterDevice),
189            S_IFBLK => Some(BlockDevice),
190            S_IFREG => Some(RegularFile),
191            S_IFIFO => Some(FIFO),
192            S_IFLNK => Some(SymbolicLink),
193            S_IFSOCK => Some(Socket),
194            _ => None
195        }
196    }
197
198    /// Returns [true] if file is a directory.
199    pub fn is_directory(&self) -> bool {
200        *self == FileType::Directory
201    }
202
203    /// Returns [true] if file is a character device.
204    pub fn is_character_device(&self) -> bool {
205        *self == FileType::CharacterDevice
206    }
207
208    /// Returns [true] if file is a block device.
209    pub fn is_block_device(&self) -> bool {
210        *self == FileType::BlockDevice
211    }
212
213    /// Returns [true] if file is a regular file.
214    pub fn is_regular_file(&self) -> bool {
215        *self == FileType::RegularFile
216    }
217
218    /// Returns [true] if file is a first-in first-out special file.
219    pub fn is_fifo(&self) -> bool {
220        *self == FileType::FIFO
221    }
222
223    /// Returns [true] if file is a symbolic link.
224    pub fn is_symbolic_link(&self) -> bool {
225        *self == FileType::SymbolicLink
226    }
227
228    /// Returns [true] if file is a socket endpoint.
229    pub fn is_socket(&self) -> bool {
230        *self == FileType::Socket
231    }
232
233    /// Gets [Mode] with corresponding file type bits set.
234    pub fn mode(&self) -> Mode {
235        use FileType::*;
236        let mode = match self {
237            Directory =>          S_IFDIR,
238            CharacterDevice =>    S_IFCHR,
239            BlockDevice =>        S_IFBLK,
240            RegularFile =>        S_IFREG,
241            FIFO =>               S_IFIFO,
242            SymbolicLink =>       S_IFLNK,
243            Socket =>             S_IFSOCK,
244        };
245
246        Mode::new(mode, S_IFMT)
247    }
248}
249
250/// Protection bit that can be set for a [User].
251#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
252pub enum ProtectionBit {
253    /// Permission to read.
254    Read,
255    /// Permission to write.
256    Write,
257    /// Permission to execute.
258    Execute,
259    /// Permission to search a directory - like execute but only applies to directories.
260    Search,
261}
262
263impl From<ProtectionBit> for Protection {
264    fn from(bit: ProtectionBit) -> Protection {
265        Protection::empty().with_set(bit)
266    }
267}
268
269#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
270enum ExecuteOrSearch {
271    Execute(bool),
272    Search(bool),
273}
274
275/// Protection bits for user's access to a file.
276#[derive(Debug, Clone)]
277pub struct Protection {
278    read: Option<bool>,
279    write: Option<bool>,
280    execute: Option<ExecuteOrSearch>,
281}
282
283impl Protection {
284    /// Constructs [Protection] with all bits forgotten.
285    pub fn empty() -> Protection {
286        Protection {
287            read: None,
288            write: None,
289            execute: None,
290        }
291    }
292
293    /// Constructs [Protection] with all bits set.
294    pub fn all_set() -> Protection {
295        Protection {
296            read: Some(true),
297            write: Some(true),
298            execute: Some(ExecuteOrSearch::Execute(true)),
299        }
300    }
301
302    /// Constructs [Protection] with all bits cleared.
303    pub fn all_clear() -> Protection {
304        Protection {
305            read: Some(false),
306            write: Some(false),
307            execute: Some(ExecuteOrSearch::Execute(false)),
308        }
309    }
310
311    /// Constructs [Protection] representing [User] access to the file described by [Mode].
312    pub fn from_mode_user(mode: &Mode, user: User) -> Protection {
313        let mut mask = mode.mask;
314        let mut dir_mask = mode.dir_mask;
315        let mut mode = mode.mode;
316
317        let owner_mask = S_IREAD | S_IWRITE | S_IEXEC;
318
319        let shift = Self::user_shift(user);
320        mode <<= shift;
321        mask <<= shift;
322        dir_mask <<= shift;
323
324        mode &= owner_mask;
325        mask &= owner_mask;
326        dir_mask &= owner_mask;
327
328        let mut ret = Protection::empty();
329
330        if mask & S_IREAD > 0 {
331            ret.read = Some(mode & S_IREAD > 0);
332        }
333
334        if mask & S_IWRITE > 0 {
335            ret.write = Some(mode & S_IWRITE > 0);
336        }
337
338        if mask & S_IEXEC > 0 {
339            if dir_mask & S_IEXEC > 0 {
340                ret.execute = Some(ExecuteOrSearch::Search(mode & S_IEXEC > 0));
341            } else {
342                ret.execute = Some(ExecuteOrSearch::Execute(mode & S_IEXEC > 0));
343            }
344        }
345
346        ret
347    }
348
349    /// Returns [true] if read permission bit is set.
350    pub fn is_read_set(&self) -> bool {
351        self.read == Some(true)
352    }
353
354    /// Returns [true] if write permission bit is set.
355    pub fn is_write_set(&self) -> bool {
356        self.write == Some(true)
357    }
358
359    /// Returns [true] if execute permission bit is set.
360    pub fn is_execute_set(&self) -> bool {
361        self.execute == Some(ExecuteOrSearch::Execute(true))
362    }
363
364    /// Returns [true] if execute or search permission bits are set.
365    pub fn is_search_set(&self) -> bool {
366        self.execute == Some(ExecuteOrSearch::Execute(true)) ||
367        self.execute == Some(ExecuteOrSearch::Search(true))
368    }
369
370    /// Returns self with given [ProtectionBit] set.
371    pub fn with_set(mut self, bit: ProtectionBit) -> Protection {
372        self.set(bit);
373        self
374    }
375
376    /// Returns self with given [ProtectionBit] cleared.
377    pub fn with_cleared(mut self, bit: ProtectionBit) -> Protection {
378        self.clear(bit);
379        self
380    }
381
382    /// Returns self with given [ProtectionBit] value forgotten.
383    pub fn with_forgotten(mut self, bit: ProtectionBit) -> Protection {
384        self.forget(bit);
385        self
386    }
387
388    /// Sets given [ProtectionBit].
389    pub fn set(&mut self, bit: ProtectionBit) {
390        match bit {
391            ProtectionBit::Read => self.read = Some(true),
392            ProtectionBit::Write => self.write = Some(true),
393            ProtectionBit::Execute => self.execute = Some(ExecuteOrSearch::Execute(true)),
394            ProtectionBit::Search => self.execute = Some(ExecuteOrSearch::Search(true)),
395        }
396    }
397
398    /// Clears given [ProtectionBit].
399    pub fn clear(&mut self, bit: ProtectionBit) {
400        match bit {
401            ProtectionBit::Read => self.read = Some(false),
402            ProtectionBit::Write => self.write = Some(false),
403            ProtectionBit::Execute => self.execute = Some(ExecuteOrSearch::Execute(false)),
404            ProtectionBit::Search => self.execute = Some(ExecuteOrSearch::Search(false)),
405        }
406    }
407
408    /// Forgets the value of given [ProtectionBit].
409    pub fn forget(&mut self, bit: ProtectionBit) {
410        match bit {
411            ProtectionBit::Read => self.read = None,
412            ProtectionBit::Write => self.write = None,
413            ProtectionBit::Execute | ProtectionBit::Search => self.execute = None,
414        }
415    }
416
417    fn user_shift(user: User) -> u32 {
418        match user {
419                User::Owner => 0,
420                User::Group => 3,
421                User::Other => 6,
422        }
423    }
424
425    /// Constructs [Mode] with protection bits set for [User].
426    pub fn for_user(&self, user: User) -> Mode {
427        let mut mode = Mode::empty();
428
429        if let Some(read) = self.read {
430            if read {
431                mode.mode |= S_IREAD;
432            } else {
433                mode.mode &= !S_IREAD;
434            }
435            mode.mask |= S_IREAD;
436        }
437
438        if let Some(write) = self.write {
439            if write {
440                mode.mode |= S_IWRITE;
441            } else {
442                mode.mode &= !S_IWRITE;
443            }
444            mode.mask |= S_IWRITE;
445        }
446
447        if let Some(execute) = self.execute {
448            match execute {
449                ExecuteOrSearch::Execute(execute) | ExecuteOrSearch::Search(execute) => if execute {
450                    mode.mode |= S_IEXEC;
451                } else {
452                    mode.mode &= !S_IEXEC;
453                }
454            }
455            mode.mask |= S_IEXEC;
456
457            if let ExecuteOrSearch::Search(_) = execute {
458                mode.dir_mask |= S_IEXEC;
459            }
460        }
461
462        let shift = Self::user_shift(user);
463        mode.mode >>= shift;
464        mode.mask >>= shift;
465        mode.dir_mask >>= shift;
466
467        mode
468    }
469}
470
471/// Special bit that can be set for a [User].
472#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
473pub enum SpecialBit {
474    /// Sets the effective user ID of the calling process.
475    SetId,
476    /// Sticky Bit.
477    Sticky,
478}
479
480impl From<SpecialBit> for Special {
481    fn from(bit: SpecialBit) -> Special {
482        Special::empty().with_set(bit)
483    }
484}
485
486/// Special bits set on a file.
487#[derive(Debug, Clone)]
488pub struct Special {
489    set_id: Option<bool>,
490    sticky: Option<bool>,
491}
492
493impl Special {
494    pub fn empty() -> Special {
495        Special {
496            set_id: None,
497            sticky: None,
498        }
499    }
500
501    /// Constructs [Special] representing [User]-specific special bits set on the file described by [Mode].
502    ///
503    /// * For [User::Owner] the [SpecialBit::SetId] bit will represent the `set-user-ID` mode bit.
504    /// * For [User::Group] the [SpecialBit::SetId] bit will represent the `set-group-ID` mode bit.
505    pub fn from_mode_user(mode: &Mode, user: User) -> Special {
506        let mut ret = Special::empty();
507
508        match user {
509            User::Owner if mode.mask & S_ISUID > 0 => ret.set_id = Some(mode.mode & S_ISUID > 0),
510            User::Group if mode.mask & S_ISGID > 0 => ret.set_id = Some(mode.mode & S_ISGID > 0),
511            _ => (),
512        }
513
514        if mode.mask & S_ISVTX > 0 {
515            ret.sticky = Some(mode.mode & S_ISVTX > 0);
516        }
517
518        ret
519    }
520
521    /// Returns [true] if set ID bit is set.
522    ///
523    /// * For [User::Owner] the bit will represent the `set-user-ID` mode bit.
524    /// * For [User::Group] the bit will represent the `set-group-ID` mode bit.
525    pub fn is_set_id_set(&self) -> bool {
526        self.set_id == Some(true)
527    }
528
529    /// Returns [true] if sticky bit is set.
530    pub fn is_sticky_set(&self) -> bool {
531        self.sticky == Some(true)
532    }
533
534    /// Returns self with given [SpecialBit] set.
535    pub fn with_set(mut self, bit: SpecialBit) -> Special {
536        self.set(bit);
537        self
538    }
539
540    /// Returns self with given [SpecialBit] cleared.
541    pub fn with_cleared(mut self, bit: SpecialBit) -> Special {
542        self.clear(bit);
543        self
544    }
545
546    /// Returns self with given [SpecialBit] value forgotten.
547    pub fn with_forgotten(mut self, bit: SpecialBit) -> Special {
548        self.forget(bit);
549        self
550    }
551
552    /// Sets given [SpecialBit].
553    pub fn set(&mut self, bit: SpecialBit) {
554        match bit {
555            SpecialBit::SetId => self.set_id = Some(true),
556            SpecialBit::Sticky => self.sticky = Some(true),
557        }
558    }
559
560    /// Clears given [SpecialBit].
561    pub fn clear(&mut self, bit: SpecialBit) {
562        match bit {
563            SpecialBit::SetId => self.set_id = Some(false),
564            SpecialBit::Sticky => self.sticky = Some(false),
565        }
566    }
567
568    /// Forgets the value of given [SpecialBit].
569    pub fn forget(&mut self, bit: SpecialBit) {
570        match bit {
571            SpecialBit::SetId => self.set_id = None,
572            SpecialBit::Sticky => self.sticky = None,
573        }
574    }
575
576    /// Constructs [Mode] with special bits set according to [User].
577    ///
578    /// * For [User::Owner] the [SpecialBit::SetId] bit will represent the `set-user-ID` mode bit.
579    /// * For [User::Group] the [SpecialBit::SetId] bit will represent the `set-group-ID` mode bit.
580    pub fn for_user(&self, user: User) -> Mode {
581        let mut mode = Mode::empty();
582
583        if let Some(sticky) = self.sticky {
584            if sticky {
585                mode.mode |= S_ISVTX;
586            } else {
587                mode.mode &= !S_ISVTX;
588            }
589            mode.mask |= S_ISVTX;
590        }
591
592        if let Some(set_id) = self.set_id {
593            match user {
594                User::Owner => {
595                    if set_id {
596                        mode.mode |= S_ISUID;
597                    } else {
598                        mode.mode &= !S_ISUID;
599                    }
600                    mode.mask |= S_ISUID;
601                }
602                User::Group => {
603                    if set_id {
604                        mode.mode |= S_ISGID;
605                    } else {
606                        mode.mode &= !S_ISGID;
607                    }
608                    mode.mask |= S_ISGID;
609                }
610                _ => (), // don't set for other
611            }
612        }
613
614        mode
615    }
616}
617
618/// Unix file mode.
619///
620/// All file type, special and protection bits described in `sys/stat.h` are represented.
621///
622/// The `Mode` can represent a file mode partially by the use of a bitmask. Only modified bits will be changed in the target file.
623/// Modifications specific only to directories (see [ProtectionBit::Search]) are handled correctly.
624///
625/// The `Mode` can be displayed as a string according to the POSIX standard for the `ls` command.
626///
627/// Mode bits can be set with `chmod` compatible string.
628#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
629#[derive(Clone, PartialEq, Eq, Hash)]
630pub struct Mode {
631    mode: u32,
632    mask: u32,
633    // bits set only apply to directories
634    dir_mask: u32,
635}
636
637impl Debug for Mode {
638    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
639        f.debug_struct("Mode")
640            .field("mode", &format_args!("{:06o}", self.mode))
641            .field("mask", &format_args!("{:06o}", self.mask))
642            .field("dirm", &format_args!("{:06o}", self.dir_mask))
643            .finish()
644    }
645}
646
647impl Display for Mode {
648    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
649        use FileType::*;
650        match self.file_type() {
651            Some(Directory) =>          f.write_str("d")?,
652            Some(CharacterDevice) =>    f.write_str("c")?,
653            Some(BlockDevice) =>        f.write_str("b")?,
654            Some(RegularFile) =>        f.write_str("-")?,
655            Some(FIFO) =>               f.write_str("p")?,
656            Some(SymbolicLink) =>       f.write_str("l")?,
657            Some(Socket) =>             f.write_str("s")?,
658            None =>                     f.write_str("?")?,
659        }
660
661        use User::*;
662        for user in &[Owner, Group, Other] {
663            let special = self.user_special(*user);
664            let protection = self.user_protection(*user);
665
666            if protection.is_read_set() {
667                f.write_str("r")?;
668            } else {
669                f.write_str("-")?;
670            }
671
672            if protection.is_write_set() {
673                f.write_str("w")?;
674            } else {
675                f.write_str("-")?;
676            }
677
678            match user {
679                Owner | Group => match (special.is_set_id_set(), protection.is_execute_set(), protection.is_search_set()) {
680                    (true, true, _) => f.write_str("s")?,
681                    (true, false, _) => f.write_str("S")?,
682                    (false, false, true) => f.write_str("X")?,
683                    (false, true, _) => f.write_str("x")?,
684                    (false, false, _) => f.write_str("-")?,
685                }
686                Other => match (special.is_sticky_set(), protection.is_execute_set(), protection.is_search_set()) {
687                    (true, true, _) => f.write_str("t")?,
688                    (true, false, _) => f.write_str("T")?,
689                    (false, false, true) => f.write_str("X")?,
690                    (false, true, _) => f.write_str("x")?,
691                    (false, false, _) => f.write_str("-")?,
692                }
693            }
694        }
695
696        Ok(())
697    }
698}
699
700impl Mode {
701    /// Constructs [Mode] from mode value and mask representing which special and permissions bits
702    /// will be applied to the target file mode.
703    pub fn new(mode: u32, mask: u32) -> Mode {
704        Mode {
705            mode: mode & (mask | S_IFMT),
706            mask,
707            dir_mask: 0,
708        }
709    }
710
711    /// Constructs [Mode] with no bits sets and empty mask.
712    pub fn empty() -> Mode {
713        Mode {
714            mode: 0,
715            mask: 0,
716            dir_mask: 0,
717        }
718    }
719
720    /// Constructs [Mode] from mode value of the file represented by the path.
721    ///
722    /// The mask will be set to `0o7777` meaning that all the special and permissions bits will be applied
723    /// to the target file mode.
724    #[cfg(target_family = "unix")]
725    pub fn from_path(path: impl AsRef<Path>) -> Result<Mode, io::Error> {
726        Ok(Mode::new(metadata(path.as_ref())?.permissions().mode(), 0o7777 | S_IFMT))
727    }
728
729    /// Like [Mode::from_path] but will not follow symbolic links.
730    #[cfg(target_family = "unix")]
731    pub fn from_path_nofollow(path: impl AsRef<Path>) -> Result<Mode, io::Error> {
732        Ok(Mode::new(symlink_metadata(path.as_ref())?.permissions().mode(), 0o7777 | S_IFMT))
733    }
734
735    /// Constructs [Mode] from mode value of the file.
736    ///
737    /// The mask will be set to `0o7777` meaning that all the special and permissions bits will be applied
738    /// to the target file mode.
739    #[cfg(target_family = "unix")]
740    pub fn from_file(file: &File) -> Result<Mode, io::Error> {
741        Ok(Mode::new(file.metadata()?.permissions().mode(), 0o7777 | S_IFMT))
742    }
743
744    /// Gets mode value.
745    pub fn mode(&self) -> u32 {
746        self.mode_mask().0
747    }
748
749    /// Gets mode value and mask that represents bits of the mode that would be applied to the target
750    /// file mode.
751    ///
752    /// If the mode represents a directory the search bit will be handled accordingly.
753    pub fn mode_mask(&self) -> (u32, u32) {
754        if let Some(true) = self.file_type().map(|m| m.is_directory()) {
755            (self.mode, self.mask)
756        } else {
757            (self.mode & !self.dir_mask, self.mask & !self.dir_mask)
758        }
759    }
760
761    /// Gets [FileType] if corresponding bit patterns are present and valid in the mode value.
762    pub fn file_type(&self) -> Option<FileType> {
763        FileType::from_mode(self.mode)
764    }
765
766    /// Gets [Protection] bits for [User].
767    pub fn user_protection(&self, user: User) -> Protection {
768        Protection::from_mode_user(self, user)
769    }
770
771    /// Gets [Special] bits for [User].
772    ///
773    /// * For [User::Owner] the [SpecialBit::SetId] bit will represent the `set-user-ID` mode bit.
774    /// * For [User::Group] the [SpecialBit::SetId] bit will represent the `set-group-ID` mode bit.
775    pub fn user_special(&self, user: User) -> Special {
776        Special::from_mode_user(self, user)
777    }
778
779    /// Sets bits that are set in the given mode according to mask.
780    pub fn add(&mut self, mode: &Mode) {
781        self.mode |= mode.mode;
782        self.mask |= mode.mask;
783        self.dir_mask |= mode.dir_mask;
784    }
785
786    /// Clears bits that are set in the given mode according to mask.
787    pub fn sub(&mut self, mode: &Mode) {
788        self.mode &= !mode.mode;
789        self.mask |= mode.mask;
790        self.dir_mask |= mode.dir_mask;
791    }
792
793    /// Sets values of bits that are set or clear in the given mode according to mask.
794    pub fn set(&mut self, mode: &Mode) {
795        self.mode &= !mode.mask;
796        self.mode |= mode.mode;
797        self.mask |= mode.mask;
798        self.dir_mask |= mode.dir_mask;
799    }
800
801    /// Forgets values of bits that are set or clear in the given mode by clearing bits in the mask.
802    pub fn forget(&mut self, mode: &Mode) {
803        self.mode &= !mode.mask;
804        self.mask &= !mode.mask;
805        self.dir_mask &= !mode.dir_mask;
806    }
807
808    /// Returns self with given bit pattern representing [FileType] set.
809    pub fn with_file_type(mut self, file_type: FileType) -> Mode {
810        self.set(&file_type.mode());
811        self
812    }
813
814    /// Returns self with bit pattern and mask for [Protection] bits set for [User].
815    pub fn with_protection(mut self, user: User, protection: &Protection) -> Mode {
816        self.set(&protection.for_user(user));
817        self
818    }
819
820    /// Returns self with bit pattern and mask for [Special] bits set for [User].
821    ///
822    /// * For [User::Owner] the [SpecialBit::SetId] bit will represent the `set-user-ID` mode bit.
823    /// * For [User::Group] the [SpecialBit::SetId] bit will represent the `set-group-ID` mode bit.
824    pub fn with_special(mut self, user: User, special: &Special) -> Mode {
825        self.set(&special.for_user(user));
826        self
827    }
828
829    /// Sets bit pattern representing given [FileType].
830    pub fn set_file_type(&mut self, file_type: FileType) {
831        self.set(&file_type.mode())
832    }
833
834    /// Sets bit pattern and mask for [Protection] bits for [User].
835    pub fn set_protection(&mut self, user: User, protection: &Protection) {
836        self.set(&protection.for_user(user))
837    }
838
839    /// Sets bit pattern and mask for [Special] bits for [User].
840    ///
841    /// * For [User::Owner] the [SpecialBit::SetId] bit will represent the `set-user-ID` mode bit.
842    /// * For [User::Group] the [SpecialBit::SetId] bit will represent the `set-group-ID` mode bit.
843    pub fn set_special(&mut self, user: User, special: &Special) {
844        self.set(&special.for_user(user))
845    }
846
847    /// Sets bits according to the mode string (as described in Linux `chmod` manual).
848    ///
849    /// Only bits described by the string will be represented in the mask.
850    ///
851    /// Note: Current value of mode is used as a reference for operations that use the
852    /// user as the source of bits (like `g+u`). Consider constructing [Mode]
853    /// using [Mode::from_file] or [Mode::from_path] before using this method.
854    pub fn set_str(&mut self, mode_str: &str) -> Result<(), ModeParseError> {
855        self.set_str_umask(mode_str, umask())
856    }
857
858    /// Like [Mode::set_str] but allows to specify umask value to use instead of the calling process's umask.
859    pub fn set_str_umask(&mut self, mode_str: &str, umask: u32) -> Result<(), ModeParseError> {
860        parser::mode_set_from_str(self, mode_str, umask)
861    }
862
863    /// Clears protection bits set in the umask value if they were set.
864    pub fn apply_umask(&mut self, mut umask: u32) {
865        umask &= 0o777;
866        self.mode &= !umask;
867    }
868
869    /// Applies mode changes to given mode value.
870    ///
871    /// Only bits set in the mask are modified.
872    ///
873    /// Returns the resulting mode value.
874    ///
875    /// If the mode value passed as an argument represents a directory the search bit will be handled
876    /// accordingly.
877    pub fn apply_to(&self, mut mode: u32) -> u32 {
878        let (amode, amask) = if let Some(true) = FileType::from_mode(mode).map(|m| m.is_directory()) {
879            (self.mode, self.mask)
880        } else {
881            (self.mode & !self.dir_mask, self.mask & !self.dir_mask)
882        };
883
884        mode &= !amask;
885        mode | amode
886    }
887
888    /// Applies mode changes to the mode of a file represented by the path.
889    ///
890    /// Only bits set in the mask are modified.
891    ///
892    /// Returns the resulting mode value.
893    ///
894    /// If the mode value of the file passed as an argument represents a directory the search bit will be handled accordingly.
895    ///
896    /// The file's mode is not modified.
897    #[cfg(target_family = "unix")]
898    pub fn apply_to_path(&self, path: impl AsRef<Path>) -> Result<u32, io::Error> {
899        let file_mode = metadata(path.as_ref())?.permissions().mode();
900        Ok(self.apply_to(file_mode))
901    }
902
903    /// Like [Mode::apply_to_path] but does not follow symbolic links.
904    #[cfg(target_family = "unix")]
905    pub fn apply_to_path_nofollow(&self, path: impl AsRef<Path>) -> Result<u32, io::Error> {
906        let file_mode = symlink_metadata(path.as_ref())?.permissions().mode();
907        Ok(self.apply_to(file_mode))
908    }
909
910    /// Sets the mode of the file, represented by the path, by applying the mode changes to its
911    /// current mode.
912    ///
913    /// Only bits set in the mask are modified.
914    ///
915    /// Returns the new file mode value.
916    ///
917    /// If the mode value of the file passed as an argument represents a directory the search bit will be handled accordingly.
918    ///
919    /// The file's mode **is** modified.
920    #[cfg(target_family = "unix")]
921    pub fn set_mode_path(&self, path: impl AsRef<Path>) -> Result<u32, io::Error> {
922        let path = path.as_ref();
923        let mut perms = metadata(path)?.permissions();
924        let mode = self.apply_to(perms.mode());
925        perms.set_mode(mode);
926        set_permissions(path, perms)?;
927        Ok(mode)
928    }
929
930    /// Like [Mode::set_mode_path] but does not follow symbolic links.
931    #[cfg(target_family = "unix")]
932    pub fn set_mode_path_nofollow(&self, path: impl AsRef<Path>) -> Result<u32, io::Error> {
933        let path = path.as_ref();
934        let mut perms = symlink_metadata(path)?.permissions();
935        let mode = self.apply_to(perms.mode());
936        perms.set_mode(mode);
937        set_permissions(path, perms)?;
938        Ok(mode)
939    }
940
941    /// Sets the mode of the file by applying the mode changes to its current mode.
942    ///
943    /// Only bits set in the mask are modified.
944    ///
945    /// Returns the new file mode value.
946    ///
947    /// If the mode value of the file passed as an argument represents a directory the search bit will be handled accordingly.
948    ///
949    /// The file's mode **is** modified.
950    #[cfg(target_family = "unix")]
951    pub fn set_mode_file(&self, file: &File) -> Result<u32, io::Error> {
952        let mut perms = file.metadata()?.permissions();
953        let mode = self.apply_to(perms.mode());
954        perms.set_mode(mode);
955        file.set_permissions(perms)?;
956        Ok(mode)
957    }
958}
959
960impl From<u32> for Mode {
961    fn from(mode: u32) -> Mode {
962        Mode::new(mode, 0o7777)
963    }
964}
965
966impl FromIterator<Mode> for Mode {
967    fn from_iter<I: IntoIterator<Item=Mode>>(iter: I) -> Self {
968        let mut mode = Mode::empty();
969
970        for m in iter {
971            mode.set(&m);
972        }
973
974        mode
975    }
976}
977
978
979#[cfg(target_family = "unix")]
980mod unix {
981    use super::*;
982    use std::error::Error;
983    use std::convert::Infallible;
984
985    /// Error setting mode to a file.
986    #[derive(Debug)]
987    pub enum ModeError {
988        ModeParseError(ModeParseError),
989        IoError(io::Error),
990    }
991
992    impl Display for ModeError {
993        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
994            use ModeError::*;
995            match self {
996                ModeParseError(_) => write!(f, "error parsing file mode string"),
997                IoError(_) => write!(f, "I/O error setting file mode"),
998            }
999        }
1000    }
1001
1002    impl Error for ModeError {
1003        fn source(&self) -> Option<&(dyn Error + 'static)> {
1004            use ModeError::*;
1005            match self {
1006                ModeParseError(err) => Some(err),
1007                IoError(err) => Some(err),
1008            }
1009        }
1010    }
1011
1012    impl From<ModeParseError> for ModeError {
1013        fn from(err: ModeParseError) -> ModeError {
1014            ModeError::ModeParseError(err)
1015        }
1016    }
1017
1018    impl From<io::Error> for ModeError {
1019        fn from(err: io::Error) -> ModeError {
1020            ModeError::IoError(err)
1021        }
1022    }
1023
1024    impl From<Infallible> for ModeError {
1025        fn from(_err: Infallible) -> ModeError {
1026            unreachable!()
1027        }
1028    }
1029
1030    /// Types that convert to this type can be used with `set_mode` methods.
1031    pub enum SetMode<'s> {
1032        Value(Mode),
1033        Str(&'s str),
1034    }
1035
1036    impl<'s, M: Into<Mode>> From<M> for SetMode<'s> {
1037        fn from(val: M) -> SetMode<'s> {
1038            SetMode::Value(val.into())
1039        }
1040    }
1041
1042    impl<'s> From<&'s str> for SetMode<'s> {
1043        fn from(val: &'s str) -> SetMode<'s> {
1044            SetMode::Str(val)
1045        }
1046    }
1047
1048    /// Extension methods for objects that can reference as [Path](std::path::Path).
1049    pub trait ModePath {
1050        /// Constructs [Mode] from mode value of the file.
1051        ///
1052        /// See [Mode::from_path] for details.
1053        fn mode(&self) -> Result<Mode, io::Error>;
1054
1055        /// Sets the mode of the file, represented by this path, by applying the mode changes to its
1056        /// current mode.
1057        ///
1058        /// See [Mode::set_mode_path] for details.
1059        fn set_mode<'s, M: Into<SetMode<'s>>>(&self, mode: M) -> Result<u32, ModeError>;
1060    }
1061
1062    impl<T: AsRef<Path>> ModePath for T {
1063        fn mode(&self) -> Result<Mode, io::Error> {
1064            Mode::from_path(self.as_ref())
1065        }
1066
1067        fn set_mode<'s, M: Into<SetMode<'s>>>(&self, mode: M) -> Result<u32, ModeError> {
1068            let mut m = Mode::from_path(self)?; // need file mode as reference
1069            match mode.into() {
1070                SetMode::Value(val) => m.set(&val),
1071                SetMode::Str(val) => m.set_str(val)?, // this will use file mode as reference
1072            }
1073            Ok(m.set_mode_path(self)?)
1074        }
1075    }
1076
1077    /// Extension methods for [File](std::fs::File).
1078    pub trait ModeFile {
1079        /// Constructs [Mode] from mode value of the file.
1080        ///
1081        /// See [Mode::from_path] for details.
1082        fn mode(&self) -> Result<Mode, io::Error>;
1083
1084        /// Sets the mode of this file by applying the mode changes to its current mode.
1085        ///
1086        /// See [Mode::set_mode_file].
1087        fn set_mode<'s, M: Into<SetMode<'s>>>(&self, mode: M) -> Result<u32, ModeError>;
1088    }
1089
1090    impl ModeFile for File {
1091        fn mode(&self) -> Result<Mode, io::Error> {
1092            Mode::from_file(&self)
1093        }
1094
1095        fn set_mode<'s, M: Into<SetMode<'s>>>(&self, mode: M) -> Result<u32, ModeError> {
1096            let mut m = Mode::from_file(self)?; // need file mode as reference
1097            match mode.into() {
1098                SetMode::Value(val) => m.set(&val),
1099                SetMode::Str(val) => m.set_str(val)?, // this will use file mode as reference
1100            }
1101            Ok(m.set_mode_file(self)?)
1102        }
1103    }
1104
1105    /// Sets the calling process's umask.
1106    ///
1107    /// Note: On non-Unix systems this is emulated by global variable.
1108    pub fn set_umask(umask: u32) -> u32 {
1109        unsafe {
1110            libc::umask(umask as libc::mode_t) as u32
1111        }
1112    }
1113
1114    /// Gets the calling process's umask.
1115    ///
1116    /// Note: On non-Unix systems this is emulated by global variable.
1117    pub fn umask() -> u32 {
1118        let m = set_umask(0);
1119        set_umask(m);
1120        m
1121    }
1122}
1123
1124#[cfg(target_family = "unix")]
1125pub use unix::*;
1126
1127#[cfg(not(target_family = "unix"))]
1128mod non_unix {
1129    use std::sync::atomic::{AtomicU32, Ordering};
1130
1131    static UMASK: AtomicU32 = AtomicU32::new(0o002);
1132
1133    /// Sets the calling process's umask.
1134    ///
1135    /// Note: On non-Unix systems this is emulated by global variable.
1136    pub fn set_umask(umask: u32) -> u32 {
1137        UMASK.swap(umask, Ordering::Release)
1138    }
1139
1140    /// Gets the calling process's umask.
1141    ///
1142    /// Note: On non-Unix systems this is emulated by global variable.
1143    pub fn umask() -> u32 {
1144        UMASK.load(Ordering::Acquire)
1145    }
1146}
1147
1148#[cfg(not(target_family = "unix"))]
1149pub use non_unix::*;
1150
1151#[cfg(test)]
1152mod tests {
1153    use super::*;
1154    use parking_lot::Mutex;
1155
1156    const UMASK_LOCK: Mutex<()> = parking_lot::const_mutex(());
1157
1158    #[test]
1159    fn test_mode() {
1160        assert_eq!(Mode::new(0o700, 0o700).apply_to(0o412), 0o712);
1161        assert_eq!(Mode::new(0o700, 0o400).apply_to(0o412), 0o412);
1162        assert_eq!(Mode::new(0o000, 0o700).apply_to(0o412), 0o012);
1163        assert_eq!(Mode::new(0o000, 0o111).apply_to(0o412), 0o402);
1164        assert_eq!(Mode::new(0o111, 0o111).apply_to(0o412), 0o513);
1165        assert_eq!(Mode::new(0o777, 0o111).apply_to(0o412), 0o513);
1166    }
1167
1168    #[test]
1169    fn test_protection() {
1170        use ProtectionBit::*;
1171        use User::*;
1172
1173        let mut o_700 = Protection::empty();
1174        o_700.set(Read);
1175        assert!(o_700.is_read_set());
1176        assert!(!o_700.is_write_set());
1177        assert!(!o_700.is_execute_set());
1178
1179        o_700.set(Write);
1180        assert!(o_700.is_read_set());
1181        assert!(o_700.is_write_set());
1182        assert!(!o_700.is_execute_set());
1183
1184        o_700.set(Execute);
1185        assert!(o_700.is_read_set());
1186        assert!(o_700.is_write_set());
1187        assert!(o_700.is_execute_set());
1188
1189        assert_eq!(o_700.for_user(Owner), Mode::new(0o700, 0o700));
1190        assert_eq!(o_700.for_user(Group), Mode::new(0o070, 0o070));
1191        assert_eq!(o_700.for_user(Other), Mode::new(0o007, 0o007));
1192
1193        let mut o_100 = Protection::empty();
1194        o_100.set(Execute);
1195
1196        assert_eq!(o_100.for_user(Owner), Mode::new(0o100, 0o100));
1197        assert_eq!(o_100.for_user(Group), Mode::new(0o010, 0o010));
1198        assert_eq!(o_100.for_user(Other), Mode::new(0o001, 0o001));
1199
1200        let mut o_200 = Protection::empty();
1201        o_200.set(Write);
1202
1203        assert_eq!(o_200.for_user(Owner), Mode::new(0o200, 0o200));
1204        assert_eq!(o_200.for_user(Group), Mode::new(0o020, 0o020));
1205        assert_eq!(o_200.for_user(Other), Mode::new(0o002, 0o002));
1206
1207        let mut o_600 = o_700.clone();
1208        o_600.clear(Execute);
1209        assert_eq!(o_600.for_user(Owner), Mode::new(0o600, 0o700));
1210
1211        let mut o_777 = o_700.for_user(Owner);
1212        o_777.set(&o_700.for_user(Group));
1213        o_777.set(&o_700.for_user(Other));
1214
1215        assert_eq!(o_777, Mode::new(0o777, 0o777));
1216
1217        let mut o_123 = Mode::empty();
1218        o_123.set_protection(Owner, &Protection::empty().with_set(Execute));
1219        o_123.set_protection(Group, &Protection::empty().with_set(Write));
1220        o_123.set_protection(Other, &Protection::empty().with_set(Execute).with_set(Write));
1221
1222        assert_eq!(o_123, Mode::new(0o123, 0o123));
1223
1224        let mut o_102 = o_123;
1225        o_102.set_protection(Group, &Protection::empty().with_cleared(Write));
1226        o_102.set_protection(Other, &Protection::empty().with_cleared(Execute));
1227
1228        assert_eq!(o_102, Mode::new(0o102, 0o123));
1229    }
1230
1231    #[test]
1232    fn test_special() {
1233        use SpecialBit::*;
1234
1235        let mut mode = Mode::empty();
1236        mode.set_special(User::Owner, &SetId.into());
1237        assert_eq!(mode, Mode::new(0o4000, 0o4000));
1238        mode.set_special(User::Group, &SetId.into());
1239        assert_eq!(mode, Mode::new(0o6000, 0o6000));
1240
1241        let mut mode = Mode::empty();
1242        mode.set_special(User::Other, &SetId.into());
1243        assert_eq!(mode, Mode::new(0o0000, 0o0000)); // according to chmod behaviour
1244
1245        let mut mode = Mode::empty();
1246        mode.set_special(User::Owner, &Sticky.into());
1247        assert_eq!(mode, Mode::new(0o1000, 0o1000));
1248        mode.set_special(User::Group, &Sticky.into());
1249        assert_eq!(mode, Mode::new(0o1000, 0o1000));
1250        mode.set_special(User::Other, &Sticky.into());
1251        assert_eq!(mode, Mode::new(0o1000, 0o1000));
1252
1253        mode.set_special(User::Group, &SetId.into());
1254        assert_eq!(mode, Mode::new(0o3000, 0o3000));
1255
1256        mode.set_special(User::Owner, &SetId.into());
1257        assert_eq!(mode, Mode::new(0o7000, 0o7000));
1258
1259        let mut mode = Mode::empty();
1260        let special = Special::empty();
1261        mode.set_special(User::Owner, &special);
1262        assert_eq!(mode, Mode::new(0o0000, 0o0000));
1263
1264        let special = mode.user_special(User::Owner);
1265        assert!(!special.is_set_id_set());
1266        assert!(!special.is_sticky_set());
1267
1268        mode.set_special(User::Group, &special);
1269        assert_eq!(mode, Mode::new(0o0000, 0o0000));
1270
1271        let special = mode.user_special(User::Owner);
1272        assert!(!special.is_set_id_set());
1273        assert!(!special.is_sticky_set());
1274
1275        let mut mode = Mode::empty();
1276        let mut special = Special::empty();
1277
1278        special.set(SetId);
1279        special.set(Sticky);
1280
1281        mode.set_special(User::Owner, &special);
1282        assert_eq!(mode, Mode::new(0o5000, 0o5000));
1283
1284        let special = mode.user_special(User::Owner);
1285        assert!(special.is_set_id_set());
1286        assert!(special.is_sticky_set());
1287
1288        mode.set_special(User::Group, &special);
1289        assert_eq!(mode, Mode::new(0o7000, 0o7000));
1290
1291        let special = mode.user_special(User::Owner);
1292        assert!(special.is_set_id_set());
1293        assert!(special.is_sticky_set());
1294    }
1295
1296    #[test]
1297    fn test_set_str_umask_symbolic() {
1298        let mut mode = Mode::empty();
1299        mode.set_str("u=rwx,g=rw,o+x").unwrap();
1300        assert_eq!(mode, Mode::new(0o761, 0o771));
1301
1302        let mut mode = Mode::new(0o2777, 0o2777);
1303        mode.set_str_umask("o=t", 0o002).unwrap();
1304        assert_eq!(mode, Mode::new(0o3770, 0o3777)); // according to chmod behaviour
1305
1306        let mut mode = Mode::new(0o1770, 0o1770);
1307        mode.set_str_umask("g+s", 0o002).unwrap();
1308        assert_eq!(mode, Mode::new(0o3770, 0o3770)); // according to chmod behaviour
1309
1310        let mut mode = Mode::new(0o0, 0o0);
1311        mode.set_str_umask("=rwx", 0o002).unwrap();
1312        assert_eq!(mode, Mode::new(0o0775, 0o0777)); // according to chmod behaviour; with umask 0o002
1313
1314        let mut mode = Mode::new(0o0, 0o0);
1315        mode.set_str_umask("=s", 0o002).unwrap();
1316        assert_eq!(mode, Mode::new(0o6000, 0o6777)); // according to chmod behaviour; with umask 0o002
1317
1318        let mut mode = Mode::new(0o0, 0o0);
1319        mode.set_str_umask("=", 0o002).unwrap();
1320        assert_eq!(mode, Mode::new(0o0000, 0o0777)); // according to chmod behaviour; with umask 0o002
1321
1322        let mut mode = Mode::new(0o777, 0o7777);
1323        mode.set_str_umask("+w", 0o002).unwrap();
1324        assert_eq!(mode, Mode::new(0o777, 0o7777));
1325
1326        let mut mode = Mode::new(0o000, 0o7777);
1327        mode.set_str_umask("+w", 0o002).unwrap();
1328        assert_eq!(mode, Mode::new(0o220, 0o7777));
1329
1330        let mut mode = Mode::new(0o700, 0o700);
1331        mode.set_str_umask("o=u", 0o002).unwrap();
1332        assert_eq!(mode, Mode::new(0o707, 0o707)); // according to chmod behaviour
1333
1334        let mut mode = Mode::new(0o100, 0o100);
1335        mode.set_str_umask("o=u", 0o002).unwrap();
1336        assert_eq!(mode, Mode::new(0o101, 0o107)); // according to chmod behaviour
1337
1338        let mut mode = Mode::new(0o000, 0o000);
1339        mode.set_str_umask("u=rw,og=u", 0o002).unwrap();
1340        assert_eq!(mode, Mode::new(0o666, 0o777)); // according to chmod behaviour
1341
1342        let mut mode = Mode::new(0o700, 0o700);
1343        mode.set_str_umask("o=u,g=o", 0o002).unwrap();
1344        assert_eq!(mode, Mode::new(0o777, 0o777)); // according to chmod behaviour
1345
1346        let mut mode = Mode::new(0o604, 0o777); // file is rw-rw-r--
1347        mode.set_str("u+r,g+u").unwrap();
1348        assert_eq!(mode.apply_to(0o664), 0o664); // according to chmod behaviour
1349
1350        let mut mode = Mode::new(0o664, 0o777); // file is rw-rw-r--
1351        mode.set_str("u+r,g-u").unwrap(); // g-u will copy rw- from u and unset bot bits
1352        assert_eq!(mode.apply_to(0o664), 0o604); // according to chmod behaviour
1353
1354        let dir = 0o040664; // no exec/search
1355        let file = 0o100664; // no exec/search
1356
1357        let mut mode = Mode::empty();
1358        mode.set_str("u+x,g+X").unwrap();
1359
1360        assert_eq!(mode.apply_to(dir), 0o040774);
1361        assert_eq!(mode.apply_to(file), 0o100764);
1362    }
1363
1364    #[test]
1365    fn test_set_str_umask_octal() {
1366        let mut mode = Mode::new(0o000,0o000);
1367        mode.set_str_umask("=777", 0o002).unwrap();
1368        assert_eq!(mode, Mode::new(0o777, 0o7777));
1369
1370        let mut mode = Mode::new(0o000,0o000);
1371        mode.set_str_umask("+777,-111", 0o002).unwrap();
1372        assert_eq!(mode, Mode::new(0o666, 0o7777));
1373
1374        let mut mode = Mode::new(0o000,0o000);
1375        mode.set_str_umask("=0,+7,-1", 0o002).unwrap();
1376        assert_eq!(mode, Mode::new(0o006, 0o7777));
1377
1378        let mut mode = Mode::new(0o000,0o000);
1379        mode.set_str_umask("a=t,ug=s,+7,-1", 0o002).unwrap();
1380        assert_eq!(mode, Mode::new(0o7006, 0o7777));
1381    }
1382
1383    #[test]
1384    fn test_from_str() {
1385        let lock = UMASK_LOCK;
1386        let guard = lock.lock();
1387        let umask = umask();
1388
1389        set_umask(0o002);
1390        let mut mode = Mode::empty();
1391        mode.set_str("=rwx").unwrap();
1392        assert_eq!(mode, Mode::new(0o0775, 0o0777)); // according to chmod behaviour; with umask 0o002
1393
1394        set_umask(0o022);
1395        let mut mode = Mode::empty();
1396        mode.set_str("=rwx").unwrap();
1397        assert_eq!(mode, Mode::new(0o0755, 0o0777));
1398
1399        set_umask(0o002);
1400        let mut mode = Mode::empty();
1401        mode.set_str("+w").unwrap();
1402        assert_eq!(mode, Mode::new(0o0220, 0o0222)); // according to chmod behaviour; with umask 0o002
1403
1404        set_umask(umask);
1405        drop(guard);
1406    }
1407
1408    #[test]
1409    fn test_file_type() {
1410        // originally taken with from_path assuming umask 0o002
1411        assert_eq!(Mode::new(0o040775, 0o177777).file_type(), Some(FileType::Directory)); // from_path("src")
1412        assert_eq!(Mode::new(0o020666, 0o177777).file_type(), Some(FileType::CharacterDevice)); // from_path("/dev/null")
1413        assert_eq!(Mode::new(0o060660, 0o177777).file_type(), Some(FileType::BlockDevice)); // from_path("/dev/loop0")
1414        assert_eq!(Mode::new(0o100644, 0o177777).file_type(), Some(FileType::RegularFile)); // from_path(".gitignore")
1415        assert_eq!(Mode::new(0o010664, 0o177777).file_type(), Some(FileType::FIFO));
1416        assert_eq!(Mode::new(0o120777, 0o177777).file_type(), Some(FileType::SymbolicLink)); // from_path("/etc/localtime")
1417        assert_eq!(Mode::new(0o140777, 0o177777).file_type(), Some(FileType::Socket)); // from_path("/dev/log")
1418    }
1419
1420    #[test]
1421    fn test_apply_to() {
1422        let dir = 0o040664; // no exec/search
1423        let file = 0o100664; // no exec/search
1424
1425        let mut search = Mode::empty();
1426        search.set_protection(User::Owner, &ProtectionBit::Execute.into());
1427        search.set_protection(User::Group, &ProtectionBit::Search.into());
1428
1429        assert_eq!(search.apply_to(dir), 0o040774);
1430        assert_eq!(search.apply_to(file), 0o100764);
1431    }
1432
1433    #[test]
1434    fn test_to_string() {
1435        // originally taken with from_path assuming umask 0o002
1436        assert_eq!(&Mode::new(0o040775, 0o177777).to_string(), "drwxrwxr-x"); // from_path("src")
1437        assert_eq!(&Mode::new(0o100644, 0o177777).to_string(), "-rw-r--r--"); // from_path(".gitignore")
1438        assert_eq!(&Mode::new(0o104755, 0o177777).to_string(), "-rwsr-xr-x"); // from_path("/sbin/sudo")
1439        assert_eq!(&Mode::new(0o041777, 0o177777).to_string(), "drwxrwxrwt"); // from_path("/tmp")
1440
1441        assert_eq!(&Mode::new(0o7000, 0o7777).to_string(), "?--S--S--T");
1442
1443        let mut mode = Mode::new(0o7000, 0o7777);
1444        mode.set_file_type(FileType::Directory);
1445        assert_eq!(&mode.to_string(), "d--S--S--T");
1446
1447        let mut mode = Mode::empty();
1448        mode.set_str("u+x,g+X").unwrap();
1449        assert_eq!(&mode.to_string(), "?--x--X---");
1450    }
1451
1452    #[test]
1453    fn test_mode_debug() {
1454        let mode = Mode::new(0o040775, 0o177777); // from_path("src")
1455        eprintln!("{}", mode);
1456        eprintln!("{:?}", mode);
1457        eprintln!("{:#?}", mode);
1458    }
1459
1460    #[test]
1461    #[cfg(target_family = "unix")]
1462    fn test_set_mode_traits() {
1463        //TODO: use temp file
1464        let file = Path::new("LICENSE");
1465        file.set_mode(0o600).unwrap();
1466        assert_eq!(file.set_mode("+r").unwrap() & 0o777, 0o644);
1467
1468        let file = File::open(file).unwrap();
1469        file.set_mode(0o600).unwrap();
1470        assert_eq!(file.set_mode("+r").unwrap() & 0o777, 0o644);
1471        assert_eq!(file.set_mode("g-u").unwrap() & 0o777, 0o604);
1472        assert_eq!(file.set_mode("g+u").unwrap() & 0o777, 0o664);
1473    }
1474}