Skip to content

Commit 5fc1b40

Browse files
committed
Implement symlink and symlinkat
1 parent f1b12d6 commit 5fc1b40

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

src/unistd.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,45 @@ pub fn mkfifo<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
506506
Errno::result(res).map(drop)
507507
}
508508

509+
/// Creates a symbolic link at `path2` which points to `path1`.
510+
///
511+
/// If `dirfd` has a value, then `path2` is relative to directory associated
512+
/// with the file descriptor.
513+
///
514+
/// If `dirfd` is `None`, then `path2` is relative to the current working directory.
515+
/// This is identical to `symlink(path1, path2)`.
516+
///
517+
/// See also [symlinkat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html).
518+
pub fn symlinkat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
519+
path1: &P1,
520+
dirfd: Option<RawFd>,
521+
path2: &P2) -> Result<()> {
522+
let res =
523+
path1.with_nix_path(|path1| {
524+
path2.with_nix_path(|path2| {
525+
unsafe {
526+
libc::symlinkat(
527+
path1.as_ptr(),
528+
dirfd.unwrap_or(libc::AT_FDCWD),
529+
path2.as_ptr()
530+
)
531+
}
532+
})
533+
})??;
534+
Errno::result(res).map(drop)
535+
}
536+
537+
/// Creates a symbolic link at `path2` which points to `path1`.
538+
///
539+
/// This is same as calling `symlinkat(path1, None, path2)`.
540+
///
541+
/// See also [symlink(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html)
542+
pub fn symlink<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
543+
path1: &P1,
544+
path2: &P2) -> Result<()> {
545+
symlinkat(path1, None, path2)
546+
}
547+
509548
/// Returns the current directory as a `PathBuf`
510549
///
511550
/// Err is returned if the current user doesn't have the permission to read or search a component

test/test_unistd.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use nix::fcntl::{fcntl, FcntlArg, FdFlag, open, OFlag};
1+
use nix::fcntl::{fcntl, FcntlArg, FdFlag, open, OFlag, readlink};
22
use nix::unistd::*;
33
use nix::unistd::ForkResult::*;
44
use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction};
@@ -543,3 +543,29 @@ fn test_canceling_alarm() {
543543
assert_eq!(alarm::set(60), None);
544544
assert_eq!(alarm::cancel(), Some(60));
545545
}
546+
547+
#[test]
548+
fn test_symlink() {
549+
let mut buf = [0; 1024];
550+
let tempdir = TempDir::new("nix-test_symlinkat")
551+
.unwrap_or_else(|e| panic!("tempdir failed: {}", e));
552+
553+
let target = tempdir.path().join("a");
554+
let linkpath = tempdir.path().join("b");
555+
symlink(&target, &linkpath).unwrap();
556+
assert_eq!(readlink(&linkpath, &mut buf).unwrap().to_str().unwrap(),
557+
target.to_str().unwrap());
558+
559+
let target = tempdir.path().join("c");
560+
let linkpath = tempdir.path().join("d");
561+
symlinkat(&target, None, &linkpath).unwrap();
562+
assert_eq!(readlink(&linkpath, &mut buf).unwrap().to_str().unwrap(),
563+
target.to_str().unwrap());
564+
565+
let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
566+
let target = "e";
567+
let linkpath = "f";
568+
symlinkat(target, Some(dirfd), linkpath).unwrap();
569+
assert_eq!(readlink(&tempdir.path().join(linkpath), &mut buf).unwrap().to_str().unwrap(),
570+
target);
571+
}

0 commit comments

Comments
 (0)