about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrian Campbell <lambda@continuation.org>2015-04-09 03:22:44 -0400
committerBrian Campbell <lambda@continuation.org>2015-04-21 12:14:22 -0400
commit3cc84efcdd5727c0749d766d8abd79d8077f9cec (patch)
tree85d17d12eeb690ad36d9fa6f4c6a405839f3ceee
parenta691f1eefea586f154700be6ee1b991158f82b7f (diff)
downloadrust-3cc84efcdd5727c0749d766d8abd79d8077f9cec.tar.gz
rust-3cc84efcdd5727c0749d766d8abd79d8077f9cec.zip
Deprecate std::fs::soft_link in favor of platform-specific versions
On Windows, when you create a symbolic link you must specify whether it
points to a directory or a file, even if it is created dangling, while
on Unix, the same symbolic link could point to a directory, a file, or
nothing at all.  Furthermore, on Windows special privilege is necessary
to use a symbolic link, while on Unix, you can generally create a
symbolic link in any directory you have write privileges to.

This means that it is unlikely to be able to use symbolic links purely
portably; anyone who uses them will need to think about the cross
platform implications.  This means that using platform-specific APIs
will make it easier to see where code will need to differ between the
platforms, rather than trying to provide some kind of compatibility
wrapper.

Furthermore, `soft_link` has no precedence in any other API, so to avoid
confusion, move back to the more standard `symlink` terminology.

Create a `std::os::unix::symlink` for the Unix version that is
destination type agnostic, as well as `std::os::windows::{symlink_file,
symlink_dir}` for Windows.

Because this is a stable API, leave a compatibility wrapper in
`std::fs::soft_link`, which calls `symlink` on Unix and `symlink_file`
on Windows, preserving the existing behavior of `soft_link`.
-rw-r--r--src/libstd/fs.rs21
-rw-r--r--src/libstd/sys/unix/ext.rs34
-rw-r--r--src/libstd/sys/windows/c.rs2
-rw-r--r--src/libstd/sys/windows/ext.rs48
-rw-r--r--src/libstd/sys/windows/fs2.rs7
5 files changed, 104 insertions, 8 deletions
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 6e902a47396..2364e1d42bb 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -700,7 +700,7 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// Given a path, query the file system to get information about a file,
 /// directory, etc.
 ///
-/// This function will traverse soft links to query information about the
+/// This function will traverse symbolic links to query information about the
 /// destination file.
 ///
 /// # Examples
@@ -814,9 +814,13 @@ pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<(
     fs_imp::link(src.as_ref(), dst.as_ref())
 }
 
-/// Creates a new soft link on the filesystem.
+/// Creates a new symbolic link on the filesystem.
 ///
-/// The `dst` path will be a soft link pointing to the `src` path.
+/// The `dst` path will be a symbolic link pointing to the `src` path.
+/// On Windows, this will be a file symlink, not a directory symlink;
+/// for this reason, the platform-specific `std::os::unix::fs::symlink`
+/// and `std::os::windows::fs::{symlink_file, symlink_dir}` should be
+/// used instead to make the intent explicit.
 ///
 /// # Examples
 ///
@@ -828,17 +832,20 @@ pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<(
 /// # Ok(())
 /// # }
 /// ```
+#[deprecated(since = "1.0.0",
+             reason = "replaced with std::os::unix::fs::symlink and \
+                       std::os::windows::fs::{symlink_file, symlink_dir}")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
     fs_imp::symlink(src.as_ref(), dst.as_ref())
 }
 
-/// Reads a soft link, returning the file that the link points to.
+/// Reads a symbolic link, returning the file that the link points to.
 ///
 /// # Errors
 ///
 /// This function will return an error on failure. Failure conditions include
-/// reading a file that does not exist or reading a file that is not a soft
+/// reading a file that does not exist or reading a file that is not a symbolic
 /// link.
 ///
 /// # Examples
@@ -931,8 +938,8 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// Removes a directory at this path, after removing all its contents. Use
 /// carefully!
 ///
-/// This function does **not** follow soft links and it will simply remove the
-/// soft link itself.
+/// This function does **not** follow symbolic links and it will simply remove the
+/// symbolic link itself.
 ///
 /// # Errors
 ///
