Jelajahi Sumber

fix: dereferencing links permissions.

Signed-off-by: Christina Sørensen <christina@cafkafk.com>
Andrzej Grzeslak 3 tahun lalu
induk
melakukan
ee08692370
5 mengubah file dengan 136 tambahan dan 72 penghapusan
  1. 13 3
      src/fs/file.rs
  2. 2 1
      src/output/render/mod.rs
  3. 27 16
      src/output/render/octal.rs
  4. 71 43
      src/output/render/permissions.rs
  5. 23 9
      src/output/table.rs

+ 13 - 3
src/fs/file.rs

@@ -453,11 +453,21 @@ impl<'dir> File<'dir> {
     }
 
     /// This file’s permissions, with flags for each bit.
-    pub fn permissions(&self) -> f::Permissions {
+    pub fn permissions(&self) -> Option<f::Permissions> {
+        if self.is_link() && self.deref_links {
+            // If the chain of links is broken, we instead fall through and
+            // return the permissions of the original link, as would have been
+            // done if we were not dereferencing.
+            match self.link_target_recurse() {
+                FileTarget::Ok(f)   => return f.permissions(),
+                _                   => return None,
+            }
+        }
+
         let bits = self.metadata.mode();
         let has_bit = |bit| bits & bit == bit;
 
-        f::Permissions {
+        Some(f::Permissions {
             user_read:      has_bit(modes::USER_READ),
             user_write:     has_bit(modes::USER_WRITE),
             user_execute:   has_bit(modes::USER_EXECUTE),
@@ -473,7 +483,7 @@ impl<'dir> File<'dir> {
             sticky:         has_bit(modes::STICKY),
             setgid:         has_bit(modes::SETGID),
             setuid:         has_bit(modes::SETUID),
-        }
+        })
     }
 
     /// Whether this file’s extension is any of the strings that get passed in.

+ 2 - 1
src/output/render/mod.rs

@@ -17,7 +17,7 @@ mod links;
 pub use self::links::Colours as LinksColours;
 
 mod permissions;
-pub use self::permissions::Colours as PermissionsColours;
+pub use self::permissions::{Colours as PermissionsColours, PermissionsPlusRender};
 
 mod size;
 pub use self::size::Colours as SizeColours;
@@ -31,4 +31,5 @@ pub use self::users::Colours as UserColours;
 pub use self::users::Render as UserRender;
 
 mod octal;
+pub use self::octal::Render as OctalPermissionsRender;
 // octal uses just one colour

+ 27 - 16
src/output/render/octal.rs

@@ -3,26 +3,37 @@ use ansi_term::Style;
 use crate::fs::fields as f;
 use crate::output::cell::TextCell;
 
+pub trait Render {
+    fn render(&self, style: Style) -> TextCell;
+}
+
+impl Render for Option<f::OctalPermissions> {
+    fn render(&self, style: Style) -> TextCell {
+        match self {
+            Some(p) => {
+                let perm = &p.permissions;
+                let octal_sticky = f::OctalPermissions::bits_to_octal(perm.setuid, perm.setgid, perm.sticky);
+                let octal_owner  = f::OctalPermissions::bits_to_octal(perm.user_read, perm.user_write, perm.user_execute);
+                let octal_group  = f::OctalPermissions::bits_to_octal(perm.group_read, perm.group_write, perm.group_execute);
+                let octal_other  = f::OctalPermissions::bits_to_octal(perm.other_read, perm.other_write, perm.other_execute);
+
+                TextCell::paint(style, format!("{}{}{}{}", octal_sticky, octal_owner, octal_group, octal_other))
+            },
+            None => TextCell::paint(style, "----".into())
+        }
+    }
+}
 
 impl f::OctalPermissions {
     fn bits_to_octal(r: bool, w: bool, x: bool) -> u8 {
         (r as u8) * 4 + (w as u8) * 2 + (x as u8)
     }
-
-    pub fn render(&self, style: Style) -> TextCell {
-        let perm = &self.permissions;
-        let octal_sticky = Self::bits_to_octal(perm.setuid, perm.setgid, perm.sticky);
-        let octal_owner  = Self::bits_to_octal(perm.user_read, perm.user_write, perm.user_execute);
-        let octal_group  = Self::bits_to_octal(perm.group_read, perm.group_write, perm.group_execute);
-        let octal_other  = Self::bits_to_octal(perm.other_read, perm.other_write, perm.other_execute);
-
-        TextCell::paint(style, format!("{}{}{}{}", octal_sticky, octal_owner, octal_group, octal_other))
-    }
 }
 
 
 #[cfg(test)]
 pub mod test {
+    use super::Render;
     use crate::output::cell::TextCell;
     use crate::fs::fields as f;
 
@@ -37,7 +48,7 @@ pub mod test {
             other_read: true, other_write: false, other_execute: true, sticky: false,
         };
 
-        let octal = f::OctalPermissions{ permissions: bits };
+        let octal = Some(f::OctalPermissions{ permissions: bits });
 
         let expected = TextCell::paint_str(Purple.bold(), "0755");
         assert_eq!(expected, octal.render(Purple.bold()).into());
@@ -51,7 +62,7 @@ pub mod test {
             other_read: true, other_write: false, other_execute: false, sticky: false,
         };
 
-        let octal = f::OctalPermissions{ permissions: bits };
+        let octal = Some(f::OctalPermissions{ permissions: bits });
 
         let expected = TextCell::paint_str(Purple.bold(), "0644");
         assert_eq!(expected, octal.render(Purple.bold()).into());
@@ -65,7 +76,7 @@ pub mod test {
             other_read: false, other_write: false, other_execute: false, sticky: false,
         };
 
-        let octal = f::OctalPermissions{ permissions: bits };
+        let octal = Some(f::OctalPermissions{ permissions: bits });
 
         let expected = TextCell::paint_str(Purple.bold(), "0600");
         assert_eq!(expected, octal.render(Purple.bold()).into());
@@ -79,7 +90,7 @@ pub mod test {
             other_read: true, other_write: true,  other_execute: true, sticky: false,
         };
 
-        let octal = f::OctalPermissions{ permissions: bits };
+        let octal = Some(f::OctalPermissions{ permissions: bits });
 
         let expected = TextCell::paint_str(Purple.bold(), "4777");
         assert_eq!(expected, octal.render(Purple.bold()).into());
@@ -94,7 +105,7 @@ pub mod test {
             other_read: true, other_write: true,  other_execute: true, sticky: false,
         };
 
-        let octal = f::OctalPermissions{ permissions: bits };
+        let octal = Some(f::OctalPermissions{ permissions: bits });
 
         let expected = TextCell::paint_str(Purple.bold(), "2777");
         assert_eq!(expected, octal.render(Purple.bold()).into());
@@ -108,7 +119,7 @@ pub mod test {
             other_read: true, other_write: true,  other_execute: true, sticky: true,
         };
 
-        let octal = f::OctalPermissions{ permissions: bits };
+        let octal = Some(f::OctalPermissions{ permissions: bits });
 
         let expected = TextCell::paint_str(Purple.bold(), "1777");
         assert_eq!(expected, octal.render(Purple.bold()).into());

+ 71 - 43
src/output/render/permissions.rs

@@ -1,51 +1,79 @@
+use std::iter;
+
 use ansi_term::{ANSIString, Style};
 
 use crate::fs::fields as f;
 use crate::output::cell::{TextCell, DisplayWidth};
 use crate::output::render::FiletypeColours;
 
+pub trait PermissionsPlusRender {
+    fn render<C: Colours+FiletypeColours>(&self, colours: &C) -> TextCell;
+}
 
-impl f::PermissionsPlus {
-    pub fn render<C: Colours+FiletypeColours>(&self, colours: &C) -> TextCell {
-        let mut chars = vec![ self.file_type.render(colours) ];
-        chars.extend(self.permissions.render(colours, self.file_type.is_regular_file()));
-
-        if self.xattrs {
-           chars.push(colours.attribute().paint("@"));
-        }
-
-        // As these are all ASCII characters, we can guarantee that they’re
-        // all going to be one character wide, and don’t need to compute the
-        // cell’s display width.
-        TextCell {
-            width:    DisplayWidth::from(chars.len()),
-            contents: chars.into(),
+impl PermissionsPlusRender for Option<f::PermissionsPlus> {
+    fn render<C: Colours+FiletypeColours>(&self, colours: &C) -> TextCell {
+        match self {
+            Some(p) => {
+                let mut chars = vec![ p.file_type.render(colours) ];
+                let permissions = p.permissions;
+                chars.extend(Some(permissions).render(colours, p.file_type.is_regular_file()));
+
+                if p.xattrs {
+                   chars.push(colours.attribute().paint("@"));
+                }
+
+                // As these are all ASCII characters, we can guarantee that they’re
+                // all going to be one character wide, and don’t need to compute the
+                // cell’s display width.
+                TextCell {
+                    width:    DisplayWidth::from(chars.len()),
+                    contents: chars.into(),
+                }
+            },
+            None => {
+                let chars: Vec<_> = iter::repeat(colours.dash().paint("-")).take(10).collect();
+                TextCell {
+                    width:    DisplayWidth::from(chars.len()),
+                    contents: chars.into(),
+                }
+            }
         }
     }
 }
 
+pub trait RenderPermissions {
+    fn render<C: Colours>(&self, colours: &C, is_regular_file: bool) -> Vec<ANSIString<'static>>;
+}
 
-impl f::Permissions {
-    pub fn render<C: Colours>(&self, colours: &C, is_regular_file: bool) -> Vec<ANSIString<'static>> {
-
-        let bit = |bit, chr: &'static str, style: Style| {
-            if bit { style.paint(chr) }
-              else { colours.dash().paint("-") }
-        };
-
-        vec![
-            bit(self.user_read,   "r", colours.user_read()),
-            bit(self.user_write,  "w", colours.user_write()),
-            self.user_execute_bit(colours, is_regular_file),
-            bit(self.group_read,  "r", colours.group_read()),
-            bit(self.group_write, "w", colours.group_write()),
-            self.group_execute_bit(colours),
-            bit(self.other_read,  "r", colours.other_read()),
-            bit(self.other_write, "w", colours.other_write()),
-            self.other_execute_bit(colours)
-        ]
+impl RenderPermissions for Option<f::Permissions> {
+    fn render<C: Colours>(&self, colours: &C, is_regular_file: bool) -> Vec<ANSIString<'static>> {
+        match self {
+            Some(p) => {
+                let bit = |bit, chr: &'static str, style: Style| {
+                    if bit { style.paint(chr) }
+                      else { colours.dash().paint("-") }
+                };
+
+                vec![
+                    bit(p.user_read,   "r", colours.user_read()),
+                    bit(p.user_write,  "w", colours.user_write()),
+                    p.user_execute_bit(colours, is_regular_file),
+                    bit(p.group_read,  "r", colours.group_read()),
+                    bit(p.group_write, "w", colours.group_write()),
+                    p.group_execute_bit(colours),
+                    bit(p.other_read,  "r", colours.other_read()),
+                    bit(p.other_write, "w", colours.other_write()),
+                    p.other_execute_bit(colours)
+                ]
+            },
+            None => {
+                iter::repeat(colours.dash().paint("-")).take(9).collect()
+            }
+        }
     }
+}
 
+impl f::Permissions {
     fn user_execute_bit<C: Colours>(&self, colours: &C, is_regular_file: bool) -> ANSIString<'static> {
         match (self.user_execute, self.setuid, is_regular_file) {
             (false, false, _)      => colours.dash().paint("-"),
@@ -103,7 +131,7 @@ pub trait Colours {
 #[cfg(test)]
 #[allow(unused_results)]
 pub mod test {
-    use super::Colours;
+    use super::{Colours, RenderPermissions};
     use crate::output::cell::TextCellContents;
     use crate::fs::fields as f;
 
@@ -133,11 +161,11 @@ pub mod test {
 
     #[test]
     fn negate() {
-        let bits = f::Permissions {
+        let bits = Some(f::Permissions {
             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(11).paint("-"),  Fixed(11).paint("-"),  Fixed(11).paint("-"),
@@ -151,11 +179,11 @@ pub mod test {
 
     #[test]
     fn affirm() {
-        let bits = f::Permissions {
+        let bits = Some(f::Permissions {
             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![
             Fixed(101).paint("r"),  Fixed(102).paint("w"),  Fixed(103).paint("x"),
@@ -169,11 +197,11 @@ pub mod test {
 
     #[test]
     fn specials() {
-        let bits = f::Permissions {
+        let bits = Some(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(110).paint("s"),
@@ -187,11 +215,11 @@ pub mod test {
 
     #[test]
     fn extra_specials() {
-        let bits = f::Permissions {
+        let bits = Some(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(111).paint("S"),

+ 23 - 9
src/output/table.rs

@@ -13,11 +13,19 @@ use users::UsersCache;
 use crate::fs::{File, fields as f};
 use crate::fs::feature::git::GitCache;
 use crate::output::cell::TextCell;
-use crate::output::render::{GroupRender, TimeRender, UserRender};
+use crate::output::render::{
+    GroupRender,
+    OctalPermissionsRender,
+    PermissionsPlusRender,
+    TimeRender,
+    UserRender
+};
 use crate::output::time::TimeFormat;
 use crate::theme::Theme;
 
 
+
+
 /// Options for displaying a table.
 #[derive(PartialEq, Debug)]
 pub struct Options {
@@ -385,17 +393,23 @@ impl<'a, 'f> Table<'a> {
         self.widths.add_widths(row)
     }
 
-    fn permissions_plus(&self, file: &File<'_>, xattrs: bool) -> f::PermissionsPlus {
-        f::PermissionsPlus {
-            file_type: file.type_char(),
-            permissions: file.permissions(),
-            xattrs,
+    fn permissions_plus(&self, file: &File<'_>, xattrs: bool) -> Option<f::PermissionsPlus> {
+        match file.permissions() {
+            Some(p) => Some(f::PermissionsPlus {
+                file_type: file.type_char(),
+                permissions: p,
+                xattrs
+            }),
+            None => None,
         }
     }
 
-    fn octal_permissions(&self, file: &File<'_>) -> f::OctalPermissions {
-        f::OctalPermissions {
-            permissions: file.permissions(),
+    fn octal_permissions(&self, file: &File<'_>) -> Option<f::OctalPermissions> {
+        match file.permissions() {
+            Some(p) => Some(f::OctalPermissions {
+                permissions: p,
+            }),
+            None => None,
         }
     }