Преглед изворни кода

Merge branch 'sticky-bits'

This adds support for the setuid, setgid, and sticky bits like how ls does it: by replacing the user/group/execute bits with different flags depending on their presence. At least we do it with flair, and by flair, I mean purple.

Fixes #142
Benjamin Sago пре 8 година
родитељ
комит
110613bf95
7 измењених фајлова са 155 додато и 39 уклоњено
  1. 12 13
      Vagrantfile
  2. 4 0
      src/fs/fields.rs
  3. 13 1
      src/fs/file.rs
  4. 10 1
      src/output/colours.rs
  5. 92 20
      src/output/render/permissions.rs
  6. 12 2
      xtests/permissions
  7. 12 2
      xtests/permissions_sudo

+ 12 - 13
Vagrantfile

@@ -274,26 +274,25 @@ Vagrant.configure(2) do |config|
 
 
     # Awkward permission testcases.
+    # Differences in the way ‘chmod’ handles setting ‘setuid’ and ‘setgid’
+    # when you don’t already own the file mean that we need to use ‘sudo’
+    # to change permissions to those.
     config.vm.provision :shell, privileged: false, inline: <<-EOF
         set -xe
         mkdir "#{test_dir}/permissions"
 
-        touch     "#{test_dir}/permissions/all-permissions"
-        chmod 777 "#{test_dir}/permissions/all-permissions"
+        mkdir                      "#{test_dir}/permissions/forbidden-directory"
+        chmod 000                  "#{test_dir}/permissions/forbidden-directory"
+        touch -t #{some_date}      "#{test_dir}/permissions/forbidden-directory"
+        sudo chown #{user}:#{user} "#{test_dir}/permissions/forbidden-directory"
 
-        touch     "#{test_dir}/permissions/no-permissions"
-        chmod 000 "#{test_dir}/permissions/no-permissions"
-
-        mkdir     "#{test_dir}/permissions/forbidden-directory"
-        chmod 000 "#{test_dir}/permissions/forbidden-directory"
-
-        for perms in 001 002 004 010 020 040 100 200 400; do
-            touch        "#{test_dir}/permissions/$perms"
-            chmod $perms "#{test_dir}/permissions/$perms"
+        for perms in 000 001 002 004 010 020 040 100 200 400 644 755 777 1000 1001 2000 2010 4000 4100 7666 7777; do
+            touch                      "#{test_dir}/permissions/$perms"
+            sudo chown #{user}:#{user} "#{test_dir}/permissions/$perms"
+            sudo chmod $perms          "#{test_dir}/permissions/$perms"
+            sudo touch -t #{some_date} "#{test_dir}/permissions/$perms"
         done
 
-        touch -t #{some_date}      "#{test_dir}/permissions/"*
-        sudo chown #{user}:#{user} "#{test_dir}/permissions/"*
     EOF
 
 

+ 4 - 0
src/fs/fields.rs

@@ -67,6 +67,10 @@ pub struct Permissions {
     pub other_read:     bool,
     pub other_write:    bool,
     pub other_execute:  bool,
+
+    pub sticky:         bool,
+    pub setgid:         bool,
+    pub setuid:         bool,
 }
 
 /// The three pieces of information that are displayed as a single column in

+ 13 - 1
src/fs/file.rs

