Repository: JuliaLang/julia
File: base/file.jl (Julia)

Code before:

julia> rm("this_file_does_not_exist", force=true)

julia> rm("this_file_does_not_exist")
ERROR: IOError: unlink("this_file_does_not_exist"): no such file or directory (ENOENT)
Stacktrace:
[...]
```
"""
function rm(path::AbstractString; force::Bool=false, recursive::Bool=false)
    if islink(path) || !isdir(path)
        try
            unlink(path)
        catch err
            if isa(err, IOError)
                force && err.code==Base.UV_ENOENT && return
                @static if Sys.iswindows()
                    if err.code==Base.UV_EACCES && endswith(path, ".dll")
                        # Loaded DLLs cannot be deleted on Windows, even with posix delete mode
                        # but they can be renamed. Do so temporarily, until later cleanup by Pkg.gc()
                        temp_path = tempname(dirname(path), cleanup = false, suffix = string("_", basename(path)))
                        # ensure that temp_path is on the same drive as path to avoid issue #59589
                        @debug "Could not delete DLL most likely because it is loaded, moving to a temporary path" path temp_path
                        Base.open(delayed_delete_list(), "a") do io
                            println(io, abspath(temp_path)) # record the temporary path for Pkg.gc()
                        end
                        rename(path, temp_path) # do not call mv which could recursively call rm(path)
                        return
                    end
                end
            end
            rethrow()
        end
    else
        if recursive
            try
                for p in readdir(path)
                    try
                        rm(joinpath(path, p), force=force, recursive=true)
                    catch err
                        (isa(err, IOError) && err.code==Base.UV_EACCES) || rethrow()
                    end
                end
            catch err
                (isa(err, IOError) && err.code==Base.UV_EACCES) || rethrow()
            end
        end
        req = Libc.malloc(_sizeof_uv_fs)
        try
            ret = ccall(:uv_fs_rmdir, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL)
            uv_fs_req_cleanup(req)

Review comment (bug):
This looks like a race condition if multiple julia processes are running `rm` at the same time.

Code after:

julia> rm("this_file_does_not_exist", force=true)

julia> rm("this_file_does_not_exist")
ERROR: IOError: unlink("this_file_does_not_exist"): no such file or directory (ENOENT)
Stacktrace:
[...]
```
"""
function rm(path::AbstractString; force::Bool=false, recursive::Bool=false, allow_delayed_delete::Bool=true)
    # allow_delayed_delete is used by Pkg.gc() but is otherwise not part of the public API
    if islink(path) || !isdir(path)
        try
            unlink(path)
        catch err
            if isa(err, IOError)
                force && err.code==Base.UV_ENOENT && return
                @static if Sys.iswindows()
                    if allow_delayed_delete && err.code==Base.UV_EACCES && endswith(path, ".dll")
                        delayed_delete_dll(path)
                        return
                    end
                end
            end
            rethrow()
        end
    else
        if recursive
            try
                for p in readdir(path)
                    try
                        rm(joinpath(path, p), force=force, recursive=true)
                    catch err
                        (isa(err, IOError) && err.code==Base.UV_EACCES) || rethrow()
                    end
                end
            catch err
                (isa(err, IOError) && err.code==Base.UV_EACCES) || rethrow()
            end
        end
        req = Libc.malloc(_sizeof_uv_fs)
        try
            ret = ccall(:uv_fs_rmdir, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL)
            uv_fs_req_cleanup(req)
            if ret < 0 && !(force && ret == Base.UV_ENOENT)
                uv_error("rm($(repr(path)))", ret)
            end
            nothing
        finally
            Libc.free(req)
        end