PageRenderTime 30ms CodeModel.GetById 22ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

/src/luarocks/tools/tar.lua

http://github.com/keplerproject/luarocks
Lua | 174 lines | 147 code | 14 blank | 13 comment | 40 complexity | 9df2f1f9c35332dead8b6fb5e67db0dd MD5 | raw file
  1
  2--- A pure-Lua implementation of untar (unpacking .tar archives)
  3local tar = {}
  4
  5local fs = require("luarocks.fs")
  6local dir = require("luarocks.dir")
  7local fun = require("luarocks.fun")
  8
  9local blocksize = 512
 10
 11local function get_typeflag(flag)
 12   if flag == "0" or flag == "\0" then return "file"
 13   elseif flag == "1" then return "link"
 14   elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU
 15   elseif flag == "3" then return "character"
 16   elseif flag == "4" then return "block"
 17   elseif flag == "5" then return "directory"
 18   elseif flag == "6" then return "fifo"
 19   elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU
 20   elseif flag == "x" then return "next file"
 21   elseif flag == "g" then return "global extended header"
 22   elseif flag == "L" then return "long name"
 23   elseif flag == "K" then return "long link name"
 24   end
 25   return "unknown"
 26end
 27
 28local function octal_to_number(octal)
 29   local exp = 0
 30   local number = 0
 31   octal = octal:gsub("%s", "")
 32   for i = #octal,1,-1 do
 33      local digit = tonumber(octal:sub(i,i)) 
 34      if not digit then
 35         break
 36      end
 37      number = number + (digit * 8^exp)
 38      exp = exp + 1
 39   end
 40   return number
 41end
 42
 43local function checksum_header(block)
 44   local sum = 256
 45   for i = 1,148 do
 46      local b = block:byte(i) or 0
 47      sum = sum + b
 48   end
 49   for i = 157,500 do
 50      local b = block:byte(i) or 0
 51      sum = sum + b
 52   end
 53   return sum
 54end
 55
 56local function nullterm(s)
 57   return s:match("^[^%z]*")
 58end
 59
 60local function read_header_block(block)
 61   local header = {}
 62   header.name = nullterm(block:sub(1,100))
 63   header.mode = nullterm(block:sub(101,108)):gsub(" ", "")
 64   header.uid = octal_to_number(nullterm(block:sub(109,116)))
 65   header.gid = octal_to_number(nullterm(block:sub(117,124)))
 66   header.size = octal_to_number(nullterm(block:sub(125,136)))
 67   header.mtime = octal_to_number(nullterm(block:sub(137,148)))
 68   header.chksum = octal_to_number(nullterm(block:sub(149,156)))
 69   header.typeflag = get_typeflag(block:sub(157,157))
 70   header.linkname = nullterm(block:sub(158,257))
 71   header.magic = block:sub(258,263)
 72   header.version = block:sub(264,265)
 73   header.uname = nullterm(block:sub(266,297))
 74   header.gname = nullterm(block:sub(298,329))
 75   header.devmajor = octal_to_number(nullterm(block:sub(330,337)))
 76   header.devminor = octal_to_number(nullterm(block:sub(338,345)))
 77   header.prefix = block:sub(346,500)
 78   -- if header.magic ~= "ustar " and header.magic ~= "ustar\0" then
 79   --    return false, ("Invalid header magic %6x"):format(bestring_to_number(header.magic))
 80   -- end
 81   -- if header.version ~= "00" and header.version ~= " \0" then
 82   --    return false, "Unknown version "..header.version
 83   -- end
 84   if not checksum_header(block) == header.chksum then
 85      return false, "Failed header checksum"
 86   end
 87   return header
 88end
 89
 90function tar.untar(filename, destdir)
 91   assert(type(filename) == "string")
 92   assert(type(destdir) == "string")
 93
 94   local tar_handle = io.open(filename, "rb")
 95   if not tar_handle then return nil, "Error opening file "..filename end
 96   
 97   local long_name, long_link_name
 98   local ok, err
 99   local make_dir = fun.memoize(fs.make_dir)
100   while true do
101      local block
102      repeat
103         block = tar_handle:read(blocksize)
104      until (not block) or checksum_header(block) > 256
105      if not block then break end
106      if #block < blocksize then
107         ok, err = nil, "Invalid block size -- corrupted file?"
108         break
109      end
110      local header
111      header, err = read_header_block(block)
112      if not header then
113         ok = false
114         break
115      end
116
117      local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size)
118
119      if header.typeflag == "long name" then
120         long_name = nullterm(file_data)
121      elseif header.typeflag == "long link name" then
122         long_link_name = nullterm(file_data)
123      else
124         if long_name then
125            header.name = long_name
126            long_name = nil
127         end
128         if long_link_name then
129            header.name = long_link_name
130            long_link_name = nil
131         end
132      end
133      local pathname = dir.path(destdir, header.name)
134      pathname = fs.absolute_name(pathname)
135      if header.typeflag == "directory" then
136         ok, err = make_dir(pathname)
137         if not ok then
138            break
139         end
140      elseif header.typeflag == "file" then
141         local dirname = dir.dir_name(pathname)
142         if dirname ~= "" then
143            ok, err = make_dir(dirname)
144            if not ok then
145               break
146            end
147         end
148         local file_handle
149         file_handle, err = io.open(pathname, "wb")
150         if not file_handle then
151            ok = nil
152            break
153         end
154         file_handle:write(file_data)
155         file_handle:close()
156         fs.set_time(pathname, header.mtime)
157         if header.mode:match("[75]") then
158            fs.set_permissions(pathname, "exec", "all")
159         else
160            fs.set_permissions(pathname, "read", "all")
161         end
162      end
163      --[[
164      for k,v in pairs(header) do
165         util.printout("[\""..tostring(k).."\"] = "..(type(v)=="number" and v or "\""..v:gsub("%z", "\\0").."\""))
166      end
167      util.printout()
168      --]]
169   end
170   tar_handle:close()
171   return ok, err
172end
173
174return tar