// standard archive format - standard tar - ustar
struct TarHeader {
char name[100]; // 0 -99
char mode[8]; // 100-107
char uid[8]; // 108-115
char gid[8]; // 116-123
char size[12]; // 124-135
char mtime[12]; // 136-147
char chksum[8]; // 148-155
char typeflag; // 156
char linkname[100]; // 157-256
char magic[8]; // 257-264
char uname[32]; // 265-296
char gname[32]; // 297-328
char devmajor[8]; // 329-336
char devminor[8]; // 337-344
char prefix[155]; // 345-499
char padding[12]; // 500-511
};
- tar文件header固定长度为512byte
- 512byte全部由ASCILL码填充
- 未设置的字段填充0(ASCILL码中为空字符null)
- 字段分为字符串和数值
- 字符串
- 以name为例,字符串按每个字符,转换为ASCILL码后填充
- 从0开始填充,假设写入name为bin
- 在数组中显示为[98 105 110 0 ... 0]
- 数值
- 以size为例,以八进制处理数值124-134填充ASCILL码
- 135保留ASCILL码空字符
- 124-134每一位都要写入,为0的写入48(ASCILL码中的0)
- 假设写入size为114514
- 在数组中显示为[48 48 48 48 48 51 51 55 53 50 50 0]
- 字符串显示为00000337522
- mtime为Unix时间的int64格式,以数值方式写入
- name特殊处理
- 当name长度大于100字符时,显然name[100]无法存储
- 此时对name拆分,分为文件夹名字和文件名字
- 假设文件为a/b/c,此时拆分为文件夹名a/b和文件名c
- 文件夹名写入padding[155],文件名写入name[100]
- 特殊字段magic
- 固定值,为ustar(null)00
- 在数组中显示为[117 115 116 97 114 0 48 48]
- 校验字段chksum
- 计算方法为简单地将512个byte的数值相加求和,chksum所在的8个byte按32(ASCILL码空格)计算,即这8个byte的值为256
- chksum字段的最后一位设置为32(ASCILL码空格),倒数第二位为0(ASCILL码null)
- 前六位按数值的八进制写入
package util
import (
"fmt"
"time"
"unicode/utf8"
)
type TarHeaderCreater struct {
// | name | 100 | 0 -99 |
// | mode | 8 | 100-107 |
// | uid | 8 | 108-115 |
// | gid | 8 | 116-123 |
// | size | 12 | 124-135 |
// | mtime | 12 | 136-147 |
// | chksum | 8 | 148-155 |
// | typeflag | 1 | 156
// | linkname | 100 | 157-256 |
// | magic | 8 | 257-264 |
// | uname | 32 | 265-296 |
// | gname | 32 | 297-328 |
// | devmajor | 8 | 329-336 |
// | devminor | 8 | 337-344 |
// | prefix | 155 | 345-499 |
// | padding | 12 | 500-511 |
buf [512]byte
}
func (hc *TarHeaderCreater) Init() {
// Init all 0
for i := 0; i < 512; i++ {
hc.buf[i] = 0
}
// magic
hc.writeChar(257, "ustar")
hc.writeInt(263, 264, 0)
// devmajor
hc.writeInt(329, 335, 0)
// devminor
hc.writeInt(337, 343, 0)
}
func (hc *TarHeaderCreater) writeChar(start int, str string) error {
for i, c := range str {
if c < utf8.RuneSelf {
hc.buf[start+i] = byte(int(c))
} else {
return fmt.Errorf("fail to change char: %c to ASCII", c)
}
}
return nil
}
func (hc *TarHeaderCreater) writeInt(start, end, val int64) {
for i := end; i >= start; i-- {
if val <= 0 {
hc.buf[i] = byte(48)
} else {
tmp := val % 8
val /= 8
hc.buf[i] = byte(tmp + 48)
}
}
}
func (h *TarHeader) SetName(name string) error {
if len(name) > 100 {
pos := strings.LastIndex(name, "/")
if pos < 0 {
return fmt.Errorf("invaild file name: %s", name)
}
dir := name[:pos]
file := name[pos+1:]
err := h.writeChar(345, dir)
if err != nil {
return err
}
return h.writeChar(0, file)
}
return h.writeChar(0, name)
}
func (hc *TarHeaderCreater) SetMode(mode int64) {
hc.writeInt(100, 106, mode)
}
func (hc *TarHeaderCreater) SetUid(uid int64) {
hc.writeInt(108, 114, uid)
}
func (hc *TarHeaderCreater) SetGid(gid int64) {
hc.writeInt(116, 122, gid)
}
func (hc *TarHeaderCreater) SetTypeFlag(b byte) {
hc.buf[156] = b
}
func (hc *TarHeaderCreater) SetLinkName(linkName string) error {
return hc.writeChar(157, linkName)
}
func (hc *TarHeaderCreater) SetModTime(modeTime string) error {
t, err := time.Parse("2006-01-02T15:04:05Z07:00", modeTime)
if err != nil {
return err
}
hc.writeInt(136, 146, t.Unix())
return nil
}
func (hc *TarHeaderCreater) SetSize(size int64) {
hc.writeInt(124, 134, size)
}
func (hc *TarHeaderCreater) SetChksum() {
chksum := int64(256)
for _, b := range hc.buf {
chksum += int64(b)
}
hc.writeInt(148, 153, chksum)
hc.buf[155] = 32
}