From e45c52d01f8e0abffebbfb4a0ac8a72b8f0f62ba Mon Sep 17 00:00:00 2001 From: V1K1NGbg Date: Sat, 3 Aug 2024 16:25:31 +0300 Subject: tar --- src/luarocks/fs.d.tl | 2 + src/luarocks/tools/tar-original.lua | 191 ++++++++++++++++++++++++++++++++++++ src/luarocks/tools/tar.lua | 109 +++++++++++--------- src/luarocks/tools/tar.tl | 59 +++++++---- src/luarocks/tools/zip.tl | 3 +- 5 files changed, 298 insertions(+), 66 deletions(-) create mode 100644 src/luarocks/tools/tar-original.lua (limited to 'src') diff --git a/src/luarocks/fs.d.tl b/src/luarocks/fs.d.tl index a2218cd6..eec2b6a2 100644 --- a/src/luarocks/fs.d.tl +++ b/src/luarocks/fs.d.tl @@ -29,6 +29,8 @@ local record fs set_permissions: function(string, string, string) -- patch absolute_name: function(string, ?string): string + -- tar + set_time: function(string, number) end return fs diff --git a/src/luarocks/tools/tar-original.lua b/src/luarocks/tools/tar-original.lua new file mode 100644 index 00000000..bac7b2a9 --- /dev/null +++ b/src/luarocks/tools/tar-original.lua @@ -0,0 +1,191 @@ + +--- A pure-Lua implementation of untar (unpacking .tar archives) +local tar = {} + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local fun = require("luarocks.fun") + +local blocksize = 512 + +local function get_typeflag(flag) + if flag == "0" or flag == "\0" then return "file" + elseif flag == "1" then return "link" + elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU + elseif flag == "3" then return "character" + elseif flag == "4" then return "block" + elseif flag == "5" then return "directory" + elseif flag == "6" then return "fifo" + elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU + elseif flag == "x" then return "next file" + elseif flag == "g" then return "global extended header" + elseif flag == "L" then return "long name" + elseif flag == "K" then return "long link name" + end + return "unknown" +end + +local function octal_to_number(octal) + local exp = 0 + local number = 0 + octal = octal:gsub("%s", "") + for i = #octal,1,-1 do + local digit = tonumber(octal:sub(i,i)) + if not digit then + break + end + number = number + (digit * 8^exp) + exp = exp + 1 + end + return number +end + +local function checksum_header(block) + local sum = 256 + + if block:byte(1) == 0 then + return 0 + end + + for i = 1,148 do + local b = block:byte(i) or 0 + sum = sum + b + end + for i = 157,500 do + local b = block:byte(i) or 0 + sum = sum + b + end + + return sum +end + +local function nullterm(s) + return s:match("^[^%z]*") +end + +local function read_header_block(block) + local header = {} + header.name = nullterm(block:sub(1,100)) + header.mode = nullterm(block:sub(101,108)):gsub(" ", "") + header.uid = octal_to_number(nullterm(block:sub(109,116))) + header.gid = octal_to_number(nullterm(block:sub(117,124))) + header.size = octal_to_number(nullterm(block:sub(125,136))) + header.mtime = octal_to_number(nullterm(block:sub(137,148))) + header.chksum = octal_to_number(nullterm(block:sub(149,156))) + header.typeflag = get_typeflag(block:sub(157,157)) + header.linkname = nullterm(block:sub(158,257)) + header.magic = block:sub(258,263) + header.version = block:sub(264,265) + header.uname = nullterm(block:sub(266,297)) + header.gname = nullterm(block:sub(298,329)) + header.devmajor = octal_to_number(nullterm(block:sub(330,337))) + header.devminor = octal_to_number(nullterm(block:sub(338,345))) + header.prefix = block:sub(346,500) + + -- if header.magic ~= "ustar " and header.magic ~= "ustar\0" then + -- return false, ("Invalid header magic %6x"):format(bestring_to_number(header.magic)) + -- end + -- if header.version ~= "00" and header.version ~= " \0" then + -- return false, "Unknown version "..header.version + -- end + if header.typeflag == "unknown" then + if checksum_header(block) ~= header.chksum then + return false, "Failed header checksum" + end + end + return header +end + +function tar.untar(filename, destdir) + assert(type(filename) == "string") + assert(type(destdir) == "string") + + local tar_handle = io.open(filename, "rb") + if not tar_handle then return nil, "Error opening file "..filename end + + local long_name, long_link_name + local ok, err + local make_dir = fun.memoize(fs.make_dir) + while true do + local block + repeat + block = tar_handle:read(blocksize) + until (not block) or block:byte(1) > 0 + if not block then break end + if #block < blocksize then + ok, err = nil, "Invalid block size -- corrupted file?" + break + end + + local header + header, err = read_header_block(block) + if not header then + ok = false + break + end + + local file_data = "" + if header.size > 0 then + local nread = math.ceil(header.size / blocksize) * blocksize + file_data = tar_handle:read(header.size) + if nread > header.size then + tar_handle:seek("cur", nread - header.size) + end + end + + if header.typeflag == "long name" then + long_name = nullterm(file_data) + elseif header.typeflag == "long link name" then + long_link_name = nullterm(file_data) + else + if long_name then + header.name = long_name + long_name = nil + end + if long_link_name then + header.name = long_link_name + long_link_name = nil + end + end + local pathname = dir.path(destdir, header.name) + pathname = fs.absolute_name(pathname) + if header.typeflag == "directory" then + ok, err = make_dir(pathname) + if not ok then + break + end + elseif header.typeflag == "file" then + local dirname = dir.dir_name(pathname) + if dirname ~= "" then + ok, err = make_dir(dirname) + if not ok then + break + end + end + local file_handle + file_handle, err = io.open(pathname, "wb") + if not file_handle then + ok = nil + break + end + file_handle:write(file_data) + file_handle:close() + fs.set_time(pathname, header.mtime) + if header.mode:match("[75]") then + fs.set_permissions(pathname, "exec", "all") + else + fs.set_permissions(pathname, "read", "all") + end + end + --[[ + for k,v in pairs(header) do + util.printout("[\""..tostring(k).."\"] = "..(type(v)=="number" and v or "\""..v:gsub("%z", "\\0").."\"")) + end + util.printout() + --]] + end + tar_handle:close() + return ok, err +end + +return tar diff --git a/src/luarocks/tools/tar.lua b/src/luarocks/tools/tar.lua index bac7b2a9..e95b85d0 100644 --- a/src/luarocks/tools/tar.lua +++ b/src/luarocks/tools/tar.lua @@ -1,22 +1,43 @@ +local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local math = _tl_compat and _tl_compat.math or math; local string = _tl_compat and _tl_compat.string or string + +local tar = {Header = {}, } + + + + + + + + + + + + + + + + + + ---- A pure-Lua implementation of untar (unpacking .tar archives) -local tar = {} local fs = require("luarocks.fs") local dir = require("luarocks.dir") local fun = require("luarocks.fun") + + local blocksize = 512 local function get_typeflag(flag) if flag == "0" or flag == "\0" then return "file" elseif flag == "1" then return "link" - elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU + elseif flag == "2" then return "symlink" elseif flag == "3" then return "character" elseif flag == "4" then return "block" elseif flag == "5" then return "directory" elseif flag == "6" then return "fifo" - elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU + elseif flag == "7" then return "contiguous" elseif flag == "x" then return "next file" elseif flag == "g" then return "global extended header" elseif flag == "L" then return "long name" @@ -29,12 +50,12 @@ local function octal_to_number(octal) local exp = 0 local number = 0 octal = octal:gsub("%s", "") - for i = #octal,1,-1 do - local digit = tonumber(octal:sub(i,i)) + for i = #octal, 1, -1 do + local digit = tonumber(octal:sub(i, i)) if not digit then break end - number = number + (digit * 8^exp) + number = number + (digit * 8 ^ exp) exp = exp + 1 end return number @@ -47,11 +68,11 @@ local function checksum_header(block) return 0 end - for i = 1,148 do + for i = 1, 148 do local b = block:byte(i) or 0 sum = sum + b end - for i = 157,500 do + for i = 157, 500 do local b = block:byte(i) or 0 sum = sum + b end @@ -65,29 +86,29 @@ end local function read_header_block(block) local header = {} - header.name = nullterm(block:sub(1,100)) - header.mode = nullterm(block:sub(101,108)):gsub(" ", "") - header.uid = octal_to_number(nullterm(block:sub(109,116))) - header.gid = octal_to_number(nullterm(block:sub(117,124))) - header.size = octal_to_number(nullterm(block:sub(125,136))) - header.mtime = octal_to_number(nullterm(block:sub(137,148))) - header.chksum = octal_to_number(nullterm(block:sub(149,156))) - header.typeflag = get_typeflag(block:sub(157,157)) - header.linkname = nullterm(block:sub(158,257)) - header.magic = block:sub(258,263) - header.version = block:sub(264,265) - header.uname = nullterm(block:sub(266,297)) - header.gname = nullterm(block:sub(298,329)) - header.devmajor = octal_to_number(nullterm(block:sub(330,337))) - header.devminor = octal_to_number(nullterm(block:sub(338,345))) - header.prefix = block:sub(346,500) - - -- if header.magic ~= "ustar " and header.magic ~= "ustar\0" then - -- return false, ("Invalid header magic %6x"):format(bestring_to_number(header.magic)) - -- end - -- if header.version ~= "00" and header.version ~= " \0" then - -- return false, "Unknown version "..header.version - -- end + header.name = nullterm(block:sub(1, 100)) + header.mode = nullterm(block:sub(101, 108)):gsub(" ", "") + header.uid = octal_to_number(nullterm(block:sub(109, 116))) + header.gid = octal_to_number(nullterm(block:sub(117, 124))) + header.size = octal_to_number(nullterm(block:sub(125, 136))) + header.mtime = octal_to_number(nullterm(block:sub(137, 148))) + header.chksum = octal_to_number(nullterm(block:sub(149, 156))) + header.typeflag = get_typeflag(block:sub(157, 157)) + header.linkname = nullterm(block:sub(158, 257)) + header.magic = block:sub(258, 263) + header.version = block:sub(264, 265) + header.uname = nullterm(block:sub(266, 297)) + header.gname = nullterm(block:sub(298, 329)) + header.devmajor = octal_to_number(nullterm(block:sub(330, 337))) + header.devminor = octal_to_number(nullterm(block:sub(338, 345))) + header.prefix = block:sub(346, 500) + + + + + + + if header.typeflag == "unknown" then if checksum_header(block) ~= header.chksum then return false, "Failed header checksum" @@ -97,11 +118,9 @@ local function read_header_block(block) end function tar.untar(filename, destdir) - assert(type(filename) == "string") - assert(type(destdir) == "string") local tar_handle = io.open(filename, "rb") - if not tar_handle then return nil, "Error opening file "..filename end + if not tar_handle then return nil, "Error opening file " .. filename end local long_name, long_link_name local ok, err @@ -117,13 +136,13 @@ function tar.untar(filename, destdir) break end - local header - header, err = read_header_block(block) - if not header then + local headerp + headerp, err = read_header_block(block) + if not headerp then ok = false break end - + local header = headerp local file_data = "" if header.size > 0 then local nread = math.ceil(header.size / blocksize) * blocksize @@ -177,12 +196,12 @@ function tar.untar(filename, destdir) fs.set_permissions(pathname, "read", "all") end end - --[[ - for k,v in pairs(header) do - util.printout("[\""..tostring(k).."\"] = "..(type(v)=="number" and v or "\""..v:gsub("%z", "\\0").."\"")) - end - util.printout() - --]] + + + + + + end tar_handle:close() return ok, err diff --git a/src/luarocks/tools/tar.tl b/src/luarocks/tools/tar.tl index bac7b2a9..9a7ce8e6 100644 --- a/src/luarocks/tools/tar.tl +++ b/src/luarocks/tools/tar.tl @@ -1,14 +1,35 @@ --- A pure-Lua implementation of untar (unpacking .tar archives) -local tar = {} +local record tar + record Header + name: string + mode: string + uid: number + gid: number + size: number + mtime: number + chksum: number + typeflag: string + linkname: string + magic: string + version: string + uname: string + gname: string + devmajor: number + devminor: number + prefix: string + end +end local fs = require("luarocks.fs") local dir = require("luarocks.dir") local fun = require("luarocks.fun") +local type Header = tar.Header + local blocksize = 512 -local function get_typeflag(flag) +local function get_typeflag(flag: string): string if flag == "0" or flag == "\0" then return "file" elseif flag == "1" then return "link" elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU @@ -25,9 +46,9 @@ local function get_typeflag(flag) return "unknown" end -local function octal_to_number(octal) +local function octal_to_number(octal: string): number local exp = 0 - local number = 0 + local number: number = 0 octal = octal:gsub("%s", "") for i = #octal,1,-1 do local digit = tonumber(octal:sub(i,i)) @@ -40,7 +61,7 @@ local function octal_to_number(octal) return number end -local function checksum_header(block) +local function checksum_header(block: string): number local sum = 256 if block:byte(1) == 0 then @@ -59,12 +80,12 @@ local function checksum_header(block) return sum end -local function nullterm(s) +local function nullterm(s: string): string return s:match("^[^%z]*") end -local function read_header_block(block) - local header = {} +local function read_header_block(block: string): boolean | Header, string + local header: Header = {} header.name = nullterm(block:sub(1,100)) header.mode = nullterm(block:sub(101,108)):gsub(" ", "") header.uid = octal_to_number(nullterm(block:sub(109,116))) @@ -96,18 +117,16 @@ local function read_header_block(block) return header end -function tar.untar(filename, destdir) - assert(type(filename) == "string") - assert(type(destdir) == "string") +function tar.untar(filename: string, destdir: string): boolean, string local tar_handle = io.open(filename, "rb") if not tar_handle then return nil, "Error opening file "..filename end - local long_name, long_link_name - local ok, err + local long_name, long_link_name: string, string + local ok, err: boolean, string local make_dir = fun.memoize(fs.make_dir) while true do - local block + local block: string repeat block = tar_handle:read(blocksize) until (not block) or block:byte(1) > 0 @@ -117,19 +136,19 @@ function tar.untar(filename, destdir) break end - local header - header, err = read_header_block(block) - if not header then + local headerp: boolean | Header + headerp, err = read_header_block(block) + if not headerp then ok = false break end - + local header: Header = headerp as Header --! cast local file_data = "" if header.size > 0 then local nread = math.ceil(header.size / blocksize) * blocksize file_data = tar_handle:read(header.size) if nread > header.size then - tar_handle:seek("cur", nread - header.size) + tar_handle:seek("cur", nread - header.size as integer) --! cast to integer end end @@ -162,7 +181,7 @@ function tar.untar(filename, destdir) break end end - local file_handle + local file_handle: FILE file_handle, err = io.open(pathname, "wb") if not file_handle then ok = nil diff --git a/src/luarocks/tools/zip.tl b/src/luarocks/tools/zip.tl index 82d582fa..849a8180 100644 --- a/src/luarocks/tools/zip.tl +++ b/src/luarocks/tools/zip.tl @@ -1,7 +1,8 @@ --- A Lua implementation of .zip and .gz file compression and decompression, -- using only lzlib or lua-lzib. -local zip = {} +local record zip +end local zlib = require("zlib") local fs = require("luarocks.fs") -- cgit v1.2.3-55-g6feb