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
This commit is contained in:
Benjamin Sago 2017-05-30 15:32:23 +01:00
commit 110613bf95
7 changed files with 155 additions and 39 deletions

25
Vagrantfile vendored
View File

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

View File

@ -67,6 +67,10 @@ pub struct Permissions {
pub other_read: bool, pub other_read: bool,
pub other_write: bool, pub other_write: bool,
pub other_execute: 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 /// The three pieces of information that are displayed as a single column in

View File

@ -309,19 +309,25 @@ impl<'dir> File<'dir> {
/// This files permissions, with flags for each bit. /// This files permissions, with flags for each bit.
pub fn permissions(&self) -> f::Permissions { pub fn permissions(&self) -> f::Permissions {
let bits = self.metadata.permissions().mode(); let bits = self.metadata.mode();
let has_bit = |bit| { bits & bit == bit }; let has_bit = |bit| { bits & bit == bit };
f::Permissions { f::Permissions {
user_read: has_bit(modes::USER_READ), user_read: has_bit(modes::USER_READ),
user_write: has_bit(modes::USER_WRITE), user_write: has_bit(modes::USER_WRITE),
user_execute: has_bit(modes::USER_EXECUTE), user_execute: has_bit(modes::USER_EXECUTE),
group_read: has_bit(modes::GROUP_READ), group_read: has_bit(modes::GROUP_READ),
group_write: has_bit(modes::GROUP_WRITE), group_write: has_bit(modes::GROUP_WRITE),
group_execute: has_bit(modes::GROUP_EXECUTE), group_execute: has_bit(modes::GROUP_EXECUTE),
other_read: has_bit(modes::OTHER_READ), other_read: has_bit(modes::OTHER_READ),
other_write: has_bit(modes::OTHER_WRITE), other_write: has_bit(modes::OTHER_WRITE),
other_execute: has_bit(modes::OTHER_EXECUTE), 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_READ: Mode = libc::S_IRUSR as Mode;
pub const USER_WRITE: Mode = libc::S_IWUSR as Mode; pub const USER_WRITE: Mode = libc::S_IWUSR as Mode;
pub const USER_EXECUTE: Mode = libc::S_IXUSR 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_READ: Mode = libc::S_IRGRP as Mode;
pub const GROUP_WRITE: Mode = libc::S_IWGRP as Mode; pub const GROUP_WRITE: Mode = libc::S_IWGRP as Mode;
pub const GROUP_EXECUTE: Mode = libc::S_IXGRP 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_READ: Mode = libc::S_IROTH as Mode;
pub const OTHER_WRITE: Mode = libc::S_IWOTH as Mode; pub const OTHER_WRITE: Mode = libc::S_IWOTH as Mode;
pub const OTHER_EXECUTE: Mode = libc::S_IXOTH 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;
} }

View File

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

View File

@ -6,11 +6,8 @@ use ansi_term::{ANSIString, Style};
impl f::PermissionsPlus { impl f::PermissionsPlus {
pub fn render(&self, colours: &Colours) -> TextCell { 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) ]; 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 { if self.xattrs {
chars.push(colours.perms.attribute.paint("@")); chars.push(colours.perms.attribute.paint("@"));
@ -27,7 +24,7 @@ impl f::PermissionsPlus {
} }
impl f::Permissions { 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| { let bit = |bit, chr: &'static str, style: Style| {
if bit { style.paint(chr) } else { colours.punctuation.paint("-") } if bit { style.paint(chr) } else { colours.punctuation.paint("-") }
}; };
@ -35,15 +32,44 @@ impl f::Permissions {
vec![ vec![
bit(self.user_read, "r", colours.perms.user_read), bit(self.user_read, "r", colours.perms.user_read),
bit(self.user_write, "w", colours.perms.user_write), 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_read, "r", colours.perms.group_read),
bit(self.group_write, "w", colours.perms.group_write), 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_read, "r", colours.perms.other_read),
bit(self.other_write, "w", colours.perms.other_write), 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 { impl f::Type {
@ -76,21 +102,21 @@ pub mod test {
#[test] #[test]
fn negate() { fn negate() {
let mut details = Details::default(); let mut details = Details::default();
details.colours.punctuation = Fixed(44).normal(); details.colours.punctuation = Fixed(11).normal();
let bits = f::Permissions { let bits = f::Permissions {
user_read: false, user_write: false, user_execute: false, user_read: false, user_write: false, user_execute: false, setuid: false,
group_read: false, group_write: false, group_execute: false, group_read: false, group_write: false, group_execute: false, setgid: false,
other_read: false, other_write: false, other_execute: false, other_read: false, other_write: false, other_execute: false, sticky: false,
}; };
let expected = TextCellContents::from(vec![ let expected = TextCellContents::from(vec![
Fixed(44).paint("-"), Fixed(44).paint("-"), Fixed(44).paint("-"), Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(11).paint("-"),
Fixed(44).paint("-"), Fixed(44).paint("-"), Fixed(44).paint("-"), Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(11).paint("-"),
Fixed(44).paint("-"), Fixed(44).paint("-"), Fixed(44).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(); let mut details = Details::default();
details.colours.perms.user_read = Fixed(101).normal(); details.colours.perms.user_read = Fixed(101).normal();
details.colours.perms.user_write = Fixed(102).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_read = Fixed(104).normal();
details.colours.perms.group_write = Fixed(105).normal(); details.colours.perms.group_write = Fixed(105).normal();
@ -109,9 +136,9 @@ pub mod test {
details.colours.perms.other_execute = Fixed(109).normal(); details.colours.perms.other_execute = Fixed(109).normal();
let bits = f::Permissions { let bits = f::Permissions {
user_read: true, user_write: true, user_execute: true, user_read: true, user_write: true, user_execute: true, setuid: false,
group_read: true, group_write: true, group_execute: true, group_read: true, group_write: true, group_execute: true, setgid: false,
other_read: true, other_write: true, other_execute: true, other_read: true, other_write: true, other_execute: true, sticky: false,
}; };
let expected = TextCellContents::from(vec![ let expected = TextCellContents::from(vec![
@ -120,6 +147,51 @@ pub mod test {
Fixed(107).paint("r"), Fixed(108).paint("w"), Fixed(109).paint("x"), 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())
} }
} }

View File

@ -1,5 +1,6 @@
/testcases/permissions/forbidden-directory: Permission denied (os error 13) /testcases/permissions/forbidden-directory: Permission denied (os error 13)
Permissions Size User Group Date Modified Name Permissions Size User Group Date Modified Name
.--------- 0 cassowary cassowary  1 Jan 12:34 000
.--------x 0 cassowary cassowary  1 Jan 12:34 001 .--------x 0 cassowary cassowary  1 Jan 12:34 001
.-------w- 0 cassowary cassowary  1 Jan 12:34 002 .-------w- 0 cassowary cassowary  1 Jan 12:34 002
.------r-- 0 cassowary cassowary  1 Jan 12:34 004 .------r-- 0 cassowary cassowary  1 Jan 12:34 004
@ -9,6 +10,15 @@
.--x------ 0 cassowary cassowary  1 Jan 12:34 100 .--x------ 0 cassowary cassowary  1 Jan 12:34 100
.-w------- 0 cassowary cassowary  1 Jan 12:34 200 .-w------- 0 cassowary cassowary  1 Jan 12:34 200
.r-------- 0 cassowary cassowary  1 Jan 12:34 400 .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 d--------- - cassowary cassowary  1 Jan 12:34 forbidden-directory
.--------- 0 cassowary cassowary  1 Jan 12:34 no-permissions

View File

@ -1,5 +1,6 @@
/testcases/permissions/forbidden-directory: Permission denied (os error 13) /testcases/permissions/forbidden-directory: Permission denied (os error 13)
Permissions Size User Group Date Modified Name Permissions Size User Group Date Modified Name
.--------- 0 cassowary cassowary  1 Jan 12:34 000
.--------x 0 cassowary cassowary  1 Jan 12:34 001 .--------x 0 cassowary cassowary  1 Jan 12:34 001
.-------w- 0 cassowary cassowary  1 Jan 12:34 002 .-------w- 0 cassowary cassowary  1 Jan 12:34 002
.------r-- 0 cassowary cassowary  1 Jan 12:34 004 .------r-- 0 cassowary cassowary  1 Jan 12:34 004
@ -9,6 +10,15 @@
.--x------ 0 cassowary cassowary  1 Jan 12:34 100 .--x------ 0 cassowary cassowary  1 Jan 12:34 100
.-w------- 0 cassowary cassowary  1 Jan 12:34 200 .-w------- 0 cassowary cassowary  1 Jan 12:34 200
.r-------- 0 cassowary cassowary  1 Jan 12:34 400 .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 d--------- - cassowary cassowary  1 Jan 12:34 forbidden-directory
.--------- 0 cassowary cassowary  1 Jan 12:34 no-permissions