@@ -309,19 +309,25 @@ impl<'dir> File<'dir> {
 
     /// This file’s permissions, with flags for each bit.
     pub fn permissions(&self) -> f::Permissions {
-        let bits = self.metadata.permissions().mode();
+        let bits = self.metadata.mode();
         let has_bit = |bit| { bits & bit == bit };
 
         f::Permissions {
             user_read:      has_bit(modes::USER_READ),
             user_write:     has_bit(modes::USER_WRITE),
             user_execute:   has_bit(modes::USER_EXECUTE),
+
             group_read:     has_bit(modes::GROUP_READ),
             group_write:    has_bit(modes::GROUP_WRITE),
             group_execute:  has_bit(modes::GROUP_EXECUTE),
+
             other_read:     has_bit(modes::OTHER_READ),
             other_write:    has_bit(modes::OTHER_WRITE),
             other_execute:  has_bit(modes::OTHER_EXECUTE),
+
+            sticky:         has_bit(modes::STICKY),
+            setgid:         has_bit(modes::SETGID),
+            setuid:         has_bit(modes::SETUID),
         }
     }
 
@@ -437,12 +443,18 @@ mod modes {
     pub const USER_READ: Mode     = libc::S_IRUSR as Mode;
     pub const USER_WRITE: Mode    = libc::S_IWUSR as Mode;
     pub const USER_EXECUTE: Mode  = libc::S_IXUSR as Mode;
+
     pub const GROUP_READ: Mode    = libc::S_IRGRP as Mode;
     pub const GROUP_WRITE: Mode   = libc::S_IWGRP as Mode;
     pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
+
     pub const OTHER_READ: Mode    = libc::S_IROTH as Mode;
     pub const OTHER_WRITE: Mode   = libc::S_IWOTH as Mode;
     pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
+
+    pub const STICKY: Mode        = libc::S_ISVTX as Mode;
+    pub const SETGID: Mode        = libc::S_ISGID as Mode;
+    pub const SETUID: Mode        = libc::S_ISUID as Mode;
 }
 
 

+ 10 - 1
src/output/colours.rs

@@ -62,7 +62,10 @@ pub struct Permissions {
     pub other_write:   Style,
     pub other_execute: Style,
 
-    pub attribute:  Style,
+    pub special_user_file: Style,
+    pub special_other:     Style,
+
+    pub attribute: Style,
 }
 
 #[derive(Clone, Copy, Debug, Default, PartialEq)]
@@ -138,12 +141,18 @@ impl Colours {
                 user_write:          Red.bold(),
                 user_execute_file:   Green.bold().underline(),
                 user_execute_other:  Green.bold(),
+
                 group_read:          Yellow.normal(),
                 group_write:         Red.normal(),
                 group_execute:       Green.normal(),
+
                 other_read:          Yellow.normal(),
                 other_write:         Red.normal(),
                 other_execute:       Green.normal(),
+
+                special_user_file:   Purple.normal(),
+                special_other:       Purple.normal(),
+
                 attribute:           Style::default(),
             },
 

+ 92 - 20
src/output/render/permissions.rs

@@ -6,11 +6,8 @@ use ansi_term::{ANSIString, Style};
 
 impl f::PermissionsPlus {
     pub fn render(&self, colours: &Colours) -> TextCell {
-        let x_colour = if self.file_type.is_regular_file() { colours.perms.user_execute_file }
-                                                      else { colours.perms.user_execute_other };
-
         let mut chars = vec![ self.file_type.render(colours) ];
-        chars.extend(self.permissions.render(colours, x_colour));
+        chars.extend(self.permissions.render(colours, self.file_type.is_regular_file()));
 
         if self.xattrs {
            chars.push(colours.perms.attribute.paint("@"));
@@ -27,7 +24,7 @@ impl f::PermissionsPlus {
 }
 
 impl f::Permissions {
-    pub fn render(&self, colours: &Colours, x_colour: Style) -> Vec<ANSIString<'static>> {
+    pub fn render(&self, colours: &Colours, is_regular_file: bool) -> Vec<ANSIString<'static>> {
         let bit = |bit, chr: &'static str, style: Style| {
             if bit { style.paint(chr) } else { colours.punctuation.paint("-") }
         };
@@ -35,15 +32,44 @@ impl f::Permissions {
         vec![
             bit(self.user_read,     "r", colours.perms.user_read),
             bit(self.user_write,    "w", colours.perms.user_write),
-            bit(self.user_execute,  "x", x_colour),
+            self.user_execute_bit(colours, is_regular_file),
             bit(self.group_read,    "r", colours.perms.group_read),
             bit(self.group_write,   "w", colours.perms.group_write),
-            bit(self.group_execute, "x", colours.perms.group_execute),
+            self.group_execute_bit(colours),
             bit(self.other_read,    "r", colours.perms.other_read),
             bit(self.other_write,   "w", colours.perms.other_write),
-            bit(self.other_execute, "x", colours.perms.other_execute),
+            self.other_execute_bit(colours)
         ]
     }
+
+    fn user_execute_bit(&self, colours: &Colours, is_regular_file: bool) -> ANSIString<'static> {
+        match (self.user_execute, self.setuid, is_regular_file) {
+            (false, false, _)      => colours.punctuation.paint("-"),
+            (true,  false, false)  => colours.perms.user_execute_other.paint("x"),
+            (true,  false, true)   => colours.perms.user_execute_file.paint("x"),
+            (false, true, _)       => colours.perms.special_other.paint("S"),
+            (true,  true, false)   => colours.perms.special_other.paint("s"),
+            (true,  true, true)    => colours.perms.special_user_file.paint("s"),
+        }
+    }
+
+    fn group_execute_bit(&self, colours: &Colours) -> ANSIString<'static> {
+        match (self.group_execute, self.setgid) {
+            (false, false)  => colours.punctuation.paint("-"),
+            (true,  false)  => colours.perms.group_execute.paint("x"),
+            (false, true)   => colours.perms.special_other.paint("S"),
+            (true,  true)   => colours.perms.special_other.paint("s"),
+        }
+    }
+
+    fn other_execute_bit(&self, colours: &Colours) -> ANSIString<'static> {
+        match (self.other_execute, self.sticky) {
+            (false, false)  => colours.punctuation.paint("-"),
+            (true,  false)  => colours.perms.other_execute.paint("x"),
+            (false, true)   => colours.perms.special_other.paint("T"),
+            (true,  true)   => colours.perms.special_other.paint("t"),
+        }
+    }
 }
 
 impl f::Type {
@@ -76,21 +102,21 @@ pub mod test {
     #[test]
     fn negate() {
         let mut details = Details::default();
-        details.colours.punctuation = Fixed(44).normal();
+        details.colours.punctuation = Fixed(11).normal();
 
         let bits = f::Permissions {
-            user_read:  false,  user_write:  false,  user_execute:  false,
-            group_read: false,  group_write: false,  group_execute: false,
-            other_read: false,  other_write: false,  other_execute: false,
+            user_read:  false,  user_write:  false,  user_execute:  false,  setuid: false,
+            group_read: false,  group_write: false,  group_execute: false,  setgid: false,
+            other_read: false,  other_write: false,  other_execute: false,  sticky: false,
         };
 
         let expected = TextCellContents::from(vec![
-            Fixed(44).paint("-"),  Fixed(44).paint("-"),  Fixed(44).paint("-"),
-            Fixed(44).paint("-"),  Fixed(44).paint("-"),  Fixed(44).paint("-"),
-            Fixed(44).paint("-"),  Fixed(44).paint("-"),  Fixed(44).paint("-"),
+            Fixed(11).paint("-"),  Fixed(11).paint("-"),  Fixed(11).paint("-"),
+            Fixed(11).paint("-"),  Fixed(11).paint("-"),  Fixed(11).paint("-"),
+            Fixed(11).paint("-"),  Fixed(11).paint("-"),  Fixed(11).paint("-"),
         ]);
 
-        assert_eq!(expected, bits.render(&details.colours, Fixed(66).normal()).into())
+        assert_eq!(expected, bits.render(&details.colours, false).into())
     }
 
 
@@ -99,6 +125,7 @@ pub mod test {
         let mut details = Details::default();
         details.colours.perms.user_read    = Fixed(101).normal();
         details.colours.perms.user_write   = Fixed(102).normal();
+        details.colours.perms.user_execute_file = Fixed(103).normal();
 
         details.colours.perms.group_read    = Fixed(104).normal();
         details.colours.perms.group_write   = Fixed(105).normal();
@@ -109,9 +136,9 @@ pub mod test {
         details.colours.perms.other_execute = Fixed(109).normal();
 
         let bits = f::Permissions {
-            user_read:  true,  user_write:  true,  user_execute:  true,
-            group_read: true,  group_write: true,  group_execute: true,
-            other_read: true,  other_write: true,  other_execute: true,
+            user_read:  true,  user_write:  true,  user_execute:  true,  setuid: false,
+            group_read: true,  group_write: true,  group_execute: true,  setgid: false,
+            other_read: true,  other_write: true,  other_execute: true,  sticky: false,
         };
 
         let expected = TextCellContents::from(vec![
@@ -120,6 +147,51 @@ pub mod test {
             Fixed(107).paint("r"),  Fixed(108).paint("w"),  Fixed(109).paint("x"),
         ]);
 
-        assert_eq!(expected, bits.render(&details.colours, Fixed(103).normal()).into())
+        assert_eq!(expected, bits.render(&details.colours, true).into())
+    }
+
+
+    #[test]
+    fn specials() {
+        let mut details = Details::default();
+        details.colours.punctuation = Fixed(11).normal();
+        details.colours.perms.special_user_file = Fixed(77).normal();
+        details.colours.perms.special_other = Fixed(88).normal();
+
+        let bits = f::Permissions {
+            user_read:  false,  user_write:  false,  user_execute:  true,  setuid: true,
+            group_read: false,  group_write: false,  group_execute: true,  setgid: true,
+            other_read: false,  other_write: false,  other_execute: true,  sticky: true,
+        };
+
+        let expected = TextCellContents::from(vec![
+            Fixed(11).paint("-"),  Fixed(11).paint("-"),  Fixed(77).paint("s"),
+            Fixed(11).paint("-"),  Fixed(11).paint("-"),  Fixed(88).paint("s"),
+            Fixed(11).paint("-"),  Fixed(11).paint("-"),  Fixed(88).paint("t"),
+        ]);
+
+        assert_eq!(expected, bits.render(&details.colours, true).into())
+    }
+
+
+    #[test]
+    fn extra_specials() {
+        let mut details = Details::default();
+        details.colours.punctuation = Fixed(11).normal();
+        details.colours.perms.special_other = Fixed(88).normal();
+
+        let bits = f::Permissions {
+            user_read:  false,  user_write:  false,  user_execute:  false,  setuid: true,
+            group_read: false,  group_write: false,  group_execute: false,  setgid: true,
+            other_read: false,  other_write: false,  other_execute: false,  sticky: true,
+        };
+
+        let expected = TextCellContents::from(vec![
+            Fixed(11).paint("-"),  Fixed(11).paint("-"),  Fixed(88).paint("S"),
+            Fixed(11).paint("-"),  Fixed(11).paint("-"),  Fixed(88).paint("S"),
+            Fixed(11).paint("-"),  Fixed(11).paint("-"),  Fixed(88).paint("T"),
+        ]);
+
+        assert_eq!(expected, bits.render(&details.colours, true).into())
     }
 }

+ 12 - 2
xtests/permissions

@@ -1,5 +1,6 @@
 /testcases/permissions/forbidden-directory: Permission denied (os error 13)
 Permissions Size User      Group     Date Modified Name
+.---------     0 cassowary cassowary  1 Jan 12:34  000
 .--------x     0 cassowary cassowary  1 Jan 12:34  001
 .-------w-     0 cassowary cassowary  1 Jan 12:34  002
 .------r--     0 cassowary cassowary  1 Jan 12:34  004
@@ -9,6 +10,15 @@
 .--x------     0 cassowary cassowary  1 Jan 12:34  100
 .-w-------     0 cassowary cassowary  1 Jan 12:34  200
 .r--------     0 cassowary cassowary  1 Jan 12:34  400
-.rwxrwxrwx     0 cassowary cassowary  1 Jan 12:34  all-permissions
+.rw-r--r--     0 cassowary cassowary  1 Jan 12:34  644
+.rwxr-xr-x     0 cassowary cassowary  1 Jan 12:34  755
+.rwxrwxrwx     0 cassowary cassowary  1 Jan 12:34  777
+.--------T     0 cassowary cassowary  1 Jan 12:34  1000
+.--------t     0 cassowary cassowary  1 Jan 12:34  1001
+.-----S---     0 cassowary cassowary  1 Jan 12:34  2000
+.-----s---     0 cassowary cassowary  1 Jan 12:34  2010
+.--S------     0 cassowary cassowary  1 Jan 12:34  4000
+.--s------     0 cassowary cassowary  1 Jan 12:34  4100
+.rwSrwSrwT     0 cassowary cassowary  1 Jan 12:34  7666
+.rwsrwsrwt     0 cassowary cassowary  1 Jan 12:34  7777
 d---------     - cassowary cassowary  1 Jan 12:34  forbidden-directory
-.---------     0 cassowary cassowary  1 Jan 12:34  no-permissions

+ 12 - 2
xtests/permissions_sudo

@@ -1,5 +1,6 @@
 /testcases/permissions/forbidden-directory: Permission denied (os error 13)
 Permissions Size User      Group     Date Modified Name
+.---------     0 cassowary cassowary  1 Jan 12:34  000
 .--------x     0 cassowary cassowary  1 Jan 12:34  001
 .-------w-     0 cassowary cassowary  1 Jan 12:34  002
 .------r--     0 cassowary cassowary  1 Jan 12:34  004
@@ -9,6 +10,15 @@
 .--x------     0 cassowary cassowary  1 Jan 12:34  100
 .-w-------     0 cassowary cassowary  1 Jan 12:34  200
 .r--------     0 cassowary cassowary  1 Jan 12:34  400
-.rwxrwxrwx     0 cassowary cassowary  1 Jan 12:34  all-permissions
+.rw-r--r--     0 cassowary cassowary  1 Jan 12:34  644
+.rwxr-xr-x     0 cassowary cassowary  1 Jan 12:34  755
+.rwxrwxrwx     0 cassowary cassowary  1 Jan 12:34  777
+.--------T     0 cassowary cassowary  1 Jan 12:34  1000
+.--------t     0 cassowary cassowary  1 Jan 12:34  1001
+.-----S---     0 cassowary cassowary  1 Jan 12:34  2000
+.-----s---     0 cassowary cassowary  1 Jan 12:34  2010
+.--S------     0 cassowary cassowary  1 Jan 12:34  4000
+.--s------     0 cassowary cassowary  1 Jan 12:34  4100
+.rwSrwSrwT     0 cassowary cassowary  1 Jan 12:34  7666
+.rwsrwsrwt     0 cassowary cassowary  1 Jan 12:34  7777
 d---------     - cassowary cassowary  1 Jan 12:34  forbidden-directory
-.---------     0 cassowary cassowary  1 Jan 12:34  no-permissions