mirror of
https://github.com/Llewellynvdm/exa.git
synced 2025-01-13 09:01:47 +00:00
Properly handle errors when following a symlink
Fixes #123. The code assumes that every File that has its link_target() method called would first have been checked to make sure it’s actually a link first. Unfortunately it also assumed that the only thing that can go wrong while following a link is if the file wasn’t a link, meaning it crashes when given a link it doesn’t have permission to follow. This makes the file_target() method able to return either a file or path for displaying, as before, but also an IO error for when things go wrong.
This commit is contained in:
parent
ba366fc855
commit
74358c188a
@ -3,6 +3,7 @@
|
||||
use std::ascii::AsciiExt;
|
||||
use std::env::current_dir;
|
||||
use std::fs;
|
||||
use std::io::Error as IOError;
|
||||
use std::io::Result as IOResult;
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -162,10 +163,10 @@ impl<'dir> File<'dir> {
|
||||
/// If statting the file fails (usually because the file on the
|
||||
/// other end doesn't exist), returns the path to the file
|
||||
/// that should be there.
|
||||
pub fn link_target(&self) -> Result<File<'dir>, PathBuf> {
|
||||
pub fn link_target(&self) -> FileTarget<'dir> {
|
||||
let path = match fs::read_link(&self.path) {
|
||||
Ok(path) => path,
|
||||
Err(_) => panic!("This was not a link!"),
|
||||
Err(e) => return FileTarget::Err(e),
|
||||
};
|
||||
|
||||
let target_path = match self.dir {
|
||||
@ -180,7 +181,7 @@ impl<'dir> File<'dir> {
|
||||
|
||||
// Use plain `metadata` instead of `symlink_metadata` - we *want* to follow links.
|
||||
if let Ok(metadata) = fs::metadata(&target_path) {
|
||||
Ok(File {
|
||||
FileTarget::Ok(File {
|
||||
path: target_path.to_path_buf(),
|
||||
dir: self.dir,
|
||||
metadata: metadata,
|
||||
@ -189,7 +190,7 @@ impl<'dir> File<'dir> {
|
||||
})
|
||||
}
|
||||
else {
|
||||
Err(target_path)
|
||||
FileTarget::Broken(target_path)
|
||||
}
|
||||
}
|
||||
|
||||
@ -425,6 +426,23 @@ fn ext(path: &Path) -> Option<String> {
|
||||
}
|
||||
|
||||
|
||||
/// The result of following a symlink.
|
||||
pub enum FileTarget<'dir> {
|
||||
|
||||
/// The symlink pointed at a file that exists.
|
||||
Ok(File<'dir>),
|
||||
|
||||
/// The symlink pointed at a file that does not exist. Holds the path
|
||||
/// where the file would be, if it existed.
|
||||
Broken(PathBuf),
|
||||
|
||||
/// There was an IO error when following the link. This can happen if the
|
||||
/// file isn't a link to begin with, but also if, say, we don't have
|
||||
/// permission to follow it.
|
||||
Err(IOError),
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::ext;
|
||||
|
@ -2,7 +2,7 @@ mod dir;
|
||||
pub use self::dir::Dir;
|
||||
|
||||
mod file;
|
||||
pub use self::file::File;
|
||||
pub use self::file::{File, FileTarget};
|
||||
|
||||
pub mod feature;
|
||||
pub mod fields;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use ansi_term::Style;
|
||||
|
||||
use fs::File;
|
||||
use fs::{File, FileTarget};
|
||||
|
||||
pub use self::cell::{TextCell, TextCellContents, DisplayWidth};
|
||||
pub use self::colours::Colours;
|
||||
@ -42,7 +42,7 @@ pub fn filename(file: &File, colours: &Colours, links: bool) -> TextCellContents
|
||||
|
||||
if links && file.is_link() {
|
||||
match file.link_target() {
|
||||
Ok(target) => {
|
||||
FileTarget::Ok(target) => {
|
||||
bits.push(Style::default().paint(" "));
|
||||
bits.push(colours.punctuation.paint("->"));
|
||||
bits.push(Style::default().paint(" "));
|
||||
@ -67,12 +67,16 @@ pub fn filename(file: &File, colours: &Colours, links: bool) -> TextCellContents
|
||||
}
|
||||
},
|
||||
|
||||
Err(broken_path) => {
|
||||
FileTarget::Broken(broken_path) => {
|
||||
bits.push(Style::default().paint(" "));
|
||||
bits.push(colours.broken_arrow.paint("->"));
|
||||
bits.push(Style::default().paint(" "));
|
||||
bits.push(colours.broken_filename.paint(broken_path.display().to_string()));
|
||||
},
|
||||
|
||||
FileTarget::Err(_) => {
|
||||
// Do nothing -- the error gets displayed on the next line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
2
xtests/proc_1_root
Normal file
2
xtests/proc_1_root
Normal file
@ -0,0 +1,2 @@
|
||||
[36ml[1;33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[31mw[32mx[0m [1;32m0[0m root [34m29 Oct 17:23[0m [36m/proc/1/root[0m
|
||||
[38;5;244m└──[0m [31m<Permission denied (os error 13)>[0m
|
@ -52,5 +52,7 @@ $exa $testcases/links -T 2>&1 | diff -q - $results/links_T || exit 1
|
||||
|
||||
COLUMNS=80 $exa $testcases/links 2>&1 | diff -q - $results/links || exit 1
|
||||
|
||||
$exa /proc/1/root -l 2>&1 | diff - $results/proc_1_root || exit 1
|
||||
|
||||
|
||||
echo "All the tests passed!"
|
||||
|
Loading…
Reference in New Issue
Block a user