From f9f7ad22309c8a8dfbddf2a08234d1f61d1bccdb Mon Sep 17 00:00:00 2001 From: Benjamin Sago Date: Tue, 30 May 2017 15:29:29 +0100 Subject: [PATCH 1/3] Read setuid/setgid/sticky bits The problem here was that we were using `metadata.permissions().mode()`, which is capped at 0o777, rather than `metadata.mode()`, which exposes every bit. With this change, we can access the higher-order permission bits, and put them in the Permissions struct. --- src/fs/fields.rs | 4 ++++ src/fs/file.rs | 14 +++++++++++++- src/output/render/permissions.rs | 12 ++++++------ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/fs/fields.rs b/src/fs/fields.rs index ee25e91..65e5289 100644 --- a/src/fs/fields.rs +++ b/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 diff --git a/src/fs/file.rs b/src/fs/file.rs index 6430862..c0a0653 100644 --- a/src/fs/file.rs +++ b/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; } diff --git a/src/output/render/permissions.rs b/src/output/render/permissions.rs index 903b99d..aa6decb 100644 --- a/src/output/render/permissions.rs +++ b/src/output/render/permissions.rs @@ -79,9 +79,9 @@ pub mod test { details.colours.punctuation = Fixed(44).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![ @@ -109,9 +109,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![ From a2901c63cf5c9f77aec8625f72a1f7b4338569af Mon Sep 17 00:00:00 2001 From: Benjamin Sago Date: Tue, 30 May 2017 15:31:24 +0100 Subject: [PATCH 2/3] Render higher permission bits Unlike the others, setuid/setgid/sticky get merged with user/group/other execute in the rendered Permissions cell. So there had to be a bit of code change done to make sure that none of the bits clashed. --- src/output/colours.rs | 11 +++- src/output/render/permissions.rs | 100 ++++++++++++++++++++++++++----- 2 files changed, 96 insertions(+), 15 deletions(-) diff --git a/src/output/colours.rs b/src/output/colours.rs index 2797386..023a203 100644 --- a/src/output/colours.rs +++ b/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(), }, diff --git a/src/output/render/permissions.rs b/src/output/render/permissions.rs index aa6decb..a338202 100644 --- a/src/output/render/permissions.rs +++ b/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> { + pub fn render(&self, colours: &Colours, is_regular_file: bool) -> Vec> { 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,7 +102,7 @@ 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, setuid: false, @@ -85,12 +111,12 @@ pub mod test { }; 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(); @@ -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()) } } From c4447e35b6541e80899636ebab9daa5e95516966 Mon Sep 17 00:00:00 2001 From: Benjamin Sago Date: Tue, 30 May 2017 15:32:11 +0100 Subject: [PATCH 3/3] xtests for higher order bits Finally, re-do the permissions extended tests to include the setuid, setgid, and sticky bits, and rename the last two existing ones to match the others (files with the same names as their permissions). --- Vagrantfile | 25 ++++++++++++------------- xtests/permissions | 14 ++++++++++++-- xtests/permissions_sudo | 14 ++++++++++++-- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index 9347271..91d4295 100644 --- a/Vagrantfile +++ b/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 diff --git a/xtests/permissions b/xtests/permissions index ac62ea4..6ed9ad9 100644 --- a/xtests/permissions +++ b/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 diff --git a/xtests/permissions_sudo b/xtests/permissions_sudo index e7baf66..fb242d6 100644 --- a/xtests/permissions_sudo +++ b/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