Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filesystem: add hardlink function #41639

Merged
merged 4 commits into from
Jul 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Build system changes
New library functions
---------------------

* `hardlink(src, dst)` can be used to create hard links. ([#41639])

New library features
--------------------
Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,7 @@ export
filemode,
filesize,
gperm,
hardlink,
isblockdev,
ischardev,
isdir,
Expand Down
23 changes: 23 additions & 0 deletions base/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export
chown,
cp,
cptree,
hardlink,
mkdir,
mkpath,
mktemp,
Expand Down Expand Up @@ -998,6 +999,26 @@ if Sys.iswindows()
const UV__EPERM = -4048
end

"""
hardlink(src::AbstractString, dst::AbstractString)

Creates a hard link to an existing source file `src` with the name `dst`. The
destination, `dst`, must not exist.

See also: [`symlink`](@ref).

!!! compat "Julia 1.8"
This method was added in Julia 1.8.
"""
function hardlink(src::AbstractString, dst::AbstractString)
err = ccall(:jl_fs_hardlink, Int32, (Cstring, Cstring), src, dst)
if err < 0
msg = "hardlink($(repr(src)), $(repr(dst)))"
uv_error(msg, err)
end
return nothing
end

"""
symlink(target::AbstractString, link::AbstractString; dir_target = false)

Expand All @@ -1020,6 +1041,8 @@ a junction point will be used. Best practice for creating symlinks on Windows
is to create them only after the files/directories they reference are already
created.

See also: [`hardlink`](@ref).

!!! note
This function raises an error under operating systems that do not support
soft symbolic links, such as Windows XP.
Expand Down
1 change: 1 addition & 0 deletions doc/src/base/file.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Base.Filesystem.readdir
Base.Filesystem.walkdir
Base.Filesystem.mkdir
Base.Filesystem.mkpath
Base.Filesystem.hardlink
Base.Filesystem.symlink
Base.Filesystem.readlink
Base.Filesystem.chmod
Expand Down
8 changes: 8 additions & 0 deletions src/jl_uv.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,14 @@ JL_DLLEXPORT int jl_fs_sendfile(uv_os_fd_t src_fd, uv_os_fd_t dst_fd,
return ret;
}

JL_DLLEXPORT int jl_fs_hardlink(char *path, char *new_path)
{
uv_fs_t req;
int ret = uv_fs_link(unused_uv_loop_arg, &req, path, new_path, NULL);
uv_fs_req_cleanup(&req);
return ret;
}

JL_DLLEXPORT int jl_fs_symlink(char *path, char *new_path, int flags)
{
uv_fs_t req;
Expand Down
19 changes: 17 additions & 2 deletions test/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,32 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
end

if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
link = joinpath(dir, "afilelink.txt")
link = joinpath(dir, "afilesymlink.txt")
symlink(file, link)
@test stat(file) == stat(link)

# relative link
rellink = joinpath(subdir, "rel_afilelink.txt")
rellink = joinpath(subdir, "rel_afilesymlink.txt")
relfile = joinpath("..", "afile.txt")
symlink(relfile, rellink)
@test stat(rellink) == stat(file)
end

@testset "hardlink" begin
link = joinpath(dir, "afilehardlink.txt")
hardlink(file, link)
@test stat(file) == stat(link)

# when the destination exists
@test_throws Base.IOError hardlink(file, link)

rm(link)

# the source file does not exist
missing_file = joinpath(dir, "for-sure-missing-file.txt")
@test_throws Base.IOError hardlink(missing_file, link)
end

using Random

@testset "that temp names are actually unique" begin
Expand Down