Skip to content

Commit 1489e23

Browse files
committed
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`.
1 parent a691f1e commit 1489e23

File tree

5 files changed

+104
-8
lines changed

5 files changed

+104
-8
lines changed

src/libstd/fs.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
700700
/// Given a path, query the file system to get information about a file,
701701
/// directory, etc.
702702
///
703-
/// This function will traverse soft links to query information about the
703+
/// This function will traverse symbolic links to query information about the
704704
/// destination file.
705705
///
706706
/// # Examples
@@ -814,9 +814,13 @@ pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<(
814814
fs_imp::link(src.as_ref(), dst.as_ref())
815815
}
816816

817-
/// Creates a new soft link on the filesystem.
817+
/// Creates a new symbolic link on the filesystem.
818818
///
819-
/// The `dst` path will be a soft link pointing to the `src` path.
819+
/// The `dst` path will be a symbolic link pointing to the `src` path.
820+
/// On Windows, this will be a file symlink, not a directory symlink;
821+
/// for this reason, the platform-specific `std::os::unix::fs::symlink`
822+
/// and `std::os::windows::fs::{symlink_file, symlink_dir}` should be
823+
/// used instead to make the intent explicit.
820824
///
821825
/// # Examples
822826
///
@@ -828,17 +832,20 @@ pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<(
828832
/// # Ok(())
829833
/// # }
830834
/// ```
835+
#[deprecated(since = "1.0.0",
836+
reason = "replaced with std::os::unix::fs::symlink and \
837+
std::os::windows::fs::{symlink_file, symlink_dir}")]
831838
#[stable(feature = "rust1", since = "1.0.0")]
832839
pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
833840
fs_imp::symlink(src.as_ref(), dst.as_ref())
834841
}
835842

836-
/// Reads a soft link, returning the file that the link points to.
843+
/// Reads a symbolic link, returning the file that the link points to.
837844
///
838845
/// # Errors
839846
///
840847
/// This function will return an error on failure. Failure conditions include
841-
/// reading a file that does not exist or reading a file that is not a soft
848+
/// reading a file that does not exist or reading a file that is not a symbolic
842849
/// link.
843850
///
844851
/// # Examples
@@ -931,8 +938,8 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
931938
/// Removes a directory at this path, after removing all its contents. Use
932939
/// carefully!
933940
///
934-
/// This function does **not** follow soft links and it will simply remove the
935-
/// soft link itself.
941+
/// This function does **not** follow symbolic links and it will simply remove the
942+
/// symbolic link itself.
936943
///
937944
/// # Errors
938945
///

src/libstd/sys/unix/ext.rs

+34
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,12 @@ pub mod ffi {
189189
#[unstable(feature = "fs_ext",
190190
reason = "may want a more useful mode abstraction")]
191191
pub mod fs {
192+
use sys;
192193
use sys_common::{FromInner, AsInner, AsInnerMut};
193194
use fs::{Permissions, OpenOptions};
195+
use path::Path;
196+
use convert::AsRef;
197+
use io;
194198

195199
/// Unix-specific extensions to `Permissions`
196200
pub trait PermissionsExt {
@@ -220,6 +224,36 @@ pub mod fs {
220224
self.as_inner_mut().mode(mode); self
221225
}
222226
}
227+
228+
/// Creates a new symbolic link on the filesystem.
229+
///
230+
/// The `dst` path will be a symbolic link pointing to the `src` path.
231+
///
232+
/// # Note
233+
///
234+
/// On Windows, you must specify whether a symbolic link points to a file
235+
/// or directory. Use `os::windows::fs::symlink_file` to create a
236+
/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a
237+
/// symbolic link to a directory. Additionally, the process must have
238+
/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a
239+
/// symbolic link.
240+
///
241+
/// # Examples
242+
///
243+
/// ```
244+
/// #![feature(fs_ext)]
245+
/// use std::os::unix::fs;
246+
///
247+
/// # fn foo() -> std::io::Result<()> {
248+
/// try!(fs::symlink("a.txt", "b.txt"));
249+
/// # Ok(())
250+
/// # }
251+
/// ```
252+
pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()>
253+
{
254+
sys::fs2::symlink(src.as_ref(), dst.as_ref())
255+
}
256+
223257
}
224258

225259
////////////////////////////////////////////////////////////////////////////////

src/libstd/sys/windows/c.rs

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024;
5454
pub const FSCTL_GET_REPARSE_POINT: libc::DWORD = 0x900a8;
5555
pub const IO_REPARSE_TAG_SYMLINK: libc::DWORD = 0xa000000c;
5656

57+
pub const SYMBOLIC_LINK_FLAG_DIRECTORY: libc::DWORD = 0x1;
58+
5759
// Note that these are not actually HANDLEs, just values to pass to GetStdHandle
5860
pub const STD_INPUT_HANDLE: libc::DWORD = -10i32 as libc::DWORD;
5961
pub const STD_OUTPUT_HANDLE: libc::DWORD = -11i32 as libc::DWORD;

src/libstd/sys/windows/ext.rs

+48
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,11 @@ pub mod ffi {
191191
#[unstable(feature = "fs_ext", reason = "may require more thought/methods")]
192192
pub mod fs {
193193
use fs::OpenOptions;
194+
use sys;
194195
use sys_common::AsInnerMut;
196+
use path::Path;
197+
use convert::AsRef;
198+
use io;
195199

196200
/// Windows-specific extensions to `OpenOptions`
197201
pub trait OpenOptionsExt {
@@ -235,6 +239,50 @@ pub mod fs {
235239
self.as_inner_mut().share_mode(access); self
236240
}
237241
}
242+
243+
/// Creates a new file symbolic link on the filesystem.
244+
///
245+
/// The `dst` path will be a file symbolic link pointing to the `src`
246+
/// path.
247+
///
248+
/// # Examples
249+
///
250+
/// ```
251+
/// #![feature(fs_ext)]
252+
/// use std::os::windows::fs;
253+
///
254+
/// # fn foo() -> std::io::Result<()> {
255+
/// try!(fs::symlink_file("a.txt", "b.txt"));
256+
/// # Ok(())
257+
/// # }
258+
/// ```
259+
pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q)
260+
-> io::Result<()>
261+
{
262+
sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), false)
263+
}
264+
265+
/// Creates a new directory symlink on the filesystem.
266+
///
267+
/// The `dst` path will be a directory symbolic link pointing to the `src`
268+
/// path.
269+
///
270+
/// # Examples
271+
///
272+
/// ```
273+
/// #![feature(fs_ext)]
274+
/// use std::os::windows::fs;
275+
///
276+
/// # fn foo() -> std::io::Result<()> {
277+
/// try!(fs::symlink_file("a", "b"));
278+
/// # Ok(())
279+
/// # }
280+
/// ```
281+
pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>> (src: P, dst: Q)
282+
-> io::Result<()>
283+
{
284+
sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), true)
285+
}
238286
}
239287

240288
/// A prelude for conveniently writing platform-specific code.

src/libstd/sys/windows/fs2.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -402,11 +402,16 @@ pub fn readlink(p: &Path) -> io::Result<PathBuf> {
402402
}
403403

404404
pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
405+
symlink_inner(src, dst, false)
406+
}
407+
408+
pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
405409
use sys::c::compat::kernel32::CreateSymbolicLinkW;
406410
let src = to_utf16(src);
407411
let dst = to_utf16(dst);
412+
let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
408413
try!(cvt(unsafe {
409-
CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), 0) as libc::BOOL
414+
CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL
410415
}));
411416
Ok(())
412417
}

0 commit comments

Comments
 (0)