This repository has been archived by the owner on Nov 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
luaTar.lua
160 lines (157 loc) · 4.96 KB
/
luaTar.lua
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
--[[
ABJ4403's LuaTar
(C) 2022-2024 ABJ4403
Licensed under GNU GPL v3 License
]]
local tar = {
VERSION = 4
}
function tar.add(tarPath,fileName,content)
local tarFile,err = io.open(tarPath,"ab")
if not tarFile then return err or false end
content = content or ""
local isFolder = content == "" and fileName:sub(#fileName,#fileName) == "/"
-- note: %07o = octal
local header =
fileName..("\0"):rep(100 - #fileName).. -- Name
'0000666\0'.. -- Mode
("%07o"):format(0)..'\0'.. -- UID
("%07o"):format(0)..'\0'.. -- GID
("%011o"):format(#content)..'\0'.. -- Size
("%011o"):format(0)..'\0'.. -- Last modified
("\0"):rep(7)..' '.. -- Checksum
(isFolder and "5" or "0").. -- Type (0/1/2:file/hardlink/symlink)
("\0"):rep(100).. -- linked file name (we dont want those symlinks and stuf so aint using one
("\0"):rep(88).. -- UnixStandardTar header (6b) + version (2b), user + group (64b), device major + minor number (16b)
("\0"):rep(167) -- UStar Filename prefix
-- Checksum
local sum = 224
for i=1,512 do
sum = sum + header:byte(i)
end
header =
header:sub(1,148)..
("%06o"):format(sum)..
header:sub(155)
tarFile:write(
header..
content..
("\0"):rep((512 - #content) % 512) -- Tar file has 512 bytes block size
)
tarFile:close()
return true
end
function tar.extract(tarPath,fileName)
local file = io.open(tarPath,"rb")
if not file then return end
while true do
local block = file:read(512) -- read every block
if not block then break end
local fileSize = tonumber(block:sub(125,135),8) -- get the file size
if not fileSize then print("Found a block with likely empty file size bytes, break.")break end
if block:sub(1,math.min(100,#fileName)) == fileName then -- if a block starts with filename
local data = file:read(fileSize) -- read the whole thing
file:close()
return data
end
local skipBlk = math.ceil((fileSize-.00000000000000001) / 512) -- extra zeroes to fix Lua floating point integer bug
file:seek("cur",skipBlk * 512)
end
file:close()
end
function tar.remove(tarPath,fileName)
local file = io.open(tarPath,"rb")
if not file then return end
local removedFiles = 0
local output = io.open(tarPath..".tmp","wb")
while true do
local block = file:read(512)
if not block then break end
if block:sub(1,math.min(100,#fileName)) == fileName then -- ignores the block with the file that is going to be removed
local fileSize = tonumber(block:sub(125,135),8)
if not fileSize then print("Found a block with likely empty file size bytes, break.")break end
local skipBlk = math.ceil((fileSize-.00000000000000001) / 512) -- extra zeroes to fix Lua floating point integer bug
file:seek("cur",skipBlk * 512)
removedFiles = removedFiles + 1
else
output:write(block) -- write the rest of the block to temporary file
end
end
file:close()
output:close()
os.remove(tarPath)
os.rename(tarPath..".tmp",tarPath)
return removedFiles
end
function tar.parseHeader(tarPath)
local ret = {}
local file = io.open(tarPath,"rb")
if not file then return end
while true do
--read
local block = file:read(512)
if not block then break end
local fileName = block:sub(1,100):gsub("\0+$","")
if fileName == "" then print("Found a block with empty filename, likely empty header, break.")break end
--checksum verify
local sum = 224
local checksum = tonumber(block:sub(149,154),8)
block = block:sub(1,148).."\0\0\0\0\0\0"..block:sub(155) -- original was bunch of NULs
for i=1,#block do
sum = sum + block:byte(i)
end
local checksumValid =
checksum and
sum - checksum == 0
--result
local header = {
isValid = checksumValid,
--headerBytes = block,
mode = block:sub(101,107),
uid = tonumber(block:sub(109,115),8),
gid = tonumber(block:sub(117,123),8),
size = tonumber(block:sub(125,135),8),
lastModified = tonumber(block:sub(137,147),8),
["checksum"] = checksum,
["type"] = tonumber(block:sub(156,156)),
linkedName = block:sub(157,257),
ustar = {
--headerBytes = block:sub(158,501),
ustarHeader = block:sub(258,263),
version = block:sub(264,265),
user = block:sub(266,297),
group = block:sub(298,329),
devMajor = block:sub(330,337),
devMinor = block:sub(338,345),
filenamePrefix = block:sub(346,501),
}
}
ret[fileName] = header
--seek block
if not header.size then print("FILE SIZE BUG DETECTED! CANNOT CONTINUE!!")break end
local skipBlk =
math.ceil((header.size-.00000000000000001) / 512) -- extra zeroes to fix Lua floating point integer bug
file:seek("cur",skipBlk * 512)
end
file:close()
return ret
end
function tar.queryFile(tarPath)
local fileList = {}
for k in pairs(tar.parseHeader(tarPath)) do
if k:sub(#k,#k) ~= "/" then
table.insert(fileList,k)
end
end
return fileList
end
function tar.queryFolder(tarPath)
local fileList = {}
for k in pairs(tar.parseHeader(tarPath)) do
if k:sub(#k,#k) == "/" then
table.insert(fileList,k)
end
end
return fileList
end
return tar