Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
luarocks/src/luarocks/tools/tar.lua
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
174 lines (160 sloc)
5.29 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- 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 | |
| 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 checksum_header(block) ~= header.chksum then | |
| return false, "Failed header checksum" | |
| 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 checksum_header(block) > 256 | |
| 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 = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size) | |
| 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 |