Skip to content

proposal: archive/zip: add ability to read local file header #40354

@dstaley

Description

@dstaley

In a zip archive, file information is stored in two locations: the central directory header, and a local file header located prior to the file's data. In certain circumstances, a user might want to read information from the local file header as opposed to solely the central directory header. The current implementation relies solely on the central directory header to compute the FileHeader struct.

Instances in which a user might want to have access to the local header include:

  1. comparing the central and local file names to detect potential malicious activity (such as naming a file with a .txt extension in the central directory header, but with .exe in the local file header)
  2. detecting potential archive corruption (such as when a file contains a trailing slash in the central directory header, but the correct character in the local file header)

Currently, if a user wanted to read the local file header, they would need to implement their own zip reader, as no export would allow them the raw access to the underlying reader necessary to read the local file header.

Ideally, the solution would not introduce additional unnecessary reads for users who don't care about the local file header. One potential solution would be the addition of the following function:

func (f *File) ReadLocalHeader() (FileHeader, error) {
	var buf [fileHeaderLen]byte
	if _, err := f.zipr.ReadAt(buf[:], f.headerOffset); err != nil {
		return FileHeader{}, err
	}
	b := readBuf(buf[:])
	if sig := b.uint32(); sig != fileHeaderSignature {
		return FileHeader{}, ErrFormat
	}
	h := FileHeader{}
	h.ReaderVersion = b.uint16()
	h.Flags = b.uint16()
	h.Method = b.uint16()
	h.ModifiedTime = b.uint16()
	h.ModifiedDate = b.uint16()
	h.CRC32 = b.uint32()
	h.CompressedSize = b.uint32()
	h.UncompressedSize = b.uint32()
	h.CompressedSize64 = uint64(h.CompressedSize)
	h.UncompressedSize64 = uint64(h.UncompressedSize)
	filenameLen := int(b.uint16())

	d := make([]byte, filenameLen)
	if _, err := f.zipr.ReadAt(d[:], f.headerOffset+fileHeaderLen); err != nil {
		return FileHeader{}, err
	}
	h.Name = string(d[:filenameLen])

	return h, nil
}

This would allow users to explicitly request a read of the local file header for a given zip.File.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Incoming

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions