Streaming I/O and support of named pipes (#570)
* added streaming i/o for scripting, and a byteutil.get_size function * added i/o stream class, also added named pipes support on lua side via ffi * added constant file.named_pipes_prefix * added buffered and yield modes for io_stream * added new time function for work with UTC - utc_time, utc_offset, local_time * docs updated * constant pid moved to os.pid * now gmtime_s and localtime_s used only in windows
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
local io_stream = require "core:io_stream"
|
||||
|
||||
local lib = {
|
||||
read = file.__read_descriptor,
|
||||
write = file.__write_descriptor,
|
||||
flush = file.__flush_descriptor,
|
||||
is_alive = file.__has_descriptor,
|
||||
close = file.__close_descriptor
|
||||
}
|
||||
|
||||
return function(path, mode)
|
||||
return io_stream.new(
|
||||
file.__open_descriptor(path, mode),
|
||||
mode:find('b') ~= nil,
|
||||
lib
|
||||
)
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
local FFI = ffi
|
||||
|
||||
if FFI.os == "Windows" then
|
||||
return require "core:internal/stream_providers/named_pipe_windows"
|
||||
else
|
||||
return require "core:internal/stream_providers/named_pipe_unix"
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
local forbiddenPaths = {
|
||||
"/..\\", "\\../",
|
||||
"/../", "\\..\\"
|
||||
}
|
||||
|
||||
return function(path)
|
||||
local corrected = true
|
||||
|
||||
if path:starts_with("../") or path:starts_with("..\\") then
|
||||
corrected = false
|
||||
else
|
||||
for _, forbiddenPath in ipairs(forbiddenPaths) do
|
||||
if path:find(forbiddenPath) then
|
||||
corrected = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not corrected then error "special path \"../\" is not allowed in path to named pipe" end
|
||||
end
|
||||
@@ -0,0 +1,104 @@
|
||||
local path_validate = require "core:internal/stream_providers/named_pipe_path_validate"
|
||||
local io_stream = require "core:io_stream"
|
||||
|
||||
local FFI = ffi
|
||||
|
||||
FFI.cdef[[
|
||||
int open(const char *pathname, int flags);
|
||||
int close(int fd);
|
||||
ssize_t read(int fd, void *buf, size_t count);
|
||||
ssize_t write(int fd, const void *buf, size_t count);
|
||||
int fcntl(int fd, int cmd, ...);
|
||||
|
||||
const char *strerror(int errnum);
|
||||
]]
|
||||
|
||||
local C = FFI.C
|
||||
|
||||
local O_RDONLY = 0x0
|
||||
local O_WRONLY = 0x1
|
||||
local O_RDWR = 0x2
|
||||
local O_NONBLOCK = 0x800
|
||||
local F_GETFL = 3
|
||||
|
||||
local function getError()
|
||||
local err = ffi.errno()
|
||||
|
||||
return ffi.string(C.strerror(err)).." ("..err..")"
|
||||
end
|
||||
|
||||
local lib = {}
|
||||
|
||||
function lib.read(fd, len)
|
||||
local buffer = FFI.new("uint8_t[?]", len)
|
||||
local result = C.read(fd, buffer, len)
|
||||
|
||||
local out = Bytearray()
|
||||
|
||||
if result <= 0 then
|
||||
return out
|
||||
end
|
||||
|
||||
for i = 0, result - 1 do
|
||||
out[i+1] = buffer[i]
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function lib.write(fd, bytearray)
|
||||
local len = #bytearray
|
||||
local buffer = FFI.new("uint8_t[?]", len)
|
||||
for i = 1, len do
|
||||
buffer[i-1] = bytearray[i]
|
||||
end
|
||||
|
||||
if C.write(fd, buffer, len) == -1 then
|
||||
error("failed to write to named pipe: "..getError())
|
||||
end
|
||||
end
|
||||
|
||||
function lib.flush(fd)
|
||||
-- no flush on unix
|
||||
end
|
||||
|
||||
function lib.is_alive(fd)
|
||||
if fd == nil or fd < 0 then return false end
|
||||
|
||||
return C.fcntl(fd, F_GETFL) ~= -1
|
||||
end
|
||||
|
||||
function lib.close(fd)
|
||||
C.close(fd)
|
||||
end
|
||||
|
||||
return function(path, mode)
|
||||
path_validate(path)
|
||||
|
||||
path = "/tmp/"..path
|
||||
|
||||
local read = mode:find('r') ~= nil
|
||||
local write = mode:find('w') ~= nil
|
||||
|
||||
local flags
|
||||
|
||||
if read and write then
|
||||
flags = O_RDWR
|
||||
elseif read then
|
||||
flags = O_RDONLY
|
||||
elseif write then
|
||||
flags = O_WRONLY
|
||||
else
|
||||
error "mode must contain read or write flag"
|
||||
end
|
||||
|
||||
flags = bit.bor(flags, O_NONBLOCK)
|
||||
|
||||
local fd = C.open(path, flags)
|
||||
|
||||
if fd == -1 then
|
||||
error("failed to open named pipe: "..getError())
|
||||
end
|
||||
|
||||
return io_stream.new(fd, mode:find('b') ~= nil, lib)
|
||||
end
|
||||
@@ -0,0 +1,144 @@
|
||||
local path_validate = require "core:internal/stream_providers/named_pipe_path_validate"
|
||||
local io_stream = require "core:io_stream"
|
||||
|
||||
local FFI = ffi
|
||||
|
||||
FFI.cdef[[
|
||||
typedef void* HANDLE;
|
||||
typedef uint32_t DWORD;
|
||||
typedef int BOOL;
|
||||
typedef void* LPVOID;
|
||||
typedef const char* LPCSTR;
|
||||
|
||||
BOOL CloseHandle(HANDLE hObject);
|
||||
DWORD GetFileType(HANDLE hFile);
|
||||
BOOL ReadFile(HANDLE hFile, void* lpBuffer, DWORD nNumberOfBytesToRead,
|
||||
DWORD* lpNumberOfBytesRead, void* lpOverlapped);
|
||||
BOOL WriteFile(HANDLE hFile, const void* lpBuffer, DWORD nNumberOfBytesToWrite,
|
||||
DWORD* lpNumberOfBytesWritten, void* lpOverlapped);
|
||||
HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
void* lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
|
||||
|
||||
BOOL PeekNamedPipe(
|
||||
HANDLE hNamedPipe,
|
||||
LPVOID lpBuffer,
|
||||
DWORD nBufferSize,
|
||||
DWORD* lpBytesRead,
|
||||
DWORD* lpTotalBytesAvail,
|
||||
DWORD* lpBytesLeftThisMessage
|
||||
);
|
||||
|
||||
DWORD GetLastError(void);
|
||||
BOOL FlushFileBuffers(HANDLE hFile);
|
||||
]]
|
||||
|
||||
local C = FFI.C
|
||||
|
||||
local GENERIC_READ = 0x80000000
|
||||
local GENERIC_WRITE = 0x40000000
|
||||
local OPEN_EXISTING = 3
|
||||
local FILE_ATTRIBUTE_NORMAL = 0x00000080
|
||||
local FILE_TYPE_UNKNOWN = 0x0000
|
||||
local INVALID_HANDLE_VALUE = FFI.cast("HANDLE", -1)
|
||||
|
||||
local lib = {}
|
||||
|
||||
local function is_data_available(handle)
|
||||
local bytes_available = FFI.new("DWORD[1]")
|
||||
local success = FFI.C.PeekNamedPipe(handle, nil, 0, nil, bytes_available, nil)
|
||||
|
||||
if success == 0 then
|
||||
return -1
|
||||
end
|
||||
|
||||
return bytes_available[0] > 0
|
||||
end
|
||||
|
||||
function lib.read(handle, len)
|
||||
local out = Bytearray()
|
||||
|
||||
local has_data, err = is_data_available(handle)
|
||||
|
||||
if not has_data then
|
||||
return out
|
||||
elseif hasData == -1 then
|
||||
error("failed to read from named pipe: "..tostring(C.GetLastError()))
|
||||
end
|
||||
|
||||
local buffer = FFI.new("uint8_t[?]", len)
|
||||
local read = FFI.new("DWORD[1]")
|
||||
|
||||
local ok = C.ReadFile(handle, buffer, len, read, nil)
|
||||
|
||||
if ok == 0 or read[0] == 0 then
|
||||
return out
|
||||
end
|
||||
|
||||
for i = 0, read[0] - 1 do
|
||||
out[i+1] = buffer[i]
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function lib.write(handle, bytearray)
|
||||
local len = #bytearray
|
||||
|
||||
local buffer = FFI.new("uint8_t[?]", len)
|
||||
for i = 1, len do
|
||||
buffer[i-1] = bytearray[i]
|
||||
end
|
||||
|
||||
local written = FFI.new("DWORD[1]")
|
||||
|
||||
if C.WriteFile(handle, buffer, len, written, nil) == 0 then
|
||||
error("failed to write to named pipe: "..tostring(C.GetLastError()))
|
||||
end
|
||||
end
|
||||
|
||||
function lib.flush(handle)
|
||||
C.FlushFileBuffers(handle)
|
||||
end
|
||||
|
||||
function lib.is_alive(handle)
|
||||
if handle == nil or handle == INVALID_HANDLE_VALUE then
|
||||
return false
|
||||
else
|
||||
return C.GetFileType(handle) ~= FILE_TYPE_UNKNOWN
|
||||
end
|
||||
end
|
||||
|
||||
function lib.close(handle)
|
||||
C.CloseHandle(handle)
|
||||
end
|
||||
|
||||
return function(path, mode)
|
||||
path_validate(path)
|
||||
|
||||
path = "\\\\.\\pipe\\"..path
|
||||
|
||||
local read = mode:find('r') ~= nil
|
||||
local write = mode:find('w') ~= nil
|
||||
|
||||
local flags
|
||||
|
||||
if read and write then
|
||||
flags = bit.bor(GENERIC_READ, GENERIC_WRITE)
|
||||
elseif read then
|
||||
flags = GENERIC_READ
|
||||
elseif write then
|
||||
flags = GENERIC_WRITE
|
||||
else
|
||||
error("mode must contain read or write flag")
|
||||
end
|
||||
|
||||
local handle = C.CreateFileA(path, flags, 0, nil, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, nil)
|
||||
|
||||
if handle == INVALID_HANDLE_VALUE then
|
||||
error("failed to open named pipe: "..tostring(C.GetLastError()))
|
||||
end
|
||||
|
||||
return io_stream.new(handle, mode:find('b') ~= nil, lib)
|
||||
end
|
||||
Reference in New Issue
Block a user