@@ -28,7 +28,7 @@ local tar = require "tar"
28
28
```
29
29
30
30
The `tar` module can read and write tar archives.
31
- Only files and directories are supported.
31
+ Only files, directories and symbolic links are supported.
32
32
@@@]]
33
33
34
34
-- https://fr.wikipedia.org/wiki/Tar_%28informatique%29
51
51
52
52
local file_type = F {
53
53
file = " 0" ,
54
+ link = " 2" ,
54
55
directory = " 5" ,
55
56
}
56
57
@@ -59,6 +60,7 @@ rev_file_type["\0"] = rev_file_type["0"]
59
60
60
61
local default_mode = {
61
62
file = tonumber (" 644" , 8 ),
63
+ link = tonumber (" 777" , 8 ),
62
64
directory = tonumber (" 755" , 8 ),
63
65
}
64
66
@@ -77,20 +79,22 @@ local function header(st, xform)
77
79
local name = xform (clean_path (st .name ))
78
80
if name == nil then return Discarded end
79
81
if # name > 100 then return nil , name .. " : filename too long" end
80
- if st .size >= 8 * 1024 ^ 3 then return nil , st .name .. " : file too big" end
82
+ if st .type == " file " and st . size >= 8 * 1024 ^ 3 then return nil , st .name .. " : file too big" end
81
83
local ftype = file_type [st .type ]
82
84
if not ftype then return nil , st .name .. " : wrong file type" end
85
+ if st .type == " link" and not st .link then return nil , st .name .. " : missing link name" end
86
+ if st .link and # st .link > 100 then return nil , link .. " : filename too long" end
83
87
local header1 = pack (" c100c8c8c8c12c12" ,
84
88
name ,
85
89
format (" %07o" , st .mode or default_mode [st .type ] or " 0" ),
86
90
" " ,
87
91
" " ,
88
- format (" %011o" , st .size ),
92
+ format (" %011o" , st .size or 0 ),
89
93
format (" %011o" , st .mtime )
90
94
)
91
95
local header2 = pack (" c1c100c6c2c32c32c8c8c155c12" ,
92
96
ftype ,
93
- " " ,
97
+ st . link or " " ,
94
98
" " , " " , " " , " " , " " , " " , " " , " "
95
99
)
96
100
local checksum = format (" %07o" , sum (bytes (header1 )) + sum (bytes (header2 )) + 32 * 8 )
@@ -102,7 +106,7 @@ local function end_of_archive()
102
106
end
103
107
104
108
local function parse (archive , i )
105
- local name , mode , _ , _ , size , mtime , checksum , ftype = unpack (" c100c8c8c8c12c12c8c1 " , archive , i )
109
+ local name , mode , _ , _ , size , mtime , checksum , ftype , link = unpack (" c100c8c8c8c12c12c8c1c100 " , archive , i )
106
110
if not checksum then return nil , " Corrupted archive" end
107
111
local function cut (s ) return s :match " ^[^\0 ]*" end
108
112
if sum (bytes (archive :sub (i , i + 148 - 1 ))) + sum (bytes (archive :sub (i + 156 , i + 512 - 1 ))) + 32 * 8 ~= tonumber (cut (checksum ), 8 ) then
@@ -116,6 +120,7 @@ local function parse(archive, i)
116
120
size = tonumber (cut (size ), 8 ),
117
121
mtime = tonumber (cut (mtime ), 8 ),
118
122
type = ftype ,
123
+ link = ftype == " link" and cut (link ) or nil ,
119
124
}
120
125
end
121
126
@@ -176,6 +181,20 @@ function tar.tar(files, xform)
176
181
return true
177
182
end
178
183
184
+ local function add_link (st )
185
+ local xformed_name = xform (st .name )
186
+ if xformed_name == nil then return true end
187
+ if done (xformed_name ) then return true end
188
+ local ok , err = add_dir (xformed_name :dirname (), st )
189
+ if not ok then return nil , err end
190
+ local hd
191
+ hd , err = header (st , xform )
192
+ if hd == Discarded then return true end
193
+ if not hd then return nil , err end
194
+ chunks [# chunks + 1 ] = hd
195
+ return true
196
+ end
197
+
179
198
local function add_real_dir (path )
180
199
if done (path ) then return true end
181
200
if path :dirname () == path then return true end
@@ -211,13 +230,32 @@ function tar.tar(files, xform)
211
230
return true
212
231
end
213
232
233
+ local function add_real_link (st )
234
+ local xformed_name = xform (st .name )
235
+ if xformed_name == nil then return true end
236
+ if done (xformed_name ) then return true end
237
+ local linkst , sterr = fs .stat (st .name )
238
+ if not linkst then return nil , sterr end
239
+ local ok , err = add_real_dir (st .name :dirname ())
240
+ if not ok then return nil , err end
241
+ local hd
242
+ st .link = stlink .name
243
+ hd , err = header (st , xform )
244
+ if hd == Discarded then return true end
245
+ if not hd then return nil , err end
246
+ chunks [# chunks + 1 ] = hd
247
+ return true
248
+ end
249
+
214
250
for _ , file in ipairs (files ) do
215
251
216
252
if type (file ) == " string" then
217
253
local st , err = fs .stat (file )
218
254
if not st then return nil , err end
219
255
if st .type == " file" then
220
256
add_real_file (st )
257
+ elseif st .type == " link" then
258
+ add_real_link (st )
221
259
elseif st .type == " directory" then
222
260
add_real_dir (st .name )
223
261
for _ , name in ipairs (fs .ls (st .name / " **" )) do
@@ -241,22 +279,40 @@ function tar.tar(files, xform)
241
279
if file .content then
242
280
st .content = file .content
243
281
st .size = # file .content
282
+ elseif file .link then
283
+ st .type = " link"
284
+ st .link = file .link
244
285
else
245
- st0 , err = fs .stat (file .name )
286
+ if sys .os == " windows" then
287
+ st0 , err = fs .stat (file .name )
288
+ else
289
+ st0 , err = fs .lstat (file .name )
290
+ end
246
291
if not st0 then return nil , err end
247
- local content
248
- content , err = fs .read_bin (file .name )
249
- if not content then return nil , err end
250
- st .size = st0 .size
251
- st .content = content
292
+ if st0 .type == " link" then
293
+ local linkst , linkerr = fs .stat (file .name )
294
+ if not linkst then return nil , linkerr end
295
+ st .type = " link"
296
+ st .link = linkst .name
297
+ else
298
+ local content
299
+ content , err = fs .read_bin (file .name )
300
+ if not content then return nil , err end
301
+ st .size = st0 .size
302
+ st .content = content
303
+ end
252
304
end
253
305
if file .mtime then
254
306
st .mtime = file .mtime
255
307
else
256
308
st .mtime = st0 and st0 .mtime or os.time ()
257
309
end
258
310
local ok
259
- ok , err = add_file (st )
311
+ if st .type == " link" then
312
+ ok , err = add_link (st )
313
+ else
314
+ ok , err = add_file (st )
315
+ end
260
316
if not ok then return nil , err end
261
317
262
318
end
@@ -293,7 +349,7 @@ function tar.untar(archive, xform)
293
349
if st .type == " file" then
294
350
st .content = archive :sub (i + 512 , i + 512 + st .size - 1 )
295
351
i = i + 512 + st .size + pad (st .size )
296
- elseif st .type == " directory" then
352
+ elseif st .type == " link " or st . type == " directory" then
297
353
i = i + 512
298
354
else
299
355
return nil , st .type .. " : file type not supported"
0 commit comments