diff --git a/src/libstd/sys/unix/ext.rs b/src/libstd/sys/unix/ext.rs
index 032fd33b1d3..9504fe63697 100644
--- a/src/libstd/sys/unix/ext.rs
+++ b/src/libstd/sys/unix/ext.rs
@@ -189,8 +189,12 @@ pub mod ffi {
 #[unstable(feature = "fs_ext",
            reason = "may want a more useful mode abstraction")]
 pub mod fs {
+    use sys;
     use sys_common::{FromInner, AsInner, AsInnerMut};
     use fs::{Permissions, OpenOptions};
+    use path::Path;
+    use convert::AsRef;
+    use io;
 
     /// Unix-specific extensions to `Permissions`
     pub trait PermissionsExt {
@@ -220,6 +224,36 @@ pub mod fs {
             self.as_inner_mut().mode(mode); self
         }
     }
+
+    /// Creates a new symbolic link on the filesystem.
+    ///
+    /// The `dst` path will be a symbolic link pointing to the `src` path.
+    ///
+    /// # Note
+    ///
+    /// On Windows, you must specify whether a symbolic link points to a file
+    /// or directory.  Use `os::windows::fs::symlink_file` to create a
+    /// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a
+    /// symbolic link to a directory.  Additionally, the process must have
+    /// `SeCreateSymbolicLinkPrivilege` in order to be able to create a
+    /// symbolic link.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(fs_ext)]
+    /// use std::os::unix::fs;
+    ///
+    /// # fn foo() -> std::io::Result<()> {
+    /// try!(fs::symlink("a.txt", "b.txt"));
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()>
+    {
+        sys::fs2::symlink(src.as_ref(), dst.as_ref())
+    }
+
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs
index 45f389f0aeb..331bfbfff36 100644
--- a/src/libstd/sys/windows/c.rs
+++ b/src/libstd/sys/windows/c.rs
@@ -54,6 +54,8 @@ pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024;
 pub const FSCTL_GET_REPARSE_POINT: libc::DWORD = 0x900a8;
 pub const IO_REPARSE_TAG_SYMLINK: libc::DWORD = 0xa000000c;
 
+pub const SYMBOLIC_LINK_FLAG_DIRECTORY: libc::DWORD = 0x1;
+
 // Note that these are not actually HANDLEs, just values to pass to GetStdHandle
 pub const STD_INPUT_HANDLE: libc::DWORD = -10i32 as libc::DWORD;
 pub const STD_OUTPUT_HANDLE: libc::DWORD = -11i32 as libc::DWORD;
diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs
index 90548dcefb4..eac6496870e 100644
--- a/src/libstd/sys/windows/ext.rs
+++ b/src/libstd/sys/windows/ext.rs
@@ -191,7 +191,11 @@ pub mod ffi {
 #[unstable(feature = "fs_ext", reason = "may require more thought/methods")]
 pub mod fs {
     use fs::OpenOptions;
+    use sys;
     use sys_common::AsInnerMut;
+    use path::Path;
+    use convert::AsRef;
+    use io;
 
     /// Windows-specific extensions to `OpenOptions`
     pub trait OpenOptionsExt {
@@ -235,6 +239,50 @@ pub mod fs {
             self.as_inner_mut().share_mode(access); self
         }
     }
+
+    /// Creates a new file symbolic link on the filesystem.
+    ///
+    /// The `dst` path will be a file symbolic link pointing to the `src`
+    /// path.
+    ///
+    /// # Examples
+    ///
+    /// ```ignore
+    /// #![feature(fs_ext)]
+    /// use std::os::windows::fs;
+    ///
+    /// # fn foo() -> std::io::Result<()> {
+    /// try!(fs::symlink_file("a.txt", "b.txt"));
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q)
+                                                        -> io::Result<()>
+    {
+        sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), false)
+    }
+
+    /// Creates a new directory symlink on the filesystem.
+    ///
+    /// The `dst` path will be a directory symbolic link pointing to the `src`
+    /// path.
+    ///
+    /// # Examples
+    ///
+    /// ```ignore
+    /// #![feature(fs_ext)]
+    /// use std::os::windows::fs;
+    ///
+    /// # fn foo() -> std::io::Result<()> {
+    /// try!(fs::symlink_file("a", "b"));
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>> (src: P, dst: Q)
+                                                        -> io::Result<()>
+    {
+        sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), true)
+    }
 }
 
 /// A prelude for conveniently writing platform-specific code.
diff --git a/src/libstd/sys/windows/fs2.rs b/src/libstd/sys/windows/fs2.rs
index 9645c51ec0b..90daae3dea4 100644
--- a/src/libstd/sys/windows/fs2.rs
+++ b/src/libstd/sys/windows/fs2.rs
@@ -402,11 +402,16 @@ pub fn readlink(p: &Path) -> io::Result<PathBuf> {
 }
 
 pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
+    symlink_inner(src, dst, false)
+}
+
+pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
     use sys::c::compat::kernel32::CreateSymbolicLinkW;
     let src = to_utf16(src);
     let dst = to_utf16(dst);
+    let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
     try!(cvt(unsafe {
-        CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), 0) as libc::BOOL
+        CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL
     }));
     Ok(())
 }