diff --git a/base/file.jl b/base/file.jl index f4fa59f5d1a3e..cbebfb497e886 100644 --- a/base/file.jl +++ b/base/file.jl @@ -62,8 +62,8 @@ end # The following use Unix command line facilites rm(path::String) = FS.unlink(path) -cp(src::String, dst::String) = run(`cp $src $dst`) -mv(src::String, dst::String) = run(`mv $src $dst`) +cp(src::String, dst::String) = FS.sendfile(src, dst) +mv(src::String, dst::String) = FS.rename(src, dst) touch(path::String) = run(`touch $path`) # Obtain a temporary filename. @@ -133,33 +133,6 @@ end end end -downloadcmd = nothing -function download(url::String, filename::String) - global downloadcmd - if downloadcmd === nothing - for checkcmd in (:curl, :wget, :fetch) - if success(`which $checkcmd` |> DevNull) - downloadcmd = checkcmd - break - end - end - end - if downloadcmd == :wget - run(`wget -O $filename $url`) - elseif downloadcmd == :curl - run(`curl -o $filename -L $url`) - elseif downloadcmd == :fetch - run(`fetch -f $filename $url`) - else - error("no download agent available; install curl, wget, or fetch") - end - filename -end -function download(url::String) - filename = tempname() - download(url, filename) -end - function readdir(path::String) # Allocate space for uv_fs_t struct uv_readdir_req = zeros(Uint8, ccall(:jl_sizeof_uv_fs_t, Int32, ())) diff --git a/base/fs.jl b/base/fs.jl index 38027b337a672..d7e97c51a390c 100644 --- a/base/fs.jl +++ b/base/fs.jl @@ -20,6 +20,8 @@ export File, # close, write, unlink, + rename, + sendfile, JL_O_WRONLY, JL_O_RDONLY, JL_O_RDWR, @@ -102,6 +104,51 @@ function unlink(f::File) f end +# For move command +function rename(src::String, dst::String) + err = ccall(:jl_fs_rename, Int32, (Ptr{Uint8}, Ptr{Uint8}), bytestring(src), + bytestring(dst)) + + # on error, default to cp && rm + if err < 0 + # first copy + err = sendfile(src, dst) + uv_error("sendfile when moving file", err) + + # then rm + err = unlink(src) + uv_error("removing when moving file", err) + end +end + +# For copy command +function sendfile(src::String, dst::String) + flags = JL_O_RDONLY + src_file = open(src, flags) + if !src_file.open + error("Src file is not open") + end + + flags = JL_O_CREAT | JL_O_RDWR + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP| S_IROTH | S_IWOTH + dst_file = open(dst, flags, mode) + if !dst_file.open + error("Dst file is not open") + end + + src_stat = stat(src_file) + err = ccall(:jl_fs_sendfile, Int32, (Int32, Int32, Int64, Csize_t), + fd(src_file), fd(dst_file), 0, src_stat.size) + uv_error("sendfile", err) + + if src_file.open + close(src_file) + end + if dst_file.open + close(dst_file) + end +end + function write(f::File, buf::Ptr{Uint8}, len::Integer, offset::Integer=-1) if !f.open error("file is not open") diff --git a/base/util.jl b/base/util.jl index 0c370f435e593..9ebc1f8e38549 100644 --- a/base/util.jl +++ b/base/util.jl @@ -415,6 +415,35 @@ print_with_color(color::Symbol, io::IO, msg::String...) = print_with_color(color::Symbol, msg::String...) = print_with_color(color, STDOUT, msg...) +## file downloading ## + +downloadcmd = nothing +function download(url::String, filename::String) + global downloadcmd + if downloadcmd === nothing + for checkcmd in (:curl, :wget, :fetch) + if success(`which $checkcmd` |> DevNull) + downloadcmd = checkcmd + break + end + end + end + if downloadcmd == :wget + run(`wget -O $filename $url`) + elseif downloadcmd == :curl + run(`curl -o $filename -L $url`) + elseif downloadcmd == :fetch + run(`fetch -f $filename $url`) + else + error("no download agent available; install curl, wget, or fetch") + end + filename +end +function download(url::String) + filename = tempname() + download(url, filename) +end + ## warnings and messages ## function info(msg::String...; prefix="INFO: ") diff --git a/src/jl_uv.c b/src/jl_uv.c index 320459260faa3..52dcd50c9ad77 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -403,6 +403,24 @@ DLLEXPORT int jl_fs_unlink(char *path) return ret; } +DLLEXPORT int jl_fs_rename(char *src_path, char *dst_path) +{ + uv_fs_t req; + int ret = uv_fs_rename(jl_io_loop, &req, src_path, dst_path, NULL); + uv_fs_req_cleanup(&req); + return ret; +} + +DLLEXPORT int jl_fs_sendfile(int src_fd, int dst_fd, + int64_t in_offset, size_t len) +{ + uv_fs_t req; + int ret = uv_fs_sendfile(jl_io_loop, &req, dst_fd, src_fd, + in_offset, len, NULL); + uv_fs_req_cleanup(&req); + return ret; +} + DLLEXPORT int jl_fs_write(int handle, char *buf, size_t len, size_t offset) { uv_fs_t req; diff --git a/src/julia.expmap b/src/julia.expmap index c56bea027995d..e1352c991ff17 100644 --- a/src/julia.expmap +++ b/src/julia.expmap @@ -333,6 +333,8 @@ jl_fs_poll_start; jl_fs_event_init; jl_fs_unlink; + jl_fs_rename; + jl_fs_sendfile; jl_fs_write; jl_fs_write_byte; jl_fs_read; diff --git a/test/file.jl b/test/file.jl index d0478759d438a..347913e72b986 100644 --- a/test/file.jl +++ b/test/file.jl @@ -8,22 +8,22 @@ close(open(file,"w")) # like touch, but lets the operating system update the tim ####################################################################### # This section tests some of the features of the stat-based file info # ####################################################################### -@test isdir(dir) == true -@test isfile(dir) == false -@test islink(dir) == false -@test isdir(file) == false -@test isfile(file) == true -@test islink(file) == false -@test isreadable(file) == true -@test iswritable(file) == true +@test isdir(dir) +@test !isfile(dir) +@test !islink(dir) +@test !isdir(file) +@test isfile(file) +@test !islink(file) +@test isreadable(file) +@test iswritable(file) # Here's something else that might be UNIX-specific? run(`chmod -w $file`) -@test iswritable(file) == false +@test !iswritable(file) run(`chmod +w $file`) -@test isexecutable(file) == false +@test !isexecutable(file) @test filesize(file) == 0 -# On windows the filesize of a folder is the accumulation of all the contained -# files and is thus zero in this case. +# On windows the filesize of a folder is the accumulation of all the contained +# files and is thus zero in this case. @windows_only begin @test filesize(dir) == 0 end @@ -35,10 +35,28 @@ end # rename file newfile = joinpath(dir, "bfile.txt") mv(file, newfile) -@test ispath(file) == false -@test isfile(newfile) == true +@test !ispath(file) +@test isfile(newfile) file = newfile +# Test renaming directories +a_tmpdir = mktempdir() +b_tmpdir = joinpath(dir, "b_tmpdir") + +# grab a_tmpdir's file info before renaming +a_stat = stat(a_tmpdir) + +# rename, then make sure b_tmpdir does exist and a_tmpdir doesn't +mv(a_tmpdir, b_tmpdir) +@test isdir(b_tmpdir) +@test !ispath(a_tmpdir) + +# get b_tmpdir's file info and compare with a_tmpdir +b_stat = stat(b_tmpdir) +@test Base.samefile(a_stat, b_stat) + +rmdir(b_tmpdir) + ####################################################################### # This section tests file watchers. # ####################################################################### @@ -53,9 +71,7 @@ function test_timeout(tval) @async test_file_poll(channel,tval) tr = take(channel) t_elapsed = toq() - - @test tr == false - + @test !tr @test tval <= t_elapsed end @@ -63,15 +79,12 @@ function test_touch(slval) tval = slval*1.1 channel = RemoteRef() @async test_file_poll(channel, tval) - sleep(tval/10) # ~ one poll period f = open(file,"a") write(f,"Hello World\n") close(f) - tr = take(channel) - - @test tr == true + @test tr end @@ -102,7 +115,7 @@ function test_monitor_wait(tval) @test events.changed end -# Commented out the tests below due to issues 3015, 3016 and 3020 +# Commented out the tests below due to issues 3015, 3016 and 3020 test_timeout(0.1) test_timeout(1) # the 0.1 second tests are too optimistic @@ -143,7 +156,7 @@ write(s, [0xffffffffffffffff, 0x000000001fffffff]) close(s) s = open(file, "r") -@test isreadonly(s) == true +@test isreadonly(s) b = mmap_bitarray((17,13), s) @test b == trues(17,13) @test_throws mmap_bitarray((7,3), s) @@ -155,7 +168,7 @@ msync(b) b0 = copy(b) close(s) s = open(file, "r") -@test isreadonly(s) == true +@test isreadonly(s) b = mmap_bitarray((17,19), s) @test b == b0 close(s) @@ -184,10 +197,28 @@ emptyf = open(emptyfile) close(emptyf) rm(emptyfile) +# Test copy file +afile = joinpath(dir, "a.txt") +touch(afile) +af = open(afile, "r+") +write(af, "This is indeed a test") + +bfile = joinpath(dir, "b.txt") +cp(afile, bfile) + +a_stat = stat(afile) +b_stat = stat(bfile) +@test a_stat.mode == b_stat.mode +@test a_stat.size == b_stat.size + +close(af) +rm(afile) +rm(bfile) + ############ # Clean up # ############ rm(file) rmdir(dir) -@test ispath(file) == false -@test ispath(dir) == false +@test !ispath(file) +@test !ispath(dir)