-
-
Notifications
You must be signed in to change notification settings - Fork 6k
Expand file tree
/
Copy pathfilesystem.jl
More file actions
449 lines (386 loc) · 13.5 KB
/
Copy pathfilesystem.jl
File metadata and controls
449 lines (386 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
# This file is a part of Julia. License is MIT: https://julialang.org/license
# NB: This file is `Core.eval`-uated into the (pre-existing) module Filesystem
## File Operations (Libuv-based) ##
"""
JL_O_APPEND
JL_O_ASYNC
JL_O_CLOEXEC
JL_O_CREAT
JL_O_DIRECT
JL_O_DIRECTORY
JL_O_DSYNC
JL_O_EXCL
JL_O_FSYNC
JL_O_LARGEFILE
JL_O_NDELAY
JL_O_NOATIME
JL_O_NOCTTY
JL_O_NOFOLLOW
JL_O_NONBLOCK
JL_O_PATH
JL_O_RANDOM
JL_O_RDONLY
JL_O_RDWR
JL_O_RSYNC
JL_O_SEQUENTIAL
JL_O_SHORT_LIVED
JL_O_SYNC
JL_O_TEMPORARY
JL_O_TMPFILE
JL_O_TRUNC
JL_O_WRONLY
Enum constant for the `open` syscall, where `JL_O_*` corresponds to the `O_*` constant.
See [the libuv docs](https://docs.libuv.org/en/v1.x/fs.html#file-open-constants) for more details.
"""
(:JL_O_APPEND, :JL_O_ASYNC, :JL_O_CLOEXEC, :JL_O_CREAT, :JL_O_DIRECT,
:JL_O_DIRECTORY, :JL_O_DSYNC, :JL_O_EXCL, :JL_O_FSYNC, :JL_O_LARGEFILE,
:JL_O_NOATIME, :JL_O_NOCTTY, :JL_O_NDELAY, :JL_O_NOFOLLOW, :JL_O_NONBLOCK,
:JL_O_PATH, :JL_O_RANDOM, :JL_O_RDONLY, :JL_O_RDWR, :JL_O_RSYNC,
:JL_O_SEQUENTIAL, :JL_O_SHORT_LIVED, :JL_O_SYNC, :JL_O_TEMPORARY,
:JL_O_TMPFILE, :JL_O_TRUNC, :JL_O_WRONLY)
const S_IFDIR = 0o040000 # directory
const S_IFCHR = 0o020000 # character device
const S_IFBLK = 0o060000 # block device
const S_IFREG = 0o100000 # regular file
const S_IFIFO = 0o010000 # fifo (named pipe)
const S_IFLNK = 0o120000 # symbolic link
const S_IFSOCK = 0o140000 # socket file
const S_IFMT = 0o170000
const S_ISUID = 0o4000 # set UID bit
const S_ISGID = 0o2000 # set GID bit
const S_ENFMT = S_ISGID # file locking enforcement
const S_ISVTX = 0o1000 # sticky bit
const S_IRUSR = 0o0400 # read by owner
const S_IWUSR = 0o0200 # write by owner
const S_IXUSR = 0o0100 # execute by owner
const S_IRWXU = 0o0700 # mask for owner permissions
const S_IRGRP = 0o0040 # read by group
const S_IWGRP = 0o0020 # write by group
const S_IXGRP = 0o0010 # execute by group
const S_IRWXG = 0o0070 # mask for group permissions
const S_IROTH = 0o0004 # read by other
const S_IWOTH = 0o0002 # write by other
const S_IXOTH = 0o0001 # execute by other
const S_IRWXO = 0o0007 # mask for other permissions
"""
S_IRUSR
S_IWUSR
S_IXUSR
S_IRGRP
S_IWGRP
S_IXGRP
S_IROTH
S_IWOTH
S_IXOTH
Constants for file access permission bits.
The general structure is `S_I[permission][class]`
where `permission` is `R` for read, `W` for write, and `X` for execute,
and `class` is `USR` for user/owner, `GRP` for group, and `OTH` for other.
"""
(:S_IRUSR, :S_IWUSR, :S_IXUSR, :S_IRGRP, :S_IWGRP, :S_IXGRP, :S_IROTH, :S_IWOTH, :S_IXOTH)
"""
S_IRWXU
S_IRWXG
S_IRWXO
Constants for file access permission masks, i.e. the combination of read, write,
and execute permissions for a class.
The general structure is `S_IRWX[class]`
where `class` is `U` for user/owner, `G` for group, and `O` for other.
"""
(:S_IRWXU, :S_IRWXG, :S_IRWXO)
export File,
StatStruct,
# open,
futime,
write,
JL_O_WRONLY,
JL_O_RDONLY,
JL_O_RDWR,
JL_O_APPEND,
JL_O_CREAT,
JL_O_EXCL,
JL_O_TRUNC,
JL_O_TEMPORARY,
JL_O_SHORT_LIVED,
JL_O_SEQUENTIAL,
JL_O_RANDOM,
JL_O_NOCTTY,
JL_O_NONBLOCK,
JL_O_NDELAY,
JL_O_SYNC,
JL_O_FSYNC,
JL_O_ASYNC,
JL_O_LARGEFILE,
JL_O_DIRECTORY,
JL_O_NOFOLLOW,
JL_O_CLOEXEC,
JL_O_DIRECT,
JL_O_NOATIME,
JL_O_PATH,
JL_O_TMPFILE,
JL_O_DSYNC,
JL_O_RSYNC,
S_IRUSR, S_IWUSR, S_IXUSR, S_IRWXU,
S_IRGRP, S_IWGRP, S_IXGRP, S_IRWXG,
S_IROTH, S_IWOTH, S_IXOTH, S_IRWXO
import .Base:
IOError, _UVError, _sizeof_uv_fs, check_open, close, closewrite, eof, eventloop, fd, isopen,
bytesavailable, position, read, read!, readbytes!, readavailable, seek, seekend, show,
skip, stat, unsafe_read, unsafe_write, write, transcode, uv_error, _uv_error,
setup_stdio, rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror, filesize,
isexecutable, isreadable, iswritable, MutableDenseArrayType, truncate, unsafe_takestring!
import .Base.RefValue
if Sys.iswindows()
import .Base: cwstring
end
# helper function to clean up libuv request
uv_fs_req_cleanup(req) = ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req)
if Sys.iswindows()
const path_separator_re = r"[/\\]+"sa # May be used by some external packages
elseif Sys.isunix()
const path_separator_re = r"/+"sa # May be used by some external packages
end
include("stat.jl")
include("file.jl")
include(string(Base.BUILDROOT, "file_constants.jl")) # include($BUILDROOT/base/file_constants.jl)
## Operations with File (fd) objects ##
abstract type AbstractFile <: IO end
mutable struct File <: AbstractFile
open::Bool
handle::OS_HANDLE
File(fd::OS_HANDLE) = new(true, fd)
end
if OS_HANDLE !== RawFD
File(fd::RawFD) = File(Libc._get_osfhandle(fd)) # TODO: calling close would now destroy the wrong handle
end
rawhandle(file::File) = file.handle
setup_stdio(file::File, ::Bool) = (file, false)
# Filesystem.open, not Base.open
function open(path::AbstractString, flags::Integer, mode::Integer=0)
req = Libc.malloc(_sizeof_uv_fs)
local handle
try
ret = ccall(:uv_fs_open, Int32,
(Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Int32, Int32, Ptr{Cvoid}),
C_NULL, req, path, flags, mode, C_NULL)
handle = ccall(:uv_fs_get_result, Cssize_t, (Ptr{Cvoid},), req)
uv_fs_req_cleanup(req)
ret < 0 && uv_error("open($(repr(path)), $flags, $mode)", ret)
finally # conversion to Cstring could cause an exception
Libc.free(req)
end
return File(OS_HANDLE(@static Sys.iswindows() ? Ptr{Cvoid}(handle) : Cint(handle)))
end
isopen(f::File) = f.open
function check_open(f::File)
if !isopen(f)
throw(ArgumentError("file is closed"))
end
end
function close(f::File)
if isopen(f)
f.open = false
err = ccall(:jl_fs_close, Int32, (OS_HANDLE,), f.handle)
f.handle = INVALID_OS_HANDLE
uv_error("close", err)
end
nothing
end
closewrite(f::File) = nothing
# sendfile is the most efficient way to copy from a file descriptor
function sendfile(dst::File, src::File, src_offset::Int64, bytes::Int)
check_open(dst)
check_open(src)
while true
nsent = min(typemax(Cssize_t), bytes) # biggest allowed chunk
result = ccall(:jl_fs_sendfile, Int32, (OS_HANDLE, OS_HANDLE, Int64, Csize_t),
src.handle, dst.handle, src_offset, nsent)
uv_error("sendfile", result)
bytes -= nsent
src_offset += nsent
bytes <= 0 && break
end
nothing
end
function unsafe_write(f::File, buf::Ptr{UInt8}, len::UInt, offset::Int64=Int64(-1))
check_open(f)
err = ccall(:jl_fs_write, Int32, (OS_HANDLE, Ptr{UInt8}, Csize_t, Int64),
f.handle, buf, len, offset)
uv_error("write", err)
return len
end
write(f::File, c::UInt8) = write(f, Ref{UInt8}(c))
function truncate(f::File, n::Integer)
check_open(f)
req = Libc.malloc(_sizeof_uv_fs)
err = ccall(:uv_fs_ftruncate, Int32,
(Ptr{Cvoid}, Ptr{Cvoid}, OS_HANDLE, Int64, Ptr{Cvoid}),
C_NULL, req, f.handle, n, C_NULL)
Libc.free(req)
uv_error("ftruncate", err)
return f
end
function futime(f::File, atime::Float64, mtime::Float64)
check_open(f)
req = Libc.malloc(_sizeof_uv_fs)
err = ccall(:uv_fs_futime, Int32,
(Ptr{Cvoid}, Ptr{Cvoid}, OS_HANDLE, Float64, Float64, Ptr{Cvoid}),
C_NULL, req, f.handle, atime, mtime, C_NULL)
Libc.free(req)
uv_error("futime", err)
return f
end
function read(f::File, ::Type{UInt8})
check_open(f)
p = Ref{UInt8}()
ret = ccall(:jl_fs_read, Int32, (OS_HANDLE, Ptr{Cvoid}, Csize_t),
f.handle, p, 1)
uv_error("read", ret)
@assert ret <= sizeof(p) == 1 "unexpected read size"
ret < 1 && throw(EOFError())
return p[] % UInt8
end
function read(f::File, ::Type{Char})
b0 = read(f, UInt8)
lo = UInt8(leading_ones(b0))
c = UInt32(b0) << 24
if 0x02 ≤ lo ≤ 0x04
l = 0x08 * (0x04 - lo)
s = 16
while s ≥ l && !eof(f)
# this works around lack of peek(::File)
p = position(f)
b = read(f, UInt8)
if b & 0xc0 != 0x80
seek(f, p)
break
end
c |= UInt32(b) << s
s -= 8
end
end
return reinterpret(Char, c)
end
read(f::File, ::Type{T}) where {T<:AbstractChar} = T(read(f, Char)) # fallback
function unsafe_read(f::File, p::Ptr{UInt8}, nel::UInt)
check_open(f)
ret = ccall(:jl_fs_read, Int32, (OS_HANDLE, Ptr{Cvoid}, Csize_t),
f.handle, p, nel)
uv_error("read", ret)
ret == nel || throw(EOFError())
nothing
end
bytesavailable(f::File) = max(0, filesize(f) - position(f)) # position can be > filesize
eof(f::File) = bytesavailable(f) == 0
function readbytes!(f::File, b::MutableDenseArrayType{UInt8}, nb=length(b))
nr = min(nb, bytesavailable(f))
if length(b) < nr
resize!(b, nr)
end
ret = ccall(:jl_fs_read, Int32, (OS_HANDLE, Ptr{Cvoid}, Csize_t),
f.handle, b, nr)
uv_error("read", ret)
return ret
end
read(io::File) = read!(io, Base.StringVector(bytesavailable(io)))
readavailable(io::File) = read(io)
read(io::File, nb::Integer) = read!(io, Base.StringVector(min(nb, bytesavailable(io))))
const SEEK_SET = Int32(0)
const SEEK_CUR = Int32(1)
const SEEK_END = Int32(2)
function seek(f::File, n::Integer)
ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, n, SEEK_SET)
ret == -1 && (@static Sys.iswindows() ? windowserror : systemerror)("seek")
return f
end
function seekend(f::File)
ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, 0, SEEK_END)
ret == -1 && (@static Sys.iswindows() ? windowserror : systemerror)("seekend")
return f
end
function skip(f::File, n::Integer)
ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, n, SEEK_CUR)
ret == -1 && (@static Sys.iswindows() ? windowserror : systemerror)("skip")
return f
end
function position(f::File)
check_open(f)
ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, 0, SEEK_CUR)
ret == -1 && (@static Sys.iswindows() ? windowserror : systemerror)("lseek")
return ret
end
fd(f::File) = f.handle
stat(f::File) = stat(f.handle)
function touch(f::File)
@static if Sys.isunix()
ret = ccall(:futimes, Cint, (Cint, Ptr{Cvoid}), fd(f), C_NULL)
systemerror(:futimes, ret != 0)
else
t = time()
futime(f, t, t)
end
f
end
"""
isexecutable(path::String)
Return `true` if the given `path` has executable permissions.
!!! note
This permission may change before the user executes `path`,
so it is recommended to execute the file and handle the error if that fails,
rather than calling `isexecutable` first.
!!! note
Prior to Julia 1.6, this did not correctly interrogate filesystem
ACLs on Windows, therefore it would return `true` for any
file. From Julia 1.6 on, it correctly determines whether the
file is marked as executable or not.
See also [`ispath`](@ref), [`isreadable`](@ref), [`iswritable`](@ref).
"""
function isexecutable(path::String)
# We use `access()` and `X_OK` to determine if a given path is
# executable by the current user. `X_OK` comes from `unistd.h`.
X_OK = 0x01
return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, X_OK) == 0
end
isexecutable(path::AbstractString) = isexecutable(String(path)::String)
"""
isreadable(path::String)
Return `true` if the access permissions for the given `path` permitted reading by the current user.
!!! note
This permission may change before the user calls `open`,
so it is recommended to just call `open` alone and handle the error if that fails,
rather than calling `isreadable` first.
!!! note
Currently this function does not correctly interrogate filesystem
ACLs on Windows, therefore it can return wrong results.
!!! compat "Julia 1.11"
This function requires at least Julia 1.11.
See also [`ispath`](@ref), [`isexecutable`](@ref), [`iswritable`](@ref).
"""
function isreadable(path::String)
# We use `access()` and `R_OK` to determine if a given path is
# readable by the current user. `R_OK` comes from `unistd.h`.
R_OK = 0x04
return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, R_OK) == 0
end
isreadable(path::AbstractString) = isreadable(String(path)::String)
"""
iswritable(path::String)
Return `true` if the access permissions for the given `path` permitted writing by the current user.
!!! note
This permission may change before the user calls `open`,
so it is recommended to just call `open` alone and handle the error if that fails,
rather than calling `iswritable` first.
!!! note
Currently this function does not correctly interrogate filesystem
ACLs on Windows, therefore it can return wrong results.
!!! compat "Julia 1.11"
This function requires at least Julia 1.11.
See also [`ispath`](@ref), [`isexecutable`](@ref), [`isreadable`](@ref).
"""
function iswritable(path::String)
# We use `access()` and `W_OK` to determine if a given path is
# writable by the current user. `W_OK` comes from `unistd.h`.
W_OK = 0x02
return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, W_OK) == 0
end
iswritable(path::AbstractString) = iswritable(String(path)::String)