|
@@ -3,6 +3,7 @@
|
|
|
use std::ascii::AsciiExt;
|
|
use std::ascii::AsciiExt;
|
|
|
use std::env::current_dir;
|
|
use std::env::current_dir;
|
|
|
use std::fs;
|
|
use std::fs;
|
|
|
|
|
+use std::io::Error as IOError;
|
|
|
use std::io::Result as IOResult;
|
|
use std::io::Result as IOResult;
|
|
|
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
|
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
|
|
use std::path::{Path, PathBuf};
|
|
use std::path::{Path, PathBuf};
|
|
@@ -147,7 +148,7 @@ impl<'dir> File<'dir> {
|
|
|
/// Whether this file is a symlink on the filesystem.
|
|
/// Whether this file is a symlink on the filesystem.
|
|
|
pub fn is_link(&self) -> bool {
|
|
pub fn is_link(&self) -> bool {
|
|
|
self.metadata.file_type().is_symlink()
|
|
self.metadata.file_type().is_symlink()
|
|
|
- }
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/// Whether this file is a dotfile, based on its name. In Unix, file names
|
|
/// Whether this file is a dotfile, based on its name. In Unix, file names
|
|
|
/// beginning with a dot represent system or configuration files, and
|
|
/// beginning with a dot represent system or configuration files, and
|
|
@@ -162,10 +163,10 @@ impl<'dir> File<'dir> {
|
|
|
/// If statting the file fails (usually because the file on the
|
|
/// If statting the file fails (usually because the file on the
|
|
|
/// other end doesn't exist), returns the path to the file
|
|
/// other end doesn't exist), returns the path to the file
|
|
|
/// that should be there.
|
|
/// 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) {
|
|
let path = match fs::read_link(&self.path) {
|
|
|
Ok(path) => path,
|
|
Ok(path) => path,
|
|
|
- Err(_) => panic!("This was not a link!"),
|
|
|
|
|
|
|
+ Err(e) => return FileTarget::Err(e),
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
let target_path = match self.dir {
|
|
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.
|
|
// Use plain `metadata` instead of `symlink_metadata` - we *want* to follow links.
|
|
|
if let Ok(metadata) = fs::metadata(&target_path) {
|
|
if let Ok(metadata) = fs::metadata(&target_path) {
|
|
|
- Ok(File {
|
|
|
|
|
|
|
+ FileTarget::Ok(File {
|
|
|
path: target_path.to_path_buf(),
|
|
path: target_path.to_path_buf(),
|
|
|
dir: self.dir,
|
|
dir: self.dir,
|
|
|
metadata: metadata,
|
|
metadata: metadata,
|
|
@@ -189,7 +190,7 @@ impl<'dir> File<'dir> {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
|
- Err(target_path)
|
|
|
|
|
|
|
+ FileTarget::Broken(target_path)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -359,17 +360,17 @@ impl<'dir> File<'dir> {
|
|
|
pub fn is_pipe(&self) -> bool {
|
|
pub fn is_pipe(&self) -> bool {
|
|
|
self.metadata.file_type().is_fifo()
|
|
self.metadata.file_type().is_fifo()
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/// Whether this file is a char device on the filesystem.
|
|
/// Whether this file is a char device on the filesystem.
|
|
|
pub fn is_char_device(&self) -> bool {
|
|
pub fn is_char_device(&self) -> bool {
|
|
|
self.metadata.file_type().is_char_device()
|
|
self.metadata.file_type().is_char_device()
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/// Whether this file is a block device on the filesystem.
|
|
/// Whether this file is a block device on the filesystem.
|
|
|
pub fn is_block_device(&self) -> bool {
|
|
pub fn is_block_device(&self) -> bool {
|
|
|
self.metadata.file_type().is_block_device()
|
|
self.metadata.file_type().is_block_device()
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/// Whether this file is a socket on the filesystem.
|
|
/// Whether this file is a socket on the filesystem.
|
|
|
pub fn is_socket(&self) -> bool {
|
|
pub fn is_socket(&self) -> bool {
|
|
|
self.metadata.file_type().is_socket()
|
|
self.metadata.file_type().is_socket()
|
|
@@ -382,17 +383,17 @@ impl<'dir> File<'dir> {
|
|
|
pub fn is_pipe(&self) -> bool {
|
|
pub fn is_pipe(&self) -> bool {
|
|
|
false
|
|
false
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/// Whether this file is a char device on the filesystem.
|
|
/// Whether this file is a char device on the filesystem.
|
|
|
pub fn is_char_device(&self) -> bool {
|
|
pub fn is_char_device(&self) -> bool {
|
|
|
false
|
|
false
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/// Whether this file is a block device on the filesystem.
|
|
/// Whether this file is a block device on the filesystem.
|
|
|
pub fn is_block_device(&self) -> bool {
|
|
pub fn is_block_device(&self) -> bool {
|
|
|
false
|
|
false
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
/// Whether this file is a socket on the filesystem.
|
|
/// Whether this file is a socket on the filesystem.
|
|
|
pub fn is_socket(&self) -> bool {
|
|
pub fn is_socket(&self) -> bool {
|
|
|
false
|
|
false
|
|
@@ -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)]
|
|
#[cfg(test)]
|
|
|
mod test {
|
|
mod test {
|
|
|
use super::ext;
|
|
use super::ext;
|