tar文件header的格式和构造

Header定义

// 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)
    • 前六位按数值的八进制写入

Header构造的golang实现

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
}

posted on 2024-05-11 11:27  umichan  阅读(259)  评论(0)    收藏  举